WidgetProvider.java revision b74f2204672ae01e1b2954f5714031d369c332cb
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.provider; 18 19import android.appwidget.AppWidgetManager; 20import android.content.ContentResolver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.SharedPreferences; 24import android.database.Cursor; 25import android.database.CursorWrapper; 26import android.net.Uri; 27import android.provider.BaseColumns; 28 29import com.android.emailcommon.provider.Account; 30import com.android.emailcommon.provider.Mailbox; 31import com.android.mail.providers.Folder; 32import com.android.mail.providers.UIProvider; 33import com.android.mail.providers.UIProvider.AccountColumns; 34import com.android.mail.utils.LogTag; 35import com.android.mail.utils.LogUtils; 36import com.android.mail.widget.BaseWidgetProvider; 37import com.android.mail.widget.WidgetService; 38 39public class WidgetProvider extends BaseWidgetProvider { 40 private static final String LEGACY_PREFS_NAME = "com.android.email.widget.WidgetManager"; 41 private static final String LEGACY_ACCOUNT_ID_PREFIX = "accountId_"; 42 private static final String LEGACY_MAILBOX_ID_PREFIX = "mailboxId_"; 43 44 private static final String LOG_TAG = LogTag.getLogTag(); 45 46 // This projection is needed, as if we were to request the capabilities of the account, 47 // that provider attempts to bind to the email service to get this information. It is not 48 // valid to bind to a service in a broadcast receiver, as the bind just blocks, for the amount 49 // of time specified in the timeout. 50 // Instead, this projection doesn't include the capabilities column. The cursor wrapper then 51 // makes sure that the Account objects can find all of the columns it expects. 52 private static final String[] WIDGET_ACCOUNTS_PROJECTION = { 53 BaseColumns._ID, 54 AccountColumns.NAME, 55 AccountColumns.PROVIDER_VERSION, 56 AccountColumns.URI, 57 AccountColumns.FOLDER_LIST_URI, 58 AccountColumns.FULL_FOLDER_LIST_URI, 59 AccountColumns.SEARCH_URI, 60 AccountColumns.ACCOUNT_FROM_ADDRESSES, 61 AccountColumns.SAVE_DRAFT_URI, 62 AccountColumns.SEND_MAIL_URI, 63 AccountColumns.EXPUNGE_MESSAGE_URI, 64 AccountColumns.UNDO_URI, 65 AccountColumns.SETTINGS_INTENT_URI, 66 AccountColumns.SYNC_STATUS, 67 AccountColumns.HELP_INTENT_URI, 68 AccountColumns.SEND_FEEDBACK_INTENT_URI, 69 AccountColumns.COMPOSE_URI, 70 AccountColumns.MIME_TYPE, 71 AccountColumns.RECENT_FOLDER_LIST_URI, 72 AccountColumns.COLOR, 73 AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI, 74 AccountColumns.MANUAL_SYNC_URI, 75 AccountColumns.SettingsColumns.SIGNATURE, 76 AccountColumns.SettingsColumns.AUTO_ADVANCE, 77 AccountColumns.SettingsColumns.MESSAGE_TEXT_SIZE, 78 AccountColumns.SettingsColumns.SNAP_HEADERS, 79 AccountColumns.SettingsColumns.REPLY_BEHAVIOR, 80 AccountColumns.SettingsColumns.HIDE_CHECKBOXES, 81 AccountColumns.SettingsColumns.CONFIRM_DELETE, 82 AccountColumns.SettingsColumns.CONFIRM_ARCHIVE, 83 AccountColumns.SettingsColumns.CONFIRM_SEND, 84 AccountColumns.SettingsColumns.DEFAULT_INBOX, 85 AccountColumns.SettingsColumns.DEFAULT_INBOX_NAME, 86 AccountColumns.SettingsColumns.FORCE_REPLY_FROM_DEFAULT, 87 AccountColumns.SettingsColumns.MAX_ATTACHMENT_SIZE, 88 AccountColumns.SettingsColumns.SWIPE, 89 AccountColumns.SettingsColumns.PRIORITY_ARROWS_ENABLED 90 }; 91 92 93 /** 94 * Remove preferences when deleting widget 95 */ 96 @Override 97 public void onDeleted(Context context, int[] appWidgetIds) { 98 super.onDeleted(context, appWidgetIds); 99 100 // Remove any legacy Email widget information 101 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 102 final SharedPreferences.Editor editor = prefs.edit(); 103 for (int widgetId : appWidgetIds) { 104 // Remove the account in the preference 105 editor.remove(LEGACY_ACCOUNT_ID_PREFIX + widgetId); 106 editor.remove(LEGACY_MAILBOX_ID_PREFIX + widgetId); 107 } 108 editor.apply(); 109 } 110 111 @Override 112 protected com.android.mail.providers.Account getAccountObject( 113 Context context, String accountUri) { 114 final ContentResolver resolver = context.getContentResolver(); 115 final Cursor sparseAccountCursor = resolver.query(Uri.parse(accountUri), 116 WIDGET_ACCOUNTS_PROJECTION, null, null, null); 117 118 return getPopulatedAccountObject(sparseAccountCursor); 119 } 120 121 122 @Override 123 protected boolean isAccountValid(Context context, com.android.mail.providers.Account account) { 124 if (account != null) { 125 final ContentResolver resolver = context.getContentResolver(); 126 final Cursor sparseAccountCursor = resolver.query(account.uri, 127 WIDGET_ACCOUNTS_PROJECTION, null, null, null); 128 if (sparseAccountCursor != null) { 129 try { 130 return sparseAccountCursor.getCount() > 0; 131 } finally { 132 sparseAccountCursor.close(); 133 } 134 } 135 } 136 return false; 137 } 138 139 @Override 140 protected void migrateLegacyWidgetInformation(Context context, int widgetId) { 141 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 142 final SharedPreferences.Editor editor = prefs.edit(); 143 144 long accountId = loadAccountIdPref(context, widgetId); 145 long mailboxId = loadMailboxIdPref(context, widgetId); 146 // Legacy support; if preferences haven't been saved for this widget, load something 147 if (accountId == Account.NO_ACCOUNT || mailboxId == Mailbox.NO_MAILBOX) { 148 LogUtils.d(LOG_TAG, "Couldn't load account or mailbox. accountId: %d" + 149 " mailboxId: %d widgetId %d", accountId, mailboxId); 150 return; 151 } 152 153 // Get Account and folder objects for the account id and mailbox id 154 final com.android.mail.providers.Account uiAccount = getAccount(context, accountId); 155 final Folder uiFolder = getFolder(context, mailboxId); 156 157 if (uiAccount != null && uiFolder != null) { 158 WidgetService.saveWidgetInformation(context, widgetId, uiAccount, uiFolder); 159 160 updateWidgetInternal(context, widgetId, uiAccount, uiFolder); 161 162 // Now remove the old legacy preference value 163 editor.remove(LEGACY_ACCOUNT_ID_PREFIX + widgetId); 164 editor.remove(LEGACY_MAILBOX_ID_PREFIX + widgetId); 165 } 166 editor.apply(); 167 } 168 169 private com.android.mail.providers.Account getAccount(Context context, long accountId) { 170 final ContentResolver resolver = context.getContentResolver(); 171 final Cursor ac = resolver.query(EmailProvider.uiUri("uiaccount", accountId), 172 WIDGET_ACCOUNTS_PROJECTION, null, null, null); 173 174 com.android.mail.providers.Account uiAccount = getPopulatedAccountObject(ac); 175 176 return uiAccount; 177 } 178 179 private com.android.mail.providers.Account getPopulatedAccountObject(final Cursor ac) { 180 if (ac == null) { 181 LogUtils.e(LOG_TAG, "Null account cursor"); 182 return null; 183 } 184 185 final Cursor accountCursor = new SparseAccountCursorWrapper(ac); 186 187 com.android.mail.providers.Account uiAccount = null; 188 try { 189 if (accountCursor.moveToFirst()) { 190 uiAccount = new com.android.mail.providers.Account(accountCursor); 191 } 192 } finally { 193 accountCursor.close(); 194 } 195 return uiAccount; 196 } 197 198 private Folder getFolder(Context context, long mailboxId) { 199 final ContentResolver resolver = context.getContentResolver(); 200 final Cursor fc = resolver.query(EmailProvider.uiUri("uifolder", mailboxId), 201 UIProvider.FOLDERS_PROJECTION, null, null, null); 202 203 if (fc == null) { 204 LogUtils.e(LOG_TAG, "Null folder cursor for mailboxId %d", mailboxId); 205 return null; 206 } 207 208 Folder uiFolder = null; 209 try { 210 if (fc.moveToFirst()) { 211 uiFolder = new Folder(fc); 212 } 213 } finally { 214 fc.close(); 215 } 216 return uiFolder; 217 } 218 219 /** 220 * Returns the saved account ID for the given widget. Otherwise, 221 * {@link com.android.emailcommon.provider.Account#NO_ACCOUNT} if 222 * the account ID was not previously saved. 223 */ 224 static long loadAccountIdPref(Context context, int appWidgetId) { 225 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 226 long accountId = prefs.getLong(LEGACY_ACCOUNT_ID_PREFIX + appWidgetId, Account.NO_ACCOUNT); 227 return accountId; 228 } 229 230 /** 231 * Returns the saved mailbox ID for the given widget. Otherwise, 232 * {@link com.android.emailcommon.provider.Mailbox#NO_MAILBOX} if 233 * the mailbox ID was not previously saved. 234 */ 235 static long loadMailboxIdPref(Context context, int appWidgetId) { 236 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 237 long mailboxId = prefs.getLong(LEGACY_MAILBOX_ID_PREFIX + appWidgetId, Mailbox.NO_MAILBOX); 238 return mailboxId; 239 } 240 241 private class SparseAccountCursorWrapper extends CursorWrapper { 242 public SparseAccountCursorWrapper(Cursor cursor) { 243 super(cursor); 244 } 245 246 @Override 247 public int getColumnCount () { 248 return UIProvider.ACCOUNTS_PROJECTION.length; 249 } 250 251 @Override 252 public int getColumnIndex (String columnName) { 253 for (int i = 0; i < UIProvider.ACCOUNTS_PROJECTION.length; i++) { 254 if (UIProvider.ACCOUNTS_PROJECTION[i].equals(columnName)) { 255 return i; 256 } 257 } 258 return -1; 259 } 260 261 @Override 262 public String getColumnName (int columnIndex) { 263 return UIProvider.ACCOUNTS_PROJECTION[columnIndex]; 264 } 265 266 @Override 267 public String[] getColumnNames () { 268 return UIProvider.ACCOUNTS_PROJECTION; 269 } 270 271 @Override 272 public int getInt (int columnIndex) { 273 if (columnIndex == UIProvider.ACCOUNT_CAPABILITIES_COLUMN) { 274 return 0; 275 } 276 return super.getInt(convertColumnIndex(columnIndex)); 277 } 278 279 @Override 280 public long getLong (int columnIndex) { 281 return super.getLong(convertColumnIndex(columnIndex)); 282 } 283 284 @Override 285 public String getString (int columnIndex) { 286 return super.getString(convertColumnIndex(columnIndex)); 287 } 288 289 private int convertColumnIndex(int columnIndex) { 290 // Since this sparse cursor doesn't have the capabilities column, 291 // we need to adjust all of the column indexes that come after where the 292 // capabilities column should be 293 if (columnIndex > UIProvider.ACCOUNT_CAPABILITIES_COLUMN) { 294 return columnIndex - 1; 295 } 296 return columnIndex; 297 } 298 } 299 300} 301