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