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