blob: 264b98cc7dcfde8f88c38a18dfb5dff47e80a21b [file] [log] [blame]
/*
* 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;
}
}
}