WidgetService.java revision 161f50d0fabdaa384a63ce69f595861c5e69795f
18a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira/*
28a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * Copyright (C) 2012 The Android Open Source Project
38a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira *
48a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
58a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * you may not use this file except in compliance with the License.
68a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * You may obtain a copy of the License at
78a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira *
88a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
98a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira *
108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * Unless required by applicable law or agreed to in writing, software
118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * See the License for the specific language governing permissions and
148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira * limitations under the License.
158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira */
168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereirapackage com.android.mail.widget;
178a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.R;
198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Account;
208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Conversation;
218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.Folder;
228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.providers.UIProvider;
238a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.utils.DelayedTaskHandler;
248a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.utils.LogUtils;
258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport com.android.mail.utils.Utils;
268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
278a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.appwidget.AppWidgetManager;
288a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.ContentResolver;
298a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Context;
308a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.CursorLoader;
318a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent;
328a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Loader;
338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Loader.OnLoadCompleteListener;
348a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.database.Cursor;
358a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.os.Looper;
368a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.text.SpannableStringBuilder;
378a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.text.TextUtils;
388a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.text.format.DateUtils;
398a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.view.View;
408a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.widget.RemoteViews;
418a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.widget.RemoteViewsService;
428a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
438a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereirapublic class WidgetService extends RemoteViewsService {
448a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
458a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Lock to avoid race condition between widgets.
468a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
478a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    private static Object sWidgetLock = new Object();
488a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
498a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    @Override
508a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    public RemoteViewsFactory onGetViewFactory(Intent intent) {
518a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        return new MailFactory(getApplicationContext(), intent);
528a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    }
538a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
548a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
558a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Remote Views Factory for Mail Widget.
568a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
578a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    private static class MailFactory
588a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            implements RemoteViewsService.RemoteViewsFactory, OnLoadCompleteListener<Cursor> {
598a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private static final int MAX_CONVERSATIONS_COUNT = 25;
608a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private static final int MAX_SENDERS_LENGTH = 25;
618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private static final String LOG_TAG = new LogUtils().getLogTag();
628a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
638a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final Context mContext;
648a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final int mAppWidgetId;
658a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final Account mAccount;
668a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final Folder mFolder;
678a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final WidgetConversationViewBuilder mWidgetConversationViewBuilder;
688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private Cursor mConversationCursor;
698a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private CursorLoader mFolderLoader;
708a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private FolderUpdateHandler mFolderUpdateHandler;
718a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private int mFolderCount;
728a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private boolean mShouldShowViewMore;
738a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private boolean mFolderInformationShown = false;
748a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private ContentResolver mResolver;
758a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
768a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public MailFactory(Context context, Intent intent) {
778a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mContext = context;
788a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mAppWidgetId = intent.getIntExtra(
798a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
80518ee42d4f5cfbcac07dc61fdcac4de8c32c0e26Mindy Pereira            mAccount = new Account(intent.getStringExtra(WidgetProvider.EXTRA_ACCOUNT));
81518ee42d4f5cfbcac07dc61fdcac4de8c32c0e26Mindy Pereira            mFolder = new Folder(intent.getStringExtra(WidgetProvider.EXTRA_FOLDER));
828a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mWidgetConversationViewBuilder = new WidgetConversationViewBuilder(mContext, mAccount);
838a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mResolver = context.getContentResolver();
848a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
858a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
868a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
878a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onCreate() {
888a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            // Save the map between widgetId and account to preference
898a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            BaseWidgetProvider.saveWidgetInformation(mContext, mAppWidgetId, mAccount, mFolder);
908a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
918a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            // If the account of this widget has been removed, we want to update the widget to
928a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            // "Tap to configure" mode.
938a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            if (!BaseWidgetProvider.isWidgetConfigured(mContext, mAppWidgetId, mAccount, mFolder)) {
948a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolder);
958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
97cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira            mConversationCursor = mResolver.query(mFolder.conversationListUri,
988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    UIProvider.CONVERSATION_PROJECTION, null, null, null);
998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
100cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira            mFolderLoader = new CursorLoader(mContext, mFolder.uri, UIProvider.FOLDERS_PROJECTION,
101cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    null, null, null);
1028a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderLoader.registerListener(0, this);
1038a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderUpdateHandler = new FolderUpdateHandler(mContext.getResources().getInteger(
1048a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    R.integer.widget_folder_refresh_delay_ms));
1058a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderUpdateHandler.scheduleTask();
1068a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
1088a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
1108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onDestroy() {
1118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
1128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (mConversationCursor != null && !mConversationCursor.isClosed()) {
1138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    mConversationCursor.close();
1148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    mConversationCursor = null;
1158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
1168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
1178a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            if (mFolderLoader != null) {
1198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mFolderLoader.reset();
1208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mFolderLoader = null;
1218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
1228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
1238a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1248a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
1258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onDataSetChanged() {
1268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
1278a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // TODO: use loader manager.
1288a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mConversationCursor.requery();
1298a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
1308a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderUpdateHandler.scheduleTask();
1318a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
1328a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
1348a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * Returns the number of items should be shown in the widget list.  This method also updates
1358a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * the boolean that indicates whether the "show more" item should be shown.
1368a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return the number of items to be displayed in the list.
1378a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
1388a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
1398a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public int getCount() {
1408a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
1418a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                final int count = getConversationCount();
1428a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mShouldShowViewMore = count < mConversationCursor.getCount()
1438a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        || count < mFolderCount;
1448a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                return count + (mShouldShowViewMore ? 1 : 0);
1458a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
1468a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
1478a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1488a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
1498a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * Returns the number of conversations that should be shown in the widget.  This method
1508a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * doesn't update the boolean that indicates that the "show more" item should be included
1518a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * in the list.
1528a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return
1538a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
1548a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private int getConversationCount() {
1558a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
1568a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                return Math.min(mConversationCursor.getCount(), MAX_CONVERSATIONS_COUNT);
1578a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
1588a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
1598a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1608a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
1618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return the {@link RemoteViews} for a specific position in the list.
1628a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
1638a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
1648a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public RemoteViews getViewAt(int position) {
1658a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
1668a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // "View more conversations" view.
1678a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (mConversationCursor == null
1688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        || (mShouldShowViewMore && position >= getConversationCount())) {
1698a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    return getViewMoreConversationsView();
1708a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
1718a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1728a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (!mConversationCursor.moveToPosition(position)) {
1738a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    // If we ever fail to move to a position, return the "View More conversations"
1748a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    // view.
1758a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    LogUtils.e(LOG_TAG,
1768a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                            "Failed to move to position %d in the cursor.", position);
1778a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    return getViewMoreConversationsView();
1788a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
1798a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
180a831b2f1ba6d3c18d5e54563df8bae1f589c4257Mindy Pereira                Conversation conversation = new Conversation(mConversationCursor);
1818a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Split the senders and status from the instructions.
1828a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                SpannableStringBuilder senderBuilder = new SpannableStringBuilder();
1838a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                SpannableStringBuilder statusBuilder = new SpannableStringBuilder();
1844fa30618ad6683915858c0da1a9e08d53810d851Mindy Pereira                senderBuilder.append(conversation.senders);
1854fa30618ad6683915858c0da1a9e08d53810d851Mindy Pereira                // TODO: (mindyp) create stylized sender text.
1868a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1878a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Get styled date.
1888a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                CharSequence date = DateUtils.getRelativeTimeSpanString(
1898a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        mContext, conversation.dateMs);
1908a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1918a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Load up our remote view.
1928a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                RemoteViews remoteViews = mWidgetConversationViewBuilder.getStyledView(
1938a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        senderBuilder, statusBuilder, date, filterTag(conversation.subject),
1948a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        conversation.snippet, conversation.folderList, conversation.hasAttachments);
1958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // On click intent.
1978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                remoteViews.setOnClickFillInIntent(R.id.widget_conversation,
198161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira                        Utils.createViewConversationIntent(conversation, mFolder, mAccount));
1998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                return remoteViews;
2018a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
2028a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2038a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2048a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
2058a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return the "View more conversations" view.
2068a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
2078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private RemoteViews getViewMoreConversationsView() {
2088a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
2098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            view.setTextViewText(
2108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    R.id.loading_text, mContext.getText(R.string.view_more_conversations));
211161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira            view.setOnClickFillInIntent(R.id.widget_loading,
212161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira                    Utils.createViewFolderIntent(mFolder, mAccount));
2138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return view;
2148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
2178a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public RemoteViews getLoadingView() {
2188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
2198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            view.setTextViewText(
2208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    R.id.loading_text, mContext.getText(R.string.loading_conversation));
2218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return view;
2228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2238a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2248a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
2258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public int getViewTypeCount() {
2268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return 2;
2278a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2288a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2298a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
2308a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public long getItemId(int position) {
2318a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return position;
2328a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2348a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
2358a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public boolean hasStableIds() {
2368a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return false;
2378a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2388a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2398a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
2408a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
2418a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            if (!data.moveToFirst()) {
2428a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                return;
2438a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
2448a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2458a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            final int unreadCount = data.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
2468a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            final String folderName = data.getString(UIProvider.FOLDER_NAME_COLUMN);
2478a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderCount = data.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
2488a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2498a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.widget);
2508a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
2518a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2528a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            if (!mFolderInformationShown && !TextUtils.isEmpty(folderName)) {
2538a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // We want to do a full update to the widget at least once, as the widget
2548a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // manager doesn't cache the state of the remote views when doing a partial
2558a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // widget update. This causes the label name to be shown as blank if the state
2568a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // of the widget is restored.
2578a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                BaseWidgetProvider.configureValidAccountWidget(
2588a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        mContext, remoteViews, mAppWidgetId, mAccount, mFolder, folderName);
2598a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                appWidgetManager.updateAppWidget(mAppWidgetId, remoteViews);
2608a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mFolderInformationShown = true;
2618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
2628a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2638a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            remoteViews.setViewVisibility(R.id.widget_folder, View.VISIBLE);
2648a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            remoteViews.setTextViewText(R.id.widget_folder, folderName);
2658a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            remoteViews.setViewVisibility(R.id.widget_unread_count, View.VISIBLE);
2668a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            remoteViews.setTextViewText(
2678a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    R.id.widget_unread_count, Utils.getUnreadCountString(mContext, unreadCount));
2688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2698a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            appWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews);
2708a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2718a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2728a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
2738a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * If the subject contains the tag of a mailing-list (text surrounded with []), return the
2748a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * subject with that tag ellipsized, e.g. "[android-gmail-team] Hello" -> "[andr...] Hello"
2758a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
2768a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private static String filterTag(String subject) {
2778a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            String result = subject;
2788a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            if (subject.length() > 0 && subject.charAt(0) == '[') {
2798a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                int end = subject.indexOf(']');
2808a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (end > 0) {
2818a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    String tag = subject.substring(1, end);
2828a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    result = "[" + Utils.ellipsize(tag, 7) + "]" + subject.substring(end + 1);
2838a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
2848a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
2858a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2868a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return result;
2878a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2888a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2898a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
2908a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * A {@link DelayedTaskHandler} to throttle label update to a reasonable rate.
2918a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
2928a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private class FolderUpdateHandler extends DelayedTaskHandler {
2938a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            public FolderUpdateHandler(int refreshDelay) {
2948a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                super(Looper.myLooper(), refreshDelay);
2958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
2968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            @Override
2988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            protected void performTask() {
2998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Start the loader. The cached data will be returned if present.
3008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (mFolderLoader != null) {
3018a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    mFolderLoader.startLoading();
3028a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
3038a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
3048a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
3058a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    }
3068a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira}
307