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
18a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrookimport android.app.PendingIntent;
198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.appwidget.AppWidgetManager;
208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Context;
218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.CursorLoader;
228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Intent;
238a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Loader;
248a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.content.Loader.OnLoadCompleteListener;
2550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrookimport android.content.res.Resources;
268a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.database.Cursor;
275f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrookimport android.net.Uri;
288a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.os.Looper;
29a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrookimport android.support.v4.app.TaskStackBuilder;
30c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereiraimport android.text.SpannableString;
318a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.text.SpannableStringBuilder;
328a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.text.TextUtils;
338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.text.format.DateUtils;
34c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereiraimport android.text.style.CharacterStyle;
358a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.view.View;
368a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.widget.RemoteViews;
378a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereiraimport android.widget.RemoteViewsService;
388a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
398c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.R;
4089bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sappersteinimport com.android.mail.browse.ConversationItemView;
418c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.browse.SendersView;
428c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.compose.ComposeActivity;
438c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.preferences.MailPrefs;
448c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.providers.Account;
458c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.providers.Conversation;
468e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yangimport com.android.mail.providers.Folder;
478c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.providers.UIProvider;
488c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.providers.UIProvider.ConversationListQueryParameters;
4967aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedyimport com.android.mail.providers.UIProvider.FolderType;
508c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.utils.AccountUtils;
518c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.utils.DelayedTaskHandler;
5210ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieuximport com.android.mail.utils.FolderUri;
538c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.utils.LogTag;
548c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.utils.LogUtils;
558c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huangimport com.android.mail.utils.Utils;
568c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huang
5788acafa03a87f5c84b959697d13b81df8f11a96emindypimport java.util.ArrayList;
5810ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieuximport java.util.List;
5988acafa03a87f5c84b959697d13b81df8f11a96emindyp
608a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereirapublic class WidgetService extends RemoteViewsService {
618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
628a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Lock to avoid race condition between widgets.
638a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
64c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler    private static final Object sWidgetLock = new Object();
658a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
66ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook    private static final String LOG_TAG = LogTag.getLogTag();
67ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook
688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    @Override
698a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    public RemoteViewsFactory onGetViewFactory(Intent intent) {
70a4968036e4384431de74b96d43a40faaeecf053eMindy Pereira        return new MailFactory(getApplicationContext(), intent, this);
71e25998f8c3d20b37682cfe00aadb4a70c81eb8e4Mindy Pereira    }
72e25998f8c3d20b37682cfe00aadb4a70c81eb8e4Mindy Pereira
73e25998f8c3d20b37682cfe00aadb4a70c81eb8e4Mindy Pereira    protected void configureValidAccountWidget(Context context, RemoteViews remoteViews,
7410ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            int appWidgetId, Account account, final int folderType, final int folderCapabilities,
7510ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            final Uri folderUri, final Uri folderConversationListUri, String folderName) {
7667aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy        configureValidAccountWidget(context, remoteViews, appWidgetId, account, folderType,
7710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                folderCapabilities, folderUri, folderConversationListUri, folderName,
7810ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                WidgetService.class);
79a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    }
80a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
81a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    /**
82a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook     * Modifies the remoteView for the given account and folder.
83a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook     */
84a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    public static void configureValidAccountWidget(Context context, RemoteViews remoteViews,
8510ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            int appWidgetId, Account account, final int folderType, final int folderCapabilities,
8610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            final Uri folderUri, final Uri folderConversationListUri, String folderDisplayName,
8710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            Class<?> widgetService) {
88a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setViewVisibility(R.id.widget_folder, View.VISIBLE);
89ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook
90ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        // If the folder or account name are empty, we don't want to overwrite the valid data that
91ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        // had been saved previously.  Since the launcher will save the state of the remote views
92ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        // we should rely on the fact that valid data has been saved.  But we should still log this,
93ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        // as it shouldn't happen
9426a20758413f0f8629ea71a0943304e3ae740165Tony Mantler        if (TextUtils.isEmpty(folderDisplayName) || TextUtils.isEmpty(account.getDisplayName())) {
95ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook            LogUtils.e(LOG_TAG, new Error(),
96ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                    "Empty folder or account name.  account: %s, folder: %s",
9726a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                    account.getEmailAddress(), folderDisplayName);
98ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        }
99ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        if (!TextUtils.isEmpty(folderDisplayName)) {
100ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook            remoteViews.setTextViewText(R.id.widget_folder, folderDisplayName);
101ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook        }
102ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook
103a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setViewVisibility(R.id.widget_compose, View.VISIBLE);
104a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setViewVisibility(R.id.conversation_list, View.VISIBLE);
105f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei        remoteViews.setViewVisibility(R.id.empty_conversation_list, View.VISIBLE);
106a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setViewVisibility(R.id.widget_folder_not_synced, View.GONE);
107f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei        remoteViews.setViewVisibility(R.id.widget_configuration, View.GONE);
108f9568dcc8ebf8d975c9a9c136fe2b15f58fc8930Mark Wei        remoteViews.setEmptyView(R.id.conversation_list, R.id.empty_conversation_list);
109a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
110a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        WidgetService.configureValidWidgetIntents(context, remoteViews, appWidgetId, account,
11110ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                folderType, folderCapabilities, folderUri, folderConversationListUri,
11210ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                folderDisplayName, widgetService);
113a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    }
114a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
115a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    public static void configureValidWidgetIntents(Context context, RemoteViews remoteViews,
11610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            int appWidgetId, Account account, final int folderType, final int folderCapabilities,
11710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            final Uri folderUri, final Uri folderConversationListUri,
11810ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            final String folderDisplayName, Class<?> serviceClass) {
119a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setViewVisibility(R.id.widget_configuration, View.GONE);
120a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
121a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
122a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        // Launch an intent to avoid ANRs
123a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        final Intent intent = new Intent(context, serviceClass);
124a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
12548cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy        intent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
12667aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy        intent.putExtra(BaseWidgetProvider.EXTRA_FOLDER_TYPE, folderType);
12710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux        intent.putExtra(BaseWidgetProvider.EXTRA_FOLDER_CAPABILITIES, folderCapabilities);
128b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(BaseWidgetProvider.EXTRA_FOLDER_URI, folderUri);
129b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(BaseWidgetProvider.EXTRA_FOLDER_CONVERSATION_LIST_URI,
130b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                folderConversationListUri);
131b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        intent.putExtra(BaseWidgetProvider.EXTRA_FOLDER_DISPLAY_NAME, folderDisplayName);
132a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
133a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setRemoteAdapter(R.id.conversation_list, intent);
134a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        // Open mail app when click on header
135b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        final Intent mailIntent = Utils.createViewFolderIntent(context, folderUri, account);
1369573510415134c4c9157dd0ad1ed015ec47e4116Martin Hibdon        mailIntent.setPackage(context.getPackageName());
137a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        PendingIntent clickIntent = PendingIntent.getActivity(context, 0, mailIntent,
138a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook                PendingIntent.FLAG_UPDATE_CURRENT);
139a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setOnClickPendingIntent(R.id.widget_header, clickIntent);
140a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
141a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        // On click intent for Compose
142a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        final Intent composeIntent = new Intent();
1439573510415134c4c9157dd0ad1ed015ec47e4116Martin Hibdon        composeIntent.setPackage(context.getPackageName());
144a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        composeIntent.setAction(Intent.ACTION_SEND);
1457ae042ef2a23bf4c87f3edfcd71f7ebc784174abmindyp        composeIntent.putExtra(Utils.EXTRA_ACCOUNT, account.serialize());
146a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        composeIntent.setData(account.composeIntentUri);
147a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        composeIntent.putExtra(ComposeActivity.EXTRA_FROM_EMAIL_TASK, true);
148a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        if (account.composeIntentUri != null) {
149a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook            composeIntent.putExtra(Utils.EXTRA_COMPOSE_URI, account.composeIntentUri);
150a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        }
151a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
152a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        // Build a task stack that forces the conversation list on the stack before the compose
153a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        // activity.
154a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
155a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        clickIntent = taskStackBuilder.addNextIntent(mailIntent)
156a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook                .addNextIntent(composeIntent)
157a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook                .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
158a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setOnClickPendingIntent(R.id.widget_compose, clickIntent);
159a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
160a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        // On click intent for Conversation
161a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        final Intent conversationIntent = new Intent();
1629573510415134c4c9157dd0ad1ed015ec47e4116Martin Hibdon        conversationIntent.setPackage(context.getPackageName());
163a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        conversationIntent.setAction(Intent.ACTION_VIEW);
164a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        clickIntent = PendingIntent.getActivity(context, 0, conversationIntent,
165a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook                PendingIntent.FLAG_UPDATE_CURRENT);
166a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        remoteViews.setPendingIntentTemplate(R.id.conversation_list, clickIntent);
167a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    }
168a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
169a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    /**
170a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook     * Persists the information about the specified widget.
171a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook     */
172a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    public static void saveWidgetInformation(Context context, int appWidgetId, Account account,
173b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                final String folderUri) {
174b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        MailPrefs.get(context).configureWidget(appWidgetId, account, folderUri);
175a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    }
176a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
177a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    /**
178a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook     * Returns true if this widget id has been configured and saved.
179a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook     */
180b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy    public boolean isWidgetConfigured(Context context, int appWidgetId, Account account) {
1818c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huang        return isAccountValid(context, account) &&
1828c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huang                MailPrefs.get(context).isWidgetConfigured(appWidgetId);
183a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    }
184a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook
185a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook    protected boolean isAccountValid(Context context, Account account) {
186a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        if (account != null) {
187a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook            Account[] accounts = AccountUtils.getSyncingAccounts(context);
188a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook            for (Account existing : accounts) {
1893b965d78774a42358ce6bbdcc43b4c8df130a60eScott Kennedy                if (existing != null && account.uri.equals(existing.uri)) {
190a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook                    return true;
191a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook                }
192a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook            }
193a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        }
194a39f728390ba2cc849b27260be76753c8a58a085Paul Westbrook        return false;
1958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    }
1968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
1978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    /**
1988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     * Remote Views Factory for Mail Widget.
1998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira     */
2006fe3cb987c44fd67e3094fff99769846d8adb8a8Mindy Pereira    protected static class MailFactory
2018a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            implements RemoteViewsService.RemoteViewsFactory, OnLoadCompleteListener<Cursor> {
2028a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private static final int MAX_CONVERSATIONS_COUNT = 25;
2038a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private static final int MAX_SENDERS_LENGTH = 25;
20450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook
20550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook        private static final int FOLDER_LOADER_ID = 0;
20650e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook        private static final int CONVERSATION_CURSOR_LOADER_ID = 1;
207c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler        private static final int ACCOUNT_LOADER_ID = 2;
20850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook
2098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final Context mContext;
2108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final int mAppWidgetId;
2118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private final Account mAccount;
21267aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy        private final int mFolderType;
21310ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux        private final int mFolderCapabilities;
214b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        private final Uri mFolderUri;
215b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        private final Uri mFolderConversationListUri;
216b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        private final String mFolderDisplayName;
217747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei        private final WidgetConversationListItemViewBuilder mWidgetConversationListItemViewBuilder;
21850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook        private CursorLoader mConversationCursorLoader;
2198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private Cursor mConversationCursor;
2208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private CursorLoader mFolderLoader;
221c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler        private CursorLoader mAccountLoader;
2228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private FolderUpdateHandler mFolderUpdateHandler;
2238a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private int mFolderCount;
2248a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private boolean mShouldShowViewMore;
2258a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private boolean mFolderInformationShown = false;
226f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei        private final WidgetService mService;
227c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira        private String mSendersSplitToken;
228178ac28d19065ceca8b293e13cf291d685080aa6Mindy Pereira        private String mElidedPaddingToken;
229a1512b60eb12f732ac249fe7fcdc4fec756ed147Mindy Pereira
230a4968036e4384431de74b96d43a40faaeecf053eMindy Pereira        public MailFactory(Context context, Intent intent, WidgetService service) {
2318a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mContext = context;
2328a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mAppWidgetId = intent.getIntExtra(
2338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
23426a20758413f0f8629ea71a0943304e3ae740165Tony Mantler            mAccount = Account.newInstance(intent.getStringExtra(Utils.EXTRA_ACCOUNT));
23567aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy            mFolderType = intent.getIntExtra(WidgetProvider.EXTRA_FOLDER_TYPE, FolderType.DEFAULT);
23610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            mFolderCapabilities = intent.getIntExtra(WidgetProvider.EXTRA_FOLDER_CAPABILITIES, 0);
237b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            mFolderDisplayName = intent.getStringExtra(WidgetProvider.EXTRA_FOLDER_DISPLAY_NAME);
238b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy
239c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            final Uri folderUri = intent.getParcelableExtra(WidgetProvider.EXTRA_FOLDER_URI);
240c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            final Uri folderConversationListUri =
2418e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang                    intent.getParcelableExtra(WidgetProvider.EXTRA_FOLDER_CONVERSATION_LIST_URI);
2428e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang            if (folderUri != null && folderConversationListUri != null) {
2438e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang                mFolderUri = folderUri;
2448e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang                mFolderConversationListUri = folderConversationListUri;
2458e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang            } else {
2468e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang                // This is a old intent created in version UR8 (or earlier).
24748cfe4613549cafdf36e2a524afba730522bf291Scott Kennedy                String folderString = intent.getStringExtra(Utils.EXTRA_FOLDER);
248c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                //noinspection deprecation
2498e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang                Folder folder = Folder.fromString(folderString);
250f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                if (folder != null) {
251259df5b9e11908c8ef7c91483924891dd96b3c27Scott Kennedy                    mFolderUri = folder.folderUri.fullUri;
252f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                    mFolderConversationListUri = folder.conversationListUri;
253f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                } else {
254f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                    mFolderUri = Uri.EMPTY;
255f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                    mFolderConversationListUri = Uri.EMPTY;
256f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                    // this will mark the widget as unconfigured
257f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                    BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolderType,
25810ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                            mFolderCapabilities, mFolderUri, mFolderConversationListUri,
25910ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                            mFolderDisplayName);
260f8b76a68e46d834c46d14b42b3488293e57e26b1Mark Wei                }
2618e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang            }
2628e99b95bf01c63f733fbd80c28c684350c7d702bAlice Yang
263747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei            mWidgetConversationListItemViewBuilder = new WidgetConversationListItemViewBuilder(
264747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei                    context);
265e25998f8c3d20b37682cfe00aadb4a70c81eb8e4Mindy Pereira            mService = service;
2668a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
2678a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
2698a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onCreate() {
2708a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            // Save the map between widgetId and account to preference
271b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            saveWidgetInformation(mContext, mAppWidgetId, mAccount, mFolderUri.toString());
2728a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
2738a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            // If the account of this widget has been removed, we want to update the widget to
2748a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            // "Tap to configure" mode.
275b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            if (!mService.isWidgetConfigured(mContext, mAppWidgetId, mAccount)) {
27667aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolderType,
27710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        mFolderCapabilities, mFolderUri, mFolderConversationListUri,
27810ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        mFolderDisplayName);
2798a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
2808a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
281ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook            mFolderInformationShown = false;
282ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook
2835f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook            // We want to limit the query result to 25 and don't want these queries to cause network
2845f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook            // traffic
2854880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook            // We also want this cursor to receive notifications on all changes.  Any change that
2864880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook            // the user made locally, the default policy of the UI provider is to not send
2874880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook            // notifications for.  But in this case, since the widget is not using the
2884880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook            // ConversationCursor instance that the UI is using, the widget would not be updated.
289b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            final Uri.Builder builder = mFolderConversationListUri.buildUpon();
2905f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook            final String maxConversations = Integer.toString(MAX_CONVERSATIONS_COUNT);
2915f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook            final Uri widgetConversationQueryUri = builder
2925f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook                    .appendQueryParameter(ConversationListQueryParameters.LIMIT, maxConversations)
2935f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook                    .appendQueryParameter(ConversationListQueryParameters.USE_NETWORK,
2944880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook                            Boolean.FALSE.toString())
2954880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook                    .appendQueryParameter(ConversationListQueryParameters.ALL_NOTIFICATIONS,
2964880b5dea0335c887e6e6e5535365c3a313b96b7Paul Westbrook                            Boolean.TRUE.toString()).build();
2975f2876a4638bbc7fed2d971c777c8e2bef1cdcf2Paul Westbrook
29850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            final Resources res = mContext.getResources();
29950e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            mConversationCursorLoader = new CursorLoader(mContext, widgetConversationQueryUri,
3008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    UIProvider.CONVERSATION_PROJECTION, null, null, null);
30150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            mConversationCursorLoader.registerListener(CONVERSATION_CURSOR_LOADER_ID, this);
30250e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            mConversationCursorLoader.setUpdateThrottle(
30350e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    res.getInteger(R.integer.widget_refresh_delay_ms));
30450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            mConversationCursorLoader.startLoading();
305c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            mSendersSplitToken = res.getString(R.string.senders_split_token);
306178ac28d19065ceca8b293e13cf291d685080aa6Mindy Pereira            mElidedPaddingToken = res.getString(R.string.elided_padding_token);
307b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy            mFolderLoader = new CursorLoader(mContext, mFolderUri, UIProvider.FOLDERS_PROJECTION,
308cfb7f33ff8ef9dc60b9143af32de64c7a3d71f36Mindy Pereira                    null, null, null);
30950e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            mFolderLoader.registerListener(FOLDER_LOADER_ID, this);
31050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            mFolderUpdateHandler = new FolderUpdateHandler(
31150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    res.getInteger(R.integer.widget_folder_refresh_delay_ms));
3128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderUpdateHandler.scheduleTask();
3138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
314c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            mAccountLoader = new CursorLoader(mContext, mAccount.uri,
315c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                    UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES, null, null, null);
316c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            mAccountLoader.registerListener(ACCOUNT_LOADER_ID, this);
317c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            mAccountLoader.startLoading();
3188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
3198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
3218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onDestroy() {
3228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
32350e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                if (mConversationCursorLoader != null) {
32450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    mConversationCursorLoader.reset();
32550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    mConversationCursorLoader.unregisterListener(this);
32650e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    mConversationCursorLoader = null;
3278a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
32850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook
32950e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                // The Loader should close the cursor, so just unset the reference
33050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                // to it here.
33150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                mConversationCursor = null;
3328a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
3338a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3348a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            if (mFolderLoader != null) {
3358a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mFolderLoader.reset();
33650e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                mFolderLoader.unregisterListener(this);
3378a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                mFolderLoader = null;
3388a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
339c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler
340c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            if (mAccountLoader != null) {
341c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                mAccountLoader.reset();
342c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                mAccountLoader.unregisterListener(this);
343c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                mAccountLoader = null;
344c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            }
3458a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
3468a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3478a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
3488a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onDataSetChanged() {
34950e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            // We are not using this as signal to requery the cursor.  The query will be started
35050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            // in the following ways:
35150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            // 1) The Service is started and the loader is started in onCreate()
35250e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            //       This will happen when the service is not running, and
35350e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            //       AppWidgetManager#notifyAppWidgetViewDataChanged() is called
35450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            // 2) The service is running, with a previously created loader.  The loader is watching
35550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            //    for updates from the existing cursor.  If one is seen, the loader will load a new
35650e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            //    cursor in the background.
3578a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            mFolderUpdateHandler.scheduleTask();
3588a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
3598a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3608a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
3618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * Returns the number of items should be shown in the widget list.  This method also updates
3628a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * the boolean that indicates whether the "show more" item should be shown.
3638a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return the number of items to be displayed in the list.
3648a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
3658a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
3668a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public int getCount() {
3678a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
3688a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                final int count = getConversationCount();
36950e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                final int cursorCount = mConversationCursor != null ?
37050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                        mConversationCursor.getCount() : 0;
37150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                mShouldShowViewMore = count < cursorCount || count < mFolderCount;
3728a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                return count + (mShouldShowViewMore ? 1 : 0);
3738a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
3748a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
3758a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3768a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
3778a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * Returns the number of conversations that should be shown in the widget.  This method
3788a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * doesn't update the boolean that indicates that the "show more" item should be included
3798a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * in the list.
380c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler         * @return count
3818a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
3828a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private int getConversationCount() {
3838a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
38450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                final int cursorCount = mConversationCursor != null ?
38550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                        mConversationCursor.getCount() : 0;
38650e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                return Math.min(cursorCount, MAX_CONVERSATIONS_COUNT);
3878a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
3888a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
3898a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
3908a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
3918a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return the {@link RemoteViews} for a specific position in the list.
3928a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
3938a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
3948a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public RemoteViews getViewAt(int position) {
3958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            synchronized (sWidgetLock) {
3968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // "View more conversations" view.
3977be40b04b7a573f6c287a32f2437a2cce9afac51Paul Westbrook                if (mConversationCursor == null || mConversationCursor.isClosed()
3988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                        || (mShouldShowViewMore && position >= getConversationCount())) {
3998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    return getViewMoreConversationsView();
4008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
4018a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
4028a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (!mConversationCursor.moveToPosition(position)) {
403c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                    // If we ever fail to move to a position, return the
404c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                    // "View More conversations"
4058a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    // view.
406c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                    LogUtils.e(LOG_TAG, "Failed to move to position %d in the cursor.", position);
4078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    return getViewMoreConversationsView();
4088a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
4098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
410a831b2f1ba6d3c18d5e54563df8bae1f589c4257Mindy Pereira                Conversation conversation = new Conversation(mConversationCursor);
4118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Split the senders and status from the instructions.
4128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
413edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler                ArrayList<SpannableString> senders = new ArrayList<SpannableString>();
414edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler                SendersView.format(mContext, conversation.conversationInfo, "",
415c9e00fcb05c5311633dc3a414b9bf685816b4350James Lemieux                        MAX_SENDERS_LENGTH, senders, null, null, mAccount,
41610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        Folder.shouldShowRecipients(mFolderCapabilities), true);
41710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                final SpannableStringBuilder senderBuilder = elideParticipants(senders);
418edd6c1a2807d2ade930dfd4622707298dc470d64Tony Mantler
4198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Get styled date.
420c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                CharSequence date = DateUtils.getRelativeTimeSpanString(mContext,
421c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                        conversation.dateMs);
4228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
42367aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                final int ignoreFolderType;
42467aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                if ((mFolderType & FolderType.INBOX) != 0) {
42567aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                    ignoreFolderType = FolderType.INBOX;
42667aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                } else {
42767aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                    ignoreFolderType = -1;
42867aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy                }
42967aa9e5162a15fb8b46b4113ac627cd20668f095Scott Kennedy
4308a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Load up our remote view.
431928308d6b10d353493492ad05e0227f960ddaa7eJames Lemieux                RemoteViews remoteViews = mWidgetConversationListItemViewBuilder.getStyledView(
432928308d6b10d353493492ad05e0227f960ddaa7eJames Lemieux                        mContext, date, conversation, new FolderUri(mFolderUri), ignoreFolderType,
43389bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein                        senderBuilder,
43489bf7ce646a0ee67e0a8ee10d8834bcf54257d97Andrew Sapperstein                        ConversationItemView.filterTag(mContext, conversation.subject));
4358a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
4368a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // On click intent.
437747e678d20e99ddd17953ff4530d2ee3f1c6cfc7Mark Wei                remoteViews.setOnClickFillInIntent(R.id.widget_conversation_list_item,
438b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                        Utils.createViewConversationIntent(mContext, conversation, mFolderUri,
439b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                                mAccount));
4408a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
4418a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                return remoteViews;
4428a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
4438a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
4448c8c9927c5b1ec2cb26ee20c3e755121723b979bAndy Huang
44510ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux        private SpannableStringBuilder elideParticipants(List<SpannableString> parts) {
44610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            final SpannableStringBuilder builder = new SpannableStringBuilder();
447b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira            SpannableString prevSender = null;
44810ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux
44910ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            boolean skipToHeader = false;
45010ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux
45110ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            // start with "To: " if we're showing recipients
45210ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            if (Folder.shouldShowRecipients(mFolderCapabilities)) {
45310ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                builder.append(SendersView.getFormattedToHeader());
45410ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                skipToHeader = true;
45510ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            }
45610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux
45710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux            for (SpannableString sender : parts) {
458a3831fb1589226a14adc67657f16204718c8ac74Paul Westbrook                if (sender == null) {
459a3831fb1589226a14adc67657f16204718c8ac74Paul Westbrook                    LogUtils.e(LOG_TAG, "null sender while iterating over styledSenders");
460a3831fb1589226a14adc67657f16204718c8ac74Paul Westbrook                    continue;
461c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                }
462c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                CharacterStyle[] spans = sender.getSpans(0, sender.length(), CharacterStyle.class);
463b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                if (SendersView.sElidedString.equals(sender.toString())) {
464b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                    prevSender = sender;
465178ac28d19065ceca8b293e13cf291d685080aa6Mindy Pereira                    sender = copyStyles(spans, mElidedPaddingToken + sender + mElidedPaddingToken);
46610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                } else if (!skipToHeader && builder.length() > 0
467b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                        && (prevSender == null || !SendersView.sElidedString.equals(prevSender
468b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                                .toString()))) {
469b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                    prevSender = sender;
470b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                    sender = copyStyles(spans, mSendersSplitToken + sender);
471b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                } else {
472b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                    prevSender = sender;
47310ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                    skipToHeader = false;
474b1cbb89f72631bb7e34822b98e8d0842ebd01b83Mindy Pereira                }
475aa86405afdef0f38894c166d3a53f452a478bec3Mindy Pereira                builder.append(sender);
476c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            }
477c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            return builder;
478c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira        }
479c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira
480b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        private static SpannableString copyStyles(CharacterStyle[] spans, CharSequence newText) {
481c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            SpannableString s = new SpannableString(newText);
482c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            if (spans != null && spans.length > 0) {
483c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira                s.setSpan(spans[0], 0, s.length(), 0);
484c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            }
485c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira            return s;
486c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira        }
487c3efca18f09904f4ce39395169559c5d82bd3d06Mindy Pereira
4888a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
4898a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         * @return the "View more conversations" view.
4908a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
4918a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private RemoteViews getViewMoreConversationsView() {
4928a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
4938a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            view.setTextViewText(
4948a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    R.id.loading_text, mContext.getText(R.string.view_more_conversations));
495161f50d0fabdaa384a63ce69f595861c5e69795fMindy Pereira            view.setOnClickFillInIntent(R.id.widget_loading,
496b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                    Utils.createViewFolderIntent(mContext, mFolderUri, mAccount));
4978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return view;
4988a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
4998a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
5008a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
5018a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public RemoteViews getLoadingView() {
5028a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
5038a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            view.setTextViewText(
5048a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    R.id.loading_text, mContext.getText(R.string.loading_conversation));
5058a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return view;
5068a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
5078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
5088a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
5098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public int getViewTypeCount() {
5108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return 2;
5118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
5128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
5138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
5148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public long getItemId(int position) {
5158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return position;
5168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
5178a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
5188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
5198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public boolean hasStableIds() {
5208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            return false;
5218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
5228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
5238a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        @Override
5248a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
52550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
5265707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei            final RemoteViews remoteViews =
5275707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                    new RemoteViews(mContext.getPackageName(), R.layout.widget);
52850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook
529c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            if (!mService.isWidgetConfigured(mContext, mAppWidgetId, mAccount)) {
530c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolderType,
53110ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        mFolderCapabilities, mFolderUri, mFolderConversationListUri,
53210ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        mFolderDisplayName);
533c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            }
534c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler
53550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            if (loader == mFolderLoader) {
536127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                if (!isDataValid(data)) {
5372c4eacfcad644111fa409d9c8645cd866818269cTony Mantler                    // Our folder may have disappeared on us
5382c4eacfcad644111fa409d9c8645cd866818269cTony Mantler                    BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolderType,
53910ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                            mFolderCapabilities, mFolderUri, mFolderConversationListUri,
54010ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                            mFolderDisplayName);
5412c4eacfcad644111fa409d9c8645cd866818269cTony Mantler
542127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                    return;
543127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                }
544127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook
54550e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                final int unreadCount = data.getInt(UIProvider.FOLDER_UNREAD_COUNT_COLUMN);
54650e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                final String folderName = data.getString(UIProvider.FOLDER_NAME_COLUMN);
54750e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                mFolderCount = data.getInt(UIProvider.FOLDER_TOTAL_COUNT_COLUMN);
54850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook
549ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                if (!mFolderInformationShown && !TextUtils.isEmpty(folderName) &&
55026a20758413f0f8629ea71a0943304e3ae740165Tony Mantler                        !TextUtils.isEmpty(mAccount.getDisplayName())) {
55150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    // We want to do a full update to the widget at least once, as the widget
55250e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    // manager doesn't cache the state of the remote views when doing a partial
55350e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    // widget update. This causes the folder name to be shown as blank if the state
55450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    // of the widget is restored.
555b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy                    mService.configureValidAccountWidget(mContext, remoteViews, mAppWidgetId,
55610ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                            mAccount, mFolderType, mFolderCapabilities, mFolderUri,
55710ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                            mFolderConversationListUri, folderName);
55850e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    appWidgetManager.updateAppWidget(mAppWidgetId, remoteViews);
55950e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                    mFolderInformationShown = true;
56050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                }
5618a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
562ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                // There is no reason to overwrite a valid non-null folder name with an empty string
563ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                if (!TextUtils.isEmpty(folderName)) {
564ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                    remoteViews.setViewVisibility(R.id.widget_folder, View.VISIBLE);
565418f38555c0bc6ff6c724f0bed6e7e897f4da91aRohan Shah                    remoteViews.setViewVisibility(R.id.widget_compose, View.VISIBLE);
566ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                    remoteViews.setTextViewText(R.id.widget_folder, folderName);
567ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                } else {
568ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                    LogUtils.e(LOG_TAG, "Empty folder name");
569ed00e05911f63d47104ffd8ebc3d8ddabceb238ePaul Westbrook                }
57050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook
57150e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                appWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews);
57250e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            } else if (loader == mConversationCursorLoader) {
57350e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                // We want to cache the new cursor
57450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                synchronized (sWidgetLock) {
575127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                    if (!isDataValid(data)) {
576127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                        mConversationCursor = null;
577127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                    } else {
578127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                        mConversationCursor = data;
579127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook                    }
58050e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook                }
5815707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei
5825707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                appWidgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId,
5835707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                        R.id.conversation_list);
5845707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei
5855707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                if (mConversationCursor == null || mConversationCursor.getCount() == 0) {
5865707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                    remoteViews.setTextViewText(R.id.empty_conversation_list,
587a44b0eda75782a95cbf16e8d14e8080325028657Andrew Sapperstein                            mContext.getString(R.string.empty_folder));
5885707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                    appWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews);
5895707f53aecf206a92ff7e90cb48e483f54b0845aMark Wei                }
590c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler            } else if (loader == mAccountLoader) {
591c197d4c5bfe62255ca664d7029367ef45f946219Tony Mantler                BaseWidgetProvider.updateWidget(mContext, mAppWidgetId, mAccount, mFolderType,
59210ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        mFolderCapabilities, mFolderUri, mFolderConversationListUri,
59310ea28ab588d8e922c0f78f0f47fe479739ec2cfJames Lemieux                        mFolderDisplayName);
59450e1b09e6e5bb8e1993d98fb14c690de743b53dfPaul Westbrook            }
5958a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
5968a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
5978a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        /**
598127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook         * Returns a boolean indicating whether this cursor has valid data.
599127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook         * Note: This seeks to the first position in the cursor
600127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook         */
601b39aaf53a555c1046ef31b3fecf15d086acca013Scott Kennedy        private static boolean isDataValid(Cursor cursor) {
602127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook            return cursor != null && !cursor.isClosed() && cursor.moveToFirst();
603127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook        }
604127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook
605127d1fc15dc6a40e8dc9c4d4b003f4bfa915548fPaul Westbrook        /**
60630fd47bf1947da5ad813cb957b6cbe569dce563aMindy Pereira         * A {@link DelayedTaskHandler} to throttle folder update to a reasonable rate.
6078a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira         */
6088a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        private class FolderUpdateHandler extends DelayedTaskHandler {
6098a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            public FolderUpdateHandler(int refreshDelay) {
6108a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                super(Looper.myLooper(), refreshDelay);
6118a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
6128a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira
6138a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            @Override
6148a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            protected void performTask() {
6158a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                // Start the loader. The cached data will be returned if present.
6168a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                if (mFolderLoader != null) {
6178a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                    mFolderLoader.startLoading();
6188a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira                }
6198a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira            }
6208a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira        }
6218a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira    }
6228a8c50d8fcc4f20549c9f395edbad017a940e72bMindy Pereira}
623