| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.messaging.widget; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.database.Cursor; |
| import android.graphics.Typeface; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.text.Spannable; |
| import android.text.SpannableStringBuilder; |
| import android.text.TextPaint; |
| import android.text.TextUtils; |
| import android.text.style.ForegroundColorSpan; |
| import android.text.style.StyleSpan; |
| import android.view.View; |
| import android.widget.RemoteViews; |
| import android.widget.RemoteViewsService; |
| |
| import com.android.messaging.R; |
| import com.android.messaging.datamodel.MessagingContentProvider; |
| import com.android.messaging.datamodel.data.ConversationListData; |
| import com.android.messaging.datamodel.data.ConversationListItemData; |
| import com.android.messaging.sms.MmsUtils; |
| import com.android.messaging.ui.UIIntents; |
| import com.android.messaging.ui.conversationlist.ConversationListItemView; |
| import com.android.messaging.util.ContentType; |
| import com.android.messaging.util.Dates; |
| import com.android.messaging.util.LogUtil; |
| import com.android.messaging.util.OsUtil; |
| import com.android.messaging.util.PhoneUtils; |
| |
| public class WidgetConversationListService extends RemoteViewsService { |
| private static final String TAG = LogUtil.BUGLE_WIDGET_TAG; |
| |
| @Override |
| public RemoteViewsFactory onGetViewFactory(Intent intent) { |
| if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { |
| LogUtil.v(TAG, "onGetViewFactory intent: " + intent); |
| } |
| return new WidgetConversationListFactory(getApplicationContext(), intent); |
| } |
| |
| /** |
| * Remote Views Factory for Bugle Widget. |
| */ |
| private static class WidgetConversationListFactory extends BaseWidgetFactory { |
| |
| public WidgetConversationListFactory(Context context, Intent intent) { |
| super(context, intent); |
| } |
| |
| @Override |
| protected Cursor doQuery() { |
| return mContext.getContentResolver().query(MessagingContentProvider.CONVERSATIONS_URI, |
| ConversationListItemData.PROJECTION, |
| ConversationListData.WHERE_NOT_ARCHIVED, |
| null, // selection args |
| ConversationListData.SORT_ORDER); |
| } |
| |
| /** |
| * @return the {@link RemoteViews} for a specific position in the list. |
| */ |
| @Override |
| public RemoteViews getViewAt(int position) { |
| if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { |
| LogUtil.v(TAG, "getViewAt position: " + position); |
| } |
| synchronized (sWidgetLock) { |
| // "View more conversations" view. |
| if (mCursor == null |
| || (mShouldShowViewMore && position >= getItemCount())) { |
| return getViewMoreItemsView(); |
| } |
| |
| if (!mCursor.moveToPosition(position)) { |
| // If we ever fail to move to a position, return the "View More conversations" |
| // view. |
| LogUtil.w(TAG, "Failed to move to position: " + position); |
| return getViewMoreItemsView(); |
| } |
| |
| final ConversationListItemData conv = new ConversationListItemData(); |
| conv.bind(mCursor); |
| |
| // Inflate and fill out the remote view |
| final RemoteViews remoteViews = new RemoteViews( |
| mContext.getPackageName(), R.layout.widget_conversation_list_item); |
| |
| final boolean hasUnreadMessages = !conv.getIsRead(); |
| final Resources resources = mContext.getResources(); |
| final boolean isDefaultSmsApp = PhoneUtils.getDefault().isDefaultSmsApp(); |
| |
| final String timeStamp = conv.getIsSendRequested() ? |
| resources.getString(R.string.message_status_sending) : |
| Dates.getWidgetTimeString(conv.getTimestamp(), true /*abbreviated*/) |
| .toString(); |
| // Date/Timestamp or Sending or Error state -- all shown in the date item |
| remoteViews.setTextViewText(R.id.date, |
| boldifyIfUnread(timeStamp, hasUnreadMessages)); |
| |
| // From |
| remoteViews.setTextViewText(R.id.from, |
| boldifyIfUnread(conv.getName(), hasUnreadMessages)); |
| |
| // Notifications turned off mini-bell icon |
| remoteViews.setViewVisibility(R.id.conversation_notification_bell, |
| conv.getNotificationEnabled() ? View.GONE : View.VISIBLE); |
| |
| // On click intent. |
| final Intent intent = UIIntents.get().getIntentForConversationActivity(mContext, |
| conv.getConversationId(), null /* draft */); |
| |
| remoteViews.setOnClickFillInIntent(R.id.widget_conversation_list_item, intent); |
| |
| // Avatar |
| boolean includeAvatar; |
| if (OsUtil.isAtLeastJB()) { |
| final Bundle options = mAppWidgetManager.getAppWidgetOptions(mAppWidgetId); |
| if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { |
| LogUtil.v(TAG, "getViewAt BugleWidgetProvider.WIDGET_SIZE_KEY: " + |
| options.getInt(BugleWidgetProvider.WIDGET_SIZE_KEY)); |
| } |
| |
| includeAvatar = options.getInt(BugleWidgetProvider.WIDGET_SIZE_KEY) == |
| BugleWidgetProvider.SIZE_LARGE; |
| } else { |
| includeAvatar = true;; |
| } |
| |
| // Show the avatar when grande size, otherwise hide it. |
| remoteViews.setViewVisibility(R.id.avatarView, includeAvatar ? |
| View.VISIBLE : View.GONE); |
| |
| Uri iconUri = null; |
| if (conv.getIcon() != null) { |
| iconUri = Uri.parse(conv.getIcon()); |
| } |
| remoteViews.setImageViewBitmap(R.id.avatarView, includeAvatar ? |
| getAvatarBitmap(iconUri) : null); |
| |
| // Error |
| // Only show the fail icon if it is not a group conversation. |
| // And also require that we be the default sms app. |
| final boolean showError = conv.getIsFailedStatus() && |
| isDefaultSmsApp; |
| final boolean showDraft = conv.getShowDraft() && |
| isDefaultSmsApp; |
| remoteViews.setViewVisibility(R.id.conversation_failed_status_icon, |
| showError && includeAvatar ? |
| View.VISIBLE : View.GONE); |
| |
| if (showError || showDraft) { |
| remoteViews.setViewVisibility(R.id.snippet, View.GONE); |
| remoteViews.setViewVisibility(R.id.errorBlock, View.VISIBLE); |
| remoteViews.setTextViewText(R.id.errorSnippet, getSnippetText(conv)); |
| |
| if (showDraft) { |
| // Show italicized "Draft" on third line |
| final String text = resources.getString( |
| R.string.conversation_list_item_view_draft_message); |
| SpannableStringBuilder builder = new SpannableStringBuilder(text); |
| builder.setSpan(new StyleSpan(Typeface.ITALIC), 0, text.length(), |
| Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
| builder.setSpan(new ForegroundColorSpan( |
| resources.getColor(R.color.widget_text_color)), |
| 0, text.length(), |
| Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
| remoteViews.setTextViewText(R.id.errorText, builder); |
| } else { |
| // Show error message on third line |
| int failureMessageId = R.string.message_status_download_failed; |
| if (conv.getIsMessageTypeOutgoing()) { |
| failureMessageId = MmsUtils.mapRawStatusToErrorResourceId( |
| conv.getMessageStatus(), |
| conv.getMessageRawTelephonyStatus()); |
| } |
| remoteViews.setTextViewText(R.id.errorText, |
| resources.getString(failureMessageId)); |
| } |
| } else { |
| remoteViews.setViewVisibility(R.id.errorBlock, View.GONE); |
| remoteViews.setViewVisibility(R.id.snippet, View.VISIBLE); |
| remoteViews.setTextViewText(R.id.snippet, |
| boldifyIfUnread(getSnippetText(conv), hasUnreadMessages)); |
| } |
| |
| // Set the accessibility TalkBack text |
| remoteViews.setContentDescription(R.id.widget_conversation_list_item, |
| ConversationListItemView.buildContentDescription(mContext.getResources(), |
| conv, new TextPaint())); |
| |
| return remoteViews; |
| } |
| } |
| |
| private String getSnippetText(final ConversationListItemData conv) { |
| String snippetText = conv.getShowDraft() ? |
| conv.getDraftSnippetText() : conv.getSnippetText(); |
| final String previewContentType = conv.getShowDraft() ? |
| conv.getDraftPreviewContentType() : conv.getPreviewContentType(); |
| if (TextUtils.isEmpty(snippetText)) { |
| Resources resources = mContext.getResources(); |
| // Use the attachment type as a snippet so the preview doesn't look odd |
| if (ContentType.isAudioType(previewContentType)) { |
| snippetText = resources.getString( |
| R.string.conversation_list_snippet_audio_clip); |
| } else if (ContentType.isImageType(previewContentType)) { |
| snippetText = resources.getString(R.string.conversation_list_snippet_picture); |
| } else if (ContentType.isVideoType(previewContentType)) { |
| snippetText = resources.getString(R.string.conversation_list_snippet_video); |
| } else if (ContentType.isVCardType(previewContentType)) { |
| snippetText = resources.getString(R.string.conversation_list_snippet_vcard); |
| } |
| } |
| return snippetText; |
| } |
| |
| /** |
| * @return the "View more conversations" view. When the user taps this item, they're |
| * taken to the Bugle's conversation list. |
| */ |
| @Override |
| protected RemoteViews getViewMoreItemsView() { |
| if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { |
| LogUtil.v(TAG, "getViewMoreItemsView"); |
| } |
| final RemoteViews view = new RemoteViews(mContext.getPackageName(), |
| R.layout.widget_loading); |
| view.setTextViewText( |
| R.id.loading_text, mContext.getText(R.string.view_more_conversations)); |
| |
| // Tapping this "More conversations" item should take us to the ConversationList. |
| // However, the list view is primed with an intent to go to the Conversation activity. |
| // Each normal conversation list item sets the fill-in intent with the |
| // ConversationId for that particular conversation. In other words, the only place |
| // we can go is the ConversationActivity. We add an extra here to tell the |
| // ConversationActivity to really take us to the ConversationListActivity. |
| final Intent intent = new Intent(); |
| intent.putExtra(UIIntents.UI_INTENT_EXTRA_GOTO_CONVERSATION_LIST, true); |
| view.setOnClickFillInIntent(R.id.widget_loading, intent); |
| return view; |
| } |
| |
| @Override |
| public RemoteViews getLoadingView() { |
| RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading); |
| view.setTextViewText( |
| R.id.loading_text, mContext.getText(R.string.loading_conversations)); |
| return view; |
| } |
| |
| @Override |
| public int getViewTypeCount() { |
| return 2; |
| } |
| |
| @Override |
| protected int getMainLayoutId() { |
| return R.layout.widget_conversation_list; |
| } |
| } |
| |
| } |