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