/* * Copyright (C) 2012 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.moez.QKSMS.ui.widget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.provider.Telephony.Threads; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; import android.text.style.TextAppearanceSpan; import android.util.Log; import android.view.View; import android.widget.RemoteViews; import android.widget.RemoteViewsService; import com.moez.QKSMS.R; import com.moez.QKSMS.common.ConversationPrefsHelper; import com.moez.QKSMS.common.utils.ImageUtils; import com.moez.QKSMS.common.utils.MessageUtils; import com.moez.QKSMS.data.ContactHelper; import com.moez.QKSMS.data.Conversation; import com.moez.QKSMS.ui.MainActivity; import com.moez.QKSMS.ui.ThemeManager; import com.moez.QKSMS.ui.messagelist.MessageListActivity; public class WidgetService extends RemoteViewsService { private static final String TAG = "WidgetService"; /** * Lock to avoid race condition between widgets. */ private static final Object sWidgetLock = new Object(); @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { Log.v(TAG, "onGetViewFactory intent: " + intent); return new WidgetFactory(getApplicationContext(), intent); } /** * Remote Views Factory for Mms Widget. */ private static class WidgetFactory implements RemoteViewsService.RemoteViewsFactory { private static final int MAX_CONVERSATIONS_COUNT = 25; private final Context mContext; private final int mAppWidgetId; private final int mSmallWidget; private boolean mShouldShowViewMore; private Cursor mConversationCursor; private int mUnreadConvCount; private final AppWidgetManager mAppWidgetManager; public WidgetFactory(Context context, Intent intent) { mContext = context; mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); mSmallWidget = intent.getIntExtra("small_widget", 0); mAppWidgetManager = AppWidgetManager.getInstance(context); Log.v(TAG, "WidgetFactory intent: " + intent + "widget id: " + mAppWidgetId); } @Override public void onCreate() { Log.v(TAG, "onCreate"); } @Override public void onDestroy() { Log.v(TAG, "onDestroy"); synchronized (sWidgetLock) { if (mConversationCursor != null && !mConversationCursor.isClosed()) { mConversationCursor.close(); mConversationCursor = null; } } } @Override public void onDataSetChanged() { Log.v(TAG, "onDataSetChanged"); synchronized (sWidgetLock) { if (mConversationCursor != null) { mConversationCursor.close(); mConversationCursor = null; } mConversationCursor = queryAllConversations(); mUnreadConvCount = queryUnreadCount(); onLoadComplete(); } } private Cursor queryAllConversations() { return mContext.getContentResolver().query(Conversation.sAllThreadsUri, Conversation.ALL_THREADS_PROJECTION, null, null, null); } private int queryUnreadCount() { Cursor cursor = null; int unreadCount = 0; try { cursor = mContext.getContentResolver().query(Conversation.sAllThreadsUri, Conversation.ALL_THREADS_PROJECTION, Threads.READ + "=0", null, null); if (cursor != null) { unreadCount = cursor.getCount(); } } finally { if (cursor != null) { cursor.close(); } } return unreadCount; } /** * Returns the number of items should be shown in the widget list. This method also updates * the boolean that indicates whether the "show more" item should be shown. * * @return the number of items to be displayed in the list. */ @Override public int getCount() { Log.v(TAG, "getCount"); synchronized (sWidgetLock) { if (mConversationCursor == null) { return 0; } final int count = getConversationCount(); mShouldShowViewMore = count < mConversationCursor.getCount(); return count + (mShouldShowViewMore ? 1 : 0); } } /** * Returns the number of conversations that should be shown in the widget. This method * doesn't update the boolean that indicates that the "show more" item should be included * in the list. * * @return */ private int getConversationCount() { Log.v(TAG, "getConversationCount"); return Math.min(mConversationCursor.getCount(), MAX_CONVERSATIONS_COUNT); } /* * Add color to a given text */ private SpannableStringBuilder addColor(CharSequence text, int color) { SpannableStringBuilder builder = new SpannableStringBuilder(text); if (color != 0) { builder.setSpan(new ForegroundColorSpan(color), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } return builder; } /** * @return the {@link RemoteViews} for a specific position in the list. */ @Override public RemoteViews getViewAt(int position) { Log.v(TAG, "getViewAt position: " + position); synchronized (sWidgetLock) { // "View more conversations" view. if (mConversationCursor == null || (mShouldShowViewMore && position >= getConversationCount())) { return getViewMoreConversationsView(); } if (!mConversationCursor.moveToPosition(position)) { // If we ever fail to move to a position, return the "View More conversations" view. Log.w(TAG, "Failed to move to position: " + position); return getViewMoreConversationsView(); } Conversation conversation = Conversation.from(mContext, mConversationCursor); RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.list_item_conversation_widget); if (mSmallWidget == 0) { remoteViews.setViewVisibility(R.id.avatar, View.VISIBLE); bindAvatar(remoteViews, conversation); } else remoteViews.setViewVisibility(R.id.avatar, View.GONE); bindIndicators(remoteViews, conversation); bindDate(remoteViews, conversation); bindName(remoteViews, conversation); bindSnippet(remoteViews, conversation); // On click intent. Intent clickIntent = new Intent(); clickIntent.putExtra(MessageListActivity.ARG_THREAD_ID, conversation.getThreadId()); remoteViews.setOnClickFillInIntent(R.id.conversation, clickIntent); return remoteViews; } } private void bindAvatar(RemoteViews remoteViews, Conversation conversation) { Drawable avatar = conversation.getRecipients().get(0).getAvatar(mContext, null); Bitmap avatarBitmap = avatar != null ? ImageUtils.drawableToBitmap(avatar) : null; if (avatarBitmap == null) { avatarBitmap = ContactHelper.blankContact(mContext, conversation.getRecipients().formatNames(", ")); } avatarBitmap = ImageUtils.getCircleBitmap(avatarBitmap, avatarBitmap.getWidth()); remoteViews.setImageViewBitmap(R.id.avatar, avatarBitmap); } private void bindIndicators(RemoteViews remoteViews, Conversation conversation) { remoteViews.setInt(R.id.muted, "setColorFilter", ThemeManager.getColor()); remoteViews.setInt(R.id.error, "setColorFilter", ThemeManager.getColor()); remoteViews.setInt(R.id.unread, "setColorFilter", ThemeManager.getColor()); remoteViews.setViewVisibility(R.id.muted, new ConversationPrefsHelper(mContext, conversation.getThreadId()) .getNotificationsEnabled() ? View.GONE : View.VISIBLE); remoteViews.setViewVisibility(R.id.error, conversation.hasError() ? View.VISIBLE : View.GONE); remoteViews.setViewVisibility(R.id.unread, conversation.hasUnreadMessages() ? View.VISIBLE : View.GONE); } private void bindName(RemoteViews remoteViews, Conversation conversation) { SpannableStringBuilder from = addColor(conversation.getRecipients().formatNames(", "), ThemeManager.getTextOnBackgroundPrimary()); if (conversation.hasDraft()) { from.append(mContext.getResources().getString(R.string.draft_separator)); int before = from.length(); from.append(mContext.getResources().getString(R.string.has_draft)); from.setSpan(new TextAppearanceSpan(mContext, android.R.style.TextAppearance_Small, ThemeManager.getTextOnBackgroundPrimary()), before, from.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); from.setSpan(new ForegroundColorSpan(ThemeManager.getColor()), before, from.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); } remoteViews.setTextViewText(R.id.name, from); } private void bindDate(RemoteViews remoteViews, Conversation conversation) { remoteViews.setTextViewText(R.id.date, addColor(MessageUtils.formatTimeStampString(mContext, conversation.getDate()), conversation.hasUnreadMessages() ? ThemeManager.getColor() : ThemeManager.getTextOnBackgroundSecondary())); } private void bindSnippet(RemoteViews remoteViews, Conversation conversation) { remoteViews.setTextViewText(R.id.snippet, addColor(conversation.getSnippet(), conversation.hasUnreadMessages() ? ThemeManager.getTextOnBackgroundPrimary() : ThemeManager.getTextOnBackgroundSecondary())); } /** * @return the "View more conversations" view. When the user taps this item, they're * taken to the messaging app's conversation list. */ private RemoteViews getViewMoreConversationsView() { Log.v(TAG, "getViewMoreConversationsView"); RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading); view.setTextViewText(R.id.loading_text, "View more conversations"); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); view.setOnClickPendingIntent(R.id.widget_loading, pendingIntent); 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 public long getItemId(int position) { return position; } @Override public boolean hasStableIds() { return true; } private void onLoadComplete() { Log.v(TAG, "onLoadComplete"); RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.widget); mAppWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews); } } }