WidgetProvider.java revision 21959b3f297fe96c33b22b623a6c426e95d7effc
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 }; 89 90 91 /** 92 * Remove preferences when deleting widget 93 */ 94 @Override 95 public void onDeleted(Context context, int[] appWidgetIds) { 96 super.onDeleted(context, appWidgetIds); 97 98 // Remove any legacy Email widget information 99 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 100 final SharedPreferences.Editor editor = prefs.edit(); 101 for (int widgetId : appWidgetIds) { 102 // Remove the account in the preference 103 editor.remove(LEGACY_ACCOUNT_ID_PREFIX + widgetId); 104 editor.remove(LEGACY_MAILBOX_ID_PREFIX + widgetId); 105 } 106 editor.apply(); 107 } 108 109 @Override 110 protected com.android.mail.providers.Account getAccountObject( 111 Context context, String accountUri) { 112 final ContentResolver resolver = context.getContentResolver(); 113 final Cursor sparseAccountCursor = resolver.query(Uri.parse(accountUri), 114 WIDGET_ACCOUNTS_PROJECTION, null, null, null); 115 116 return getPopulatedAccountObject(sparseAccountCursor); 117 } 118 119 120 @Override 121 protected boolean isAccountValid(Context context, com.android.mail.providers.Account account) { 122 if (account != null) { 123 final ContentResolver resolver = context.getContentResolver(); 124 final Cursor sparseAccountCursor = resolver.query(account.uri, 125 WIDGET_ACCOUNTS_PROJECTION, null, null, null); 126 if (sparseAccountCursor != null) { 127 try { 128 return sparseAccountCursor.getCount() > 0; 129 } finally { 130 sparseAccountCursor.close(); 131 } 132 } 133 } 134 return false; 135 } 136 137 @Override 138 protected void migrateLegacyWidgetInformation(Context context, int widgetId) { 139 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 140 final SharedPreferences.Editor editor = prefs.edit(); 141 142 long accountId = loadAccountIdPref(context, widgetId); 143 long mailboxId = loadMailboxIdPref(context, widgetId); 144 // Legacy support; if preferences haven't been saved for this widget, load something 145 if (accountId == Account.NO_ACCOUNT || mailboxId == Mailbox.NO_MAILBOX) { 146 LogUtils.d(LOG_TAG, "Couldn't load account or mailbox. accountId: %d" + 147 " mailboxId: %d widgetId %d", accountId, mailboxId); 148 return; 149 } 150 151 // Get Account and folder objects for the account id and mailbox id 152 final com.android.mail.providers.Account uiAccount = getAccount(context, accountId); 153 final Folder uiFolder = getFolder(context, mailboxId); 154 155 if (uiAccount != null && uiFolder != null) { 156 WidgetService.saveWidgetInformation(context, widgetId, uiAccount, uiFolder); 157 158 updateWidgetInternal(context, widgetId, uiAccount, uiFolder); 159 160 // Now remove the old legacy preference value 161 editor.remove(LEGACY_ACCOUNT_ID_PREFIX + widgetId); 162 editor.remove(LEGACY_MAILBOX_ID_PREFIX + widgetId); 163 } 164 editor.apply(); 165 } 166 167 private com.android.mail.providers.Account getAccount(Context context, long accountId) { 168 final ContentResolver resolver = context.getContentResolver(); 169 final Cursor ac = resolver.query(EmailProvider.uiUri("uiaccount", accountId), 170 WIDGET_ACCOUNTS_PROJECTION, null, null, null); 171 172 com.android.mail.providers.Account uiAccount = getPopulatedAccountObject(ac); 173 174 return uiAccount; 175 } 176 177 private com.android.mail.providers.Account getPopulatedAccountObject(final Cursor ac) { 178 if (ac == null) { 179 LogUtils.e(LOG_TAG, "Null account cursor"); 180 return null; 181 } 182 183 final Cursor accountCursor = new SparseAccountCursorWrapper(ac); 184 185 com.android.mail.providers.Account uiAccount = null; 186 try { 187 if (accountCursor.moveToFirst()) { 188 uiAccount = new com.android.mail.providers.Account(accountCursor); 189 } 190 } finally { 191 accountCursor.close(); 192 } 193 return uiAccount; 194 } 195 196 private Folder getFolder(Context context, long mailboxId) { 197 final ContentResolver resolver = context.getContentResolver(); 198 final Cursor fc = resolver.query(EmailProvider.uiUri("uifolder", mailboxId), 199 UIProvider.FOLDERS_PROJECTION, null, null, null); 200 201 if (fc == null) { 202 LogUtils.e(LOG_TAG, "Null folder cursor for mailboxId %d", mailboxId); 203 return null; 204 } 205 206 Folder uiFolder = null; 207 try { 208 if (fc.moveToFirst()) { 209 uiFolder = new Folder(fc); 210 } 211 } finally { 212 fc.close(); 213 } 214 return uiFolder; 215 } 216 217 /** 218 * Returns the saved account ID for the given widget. Otherwise, 219 * {@link com.android.emailcommon.provider.Account#NO_ACCOUNT} if 220 * the account ID was not previously saved. 221 */ 222 static long loadAccountIdPref(Context context, int appWidgetId) { 223 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 224 long accountId = prefs.getLong(LEGACY_ACCOUNT_ID_PREFIX + appWidgetId, Account.NO_ACCOUNT); 225 return accountId; 226 } 227 228 /** 229 * Returns the saved mailbox ID for the given widget. Otherwise, 230 * {@link com.android.emailcommon.provider.Mailbox#NO_MAILBOX} if 231 * the mailbox ID was not previously saved. 232 */ 233 static long loadMailboxIdPref(Context context, int appWidgetId) { 234 final SharedPreferences prefs = context.getSharedPreferences(LEGACY_PREFS_NAME, 0); 235 long mailboxId = prefs.getLong(LEGACY_MAILBOX_ID_PREFIX + appWidgetId, Mailbox.NO_MAILBOX); 236 return mailboxId; 237 } 238 239 private class SparseAccountCursorWrapper extends CursorWrapper { 240 public SparseAccountCursorWrapper(Cursor cursor) { 241 super(cursor); 242 } 243 244 @Override 245 public int getColumnCount () { 246 return UIProvider.ACCOUNTS_PROJECTION.length; 247 } 248 249 @Override 250 public int getColumnIndex (String columnName) { 251 for (int i = 0; i < UIProvider.ACCOUNTS_PROJECTION.length; i++) { 252 if (UIProvider.ACCOUNTS_PROJECTION[i].equals(columnName)) { 253 return i; 254 } 255 } 256 return -1; 257 } 258 259 @Override 260 public String getColumnName (int columnIndex) { 261 return UIProvider.ACCOUNTS_PROJECTION[columnIndex]; 262 } 263 264 @Override 265 public String[] getColumnNames () { 266 return UIProvider.ACCOUNTS_PROJECTION; 267 } 268 269 @Override 270 public int getInt (int columnIndex) { 271 if (columnIndex == UIProvider.ACCOUNT_CAPABILITIES_COLUMN) { 272 return 0; 273 } 274 return super.getInt(convertColumnIndex(columnIndex)); 275 } 276 277 @Override 278 public long getLong (int columnIndex) { 279 return super.getLong(convertColumnIndex(columnIndex)); 280 } 281 282 @Override 283 public String getString (int columnIndex) { 284 return super.getString(convertColumnIndex(columnIndex)); 285 } 286 287 private int convertColumnIndex(int columnIndex) { 288 // Since this sparse cursor doesn't have the capabilities column, 289 // we need to adjust all of the column indexes that come after where the 290 // capabilities column should be 291 if (columnIndex > UIProvider.ACCOUNT_CAPABILITIES_COLUMN) { 292 return columnIndex - 1; 293 } 294 return columnIndex; 295 } 296 } 297 298} 299