MailActivityEmail.java revision 281c6365fb95037ca284dd8c910538639e8b3dcb
1/*
2 * Copyright (C) 2008 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.email2.ui;
18
19import android.content.ComponentName;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.PackageManager;
24import android.content.UriMatcher;
25import android.database.Cursor;
26import android.net.Uri;
27import android.os.Bundle;
28
29import com.android.email.NotificationController;
30import com.android.email.Preferences;
31import com.android.email.R;
32import com.android.email.provider.EmailProvider;
33import com.android.email.service.AttachmentDownloadService;
34import com.android.email.service.EmailServiceUtils;
35import com.android.email.service.MailService;
36import com.android.emailcommon.Logging;
37import com.android.emailcommon.TempDirectory;
38import com.android.emailcommon.provider.Account;
39import com.android.emailcommon.provider.EmailContent;
40import com.android.emailcommon.provider.Mailbox;
41import com.android.emailcommon.service.EmailServiceProxy;
42import com.android.emailcommon.utility.EmailAsyncTask;
43import com.android.emailcommon.utility.IntentUtilities;
44import com.android.emailcommon.utility.Utility;
45import com.android.mail.providers.Folder;
46import com.android.mail.providers.UIProvider;
47import com.android.mail.utils.LogTag;
48import com.android.mail.utils.LogUtils;
49import com.android.mail.utils.Utils;
50
51public class MailActivityEmail extends com.android.mail.ui.MailActivity {
52    /**
53     * If this is enabled there will be additional logging information sent to
54     * LogUtils.d, including protocol dumps.
55     *
56     * This should only be used for logs that are useful for debbuging user problems,
57     * not for internal/development logs.
58     *
59     * This can be enabled by typing "debug" in the AccountFolderList activity.
60     * Changing the value to 'true' here will likely have no effect at all!
61     *
62     * TODO: rename this to sUserDebug, and rename LOGD below to DEBUG.
63     */
64    public static boolean DEBUG;
65
66    public static final String LOG_TAG = LogTag.getLogTag();
67
68    // Exchange debugging flags (passed to Exchange, when available, via EmailServiceProxy)
69    public static boolean DEBUG_EXCHANGE;
70    public static boolean DEBUG_VERBOSE;
71    public static boolean DEBUG_FILE;
72
73    /**
74     * If true, inhibit hardware graphics acceleration in UI (for a/b testing)
75     */
76    public static boolean sDebugInhibitGraphicsAcceleration = false;
77
78    /**
79     * This is used to force stacked UI to return to the "welcome" screen any time we change
80     * the accounts list (e.g. deleting accounts in the Account Manager preferences.)
81     */
82    private static boolean sAccountsChangedNotification = false;
83
84    private static String sMessageDecodeErrorString;
85
86    private static Thread sUiThread;
87
88    private static final int MATCH_LEGACY_SHORTCUT_INTENT = 1;
89    /**
90     * A matcher for data URI's that specify conversation list info.
91     */
92    private static final UriMatcher sUrlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
93    static {
94        sUrlMatcher.addURI(
95                EmailProvider.LEGACY_AUTHORITY, "view/mailbox", MATCH_LEGACY_SHORTCUT_INTENT);
96    }
97
98
99    /**
100     * Asynchronous version of {@link #setServicesEnabledSync(Context)}.  Use when calling from
101     * UI thread (or lifecycle entry points.)
102     *
103     * @param context
104     */
105    public static void setServicesEnabledAsync(final Context context) {
106        EmailAsyncTask.runAsyncParallel(new Runnable() {
107            @Override
108            public void run() {
109                setServicesEnabledSync(context);
110            }
111        });
112    }
113
114    /**
115     * Called throughout the application when the number of accounts has changed. This method
116     * enables or disables the Compose activity, the boot receiver and the service based on
117     * whether any accounts are configured.
118     *
119     * Blocking call - do not call from UI/lifecycle threads.
120     *
121     * @param context
122     * @return true if there are any accounts configured.
123     */
124    public static boolean setServicesEnabledSync(Context context) {
125        // Make sure we're initialized
126        EmailContent.init(context);
127        Cursor c = null;
128        try {
129            c = context.getContentResolver().query(
130                    Account.CONTENT_URI,
131                    Account.ID_PROJECTION,
132                    null, null, null);
133            boolean enable = c.getCount() > 0;
134            setServicesEnabled(context, enable);
135            return enable;
136        } finally {
137            if (c != null) {
138                c.close();
139            }
140        }
141    }
142
143    private static void setServicesEnabled(Context context, boolean enabled) {
144        PackageManager pm = context.getPackageManager();
145        pm.setComponentEnabledSetting(
146                new ComponentName(context, MailService.class),
147                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
148                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
149                PackageManager.DONT_KILL_APP);
150        pm.setComponentEnabledSetting(
151                new ComponentName(context, AttachmentDownloadService.class),
152                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
153                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
154                PackageManager.DONT_KILL_APP);
155
156        // Start/stop the various services depending on whether there are any accounts
157        startOrStopService(enabled, context, new Intent(context, AttachmentDownloadService.class));
158        NotificationController.getInstance(context).watchForMessages();
159    }
160
161    /**
162     * Starts or stops the service as necessary.
163     * @param enabled If {@code true}, the service will be started. Otherwise, it will be stopped.
164     * @param context The context to manage the service with.
165     * @param intent The intent of the service to be managed.
166     */
167    private static void startOrStopService(boolean enabled, Context context, Intent intent) {
168        if (enabled) {
169            context.startService(intent);
170        } else {
171            context.stopService(intent);
172        }
173    }
174
175    @Override
176    public void onCreate(Bundle bundle) {
177        final Intent intent = getIntent();
178        final Uri data = intent != null ? intent.getData() : null;
179        if (data != null) {
180            final int match = sUrlMatcher.match(data);
181            switch (match) {
182                case MATCH_LEGACY_SHORTCUT_INTENT: {
183                    final long mailboxId = IntentUtilities.getMailboxIdFromIntent(intent);
184                    final Mailbox mailbox = Mailbox.restoreMailboxWithId(this, mailboxId);
185                    if (mailbox == null) {
186                        LogUtils.e(LOG_TAG, "unable to restore mailbox");
187                        break;
188                    }
189
190                    final Intent viewIntent = getViewIntent(mailbox.mAccountKey, mailboxId);
191                    if (viewIntent != null) {
192                        setIntent(viewIntent);
193                    }
194                    break;
195                }
196            }
197        }
198
199        super.onCreate(bundle);
200        sUiThread = Thread.currentThread();
201        Preferences prefs = Preferences.getPreferences(this);
202        DEBUG = prefs.getEnableDebugLogging();
203        sDebugInhibitGraphicsAcceleration = prefs.getInhibitGraphicsAcceleration();
204        enableStrictMode(prefs.getEnableStrictMode());
205        TempDirectory.setTempDirectory(this);
206
207        // Enable logging in the EAS service, so it starts up as early as possible.
208        updateLoggingFlags(this);
209
210        // Get a helper string used deep inside message decoders (which don't have context)
211        sMessageDecodeErrorString = getString(R.string.message_decode_error);
212
213        // Make sure all required services are running when the app is started (can prevent
214        // issues after an adb sync/install)
215        setServicesEnabledAsync(this);
216    }
217
218    /**
219     * Load enabled debug flags from the preferences and update the EAS debug flag.
220     */
221    public static void updateLoggingFlags(Context context) {
222        Preferences prefs = Preferences.getPreferences(context);
223        int debugLogging = prefs.getEnableDebugLogging() ? EmailServiceProxy.DEBUG_BIT : 0;
224        int verboseLogging =
225            prefs.getEnableExchangeLogging() ? EmailServiceProxy.DEBUG_VERBOSE_BIT : 0;
226        int fileLogging =
227            prefs.getEnableExchangeFileLogging() ? EmailServiceProxy.DEBUG_FILE_BIT : 0;
228        int enableStrictMode =
229            prefs.getEnableStrictMode() ? EmailServiceProxy.DEBUG_ENABLE_STRICT_MODE : 0;
230        int debugBits = debugLogging | verboseLogging | fileLogging | enableStrictMode;
231        EmailServiceUtils.setRemoteServicesLogging(context, debugBits);
232     }
233
234    /**
235     * Internal, utility method for logging.
236     * The calls to log() must be guarded with "if (Email.LOGD)" for performance reasons.
237     */
238    public static void log(String message) {
239        LogUtils.d(Logging.LOG_TAG, message);
240    }
241
242    /**
243     * Called by the accounts reconciler to notify that accounts have changed, or by  "Welcome"
244     * to clear the flag.
245     * @param setFlag true to set the notification flag, false to clear it
246     */
247    public static synchronized void setNotifyUiAccountsChanged(boolean setFlag) {
248        sAccountsChangedNotification = setFlag;
249    }
250
251    /**
252     * Called from activity onResume() functions to check for an accounts-changed condition, at
253     * which point they should finish() and jump to the Welcome activity.
254     */
255    public static synchronized boolean getNotifyUiAccountsChanged() {
256        return sAccountsChangedNotification;
257    }
258
259    public static void warnIfUiThread() {
260        if (Thread.currentThread().equals(sUiThread)) {
261            LogUtils.w(Logging.LOG_TAG, "Method called on the UI thread",
262                    new Exception("STACK TRACE"));
263        }
264    }
265
266    /**
267     * Retrieve a simple string that can be used when message decoders encounter bad data.
268     * This is provided here because the protocol decoders typically don't have mContext.
269     */
270    public static String getMessageDecodeErrorString() {
271        return sMessageDecodeErrorString != null ? sMessageDecodeErrorString : "";
272    }
273
274    public static void enableStrictMode(boolean enabled) {
275        Utility.enableStrictMode(enabled);
276    }
277
278    private Intent getViewIntent(long accountId, long mailboxId) {
279        final ContentResolver contentResolver = getContentResolver();
280
281        final Cursor accountCursor = contentResolver.query(
282                EmailProvider.uiUri("uiaccount", accountId),
283                UIProvider.ACCOUNTS_PROJECTION_NO_CAPABILITIES,
284                null, null, null);
285
286        if (accountCursor == null) {
287            LogUtils.e(LOG_TAG, "Null account cursor for mAccountId %d", accountId);
288            return null;
289        }
290
291        com.android.mail.providers.Account account = null;
292        try {
293            if (accountCursor.moveToFirst()) {
294                account = new com.android.mail.providers.Account(accountCursor);
295            }
296        } finally {
297            accountCursor.close();
298        }
299
300
301        final Cursor folderCursor = contentResolver.query(
302                EmailProvider.uiUri("uifolder", mailboxId),
303                UIProvider.FOLDERS_PROJECTION, null, null, null);
304
305        if (folderCursor == null) {
306            LogUtils.e(LOG_TAG, "Null folder cursor for account %d, mailbox %d",
307                    accountId, mailboxId);
308            return null;
309        }
310
311        Folder folder = null;
312        try {
313            if (folderCursor.moveToFirst()) {
314                folder = new Folder(folderCursor);
315            } else {
316                LogUtils.e(LOG_TAG, "Empty folder cursor for account %d, mailbox %d",
317                        accountId, mailboxId);
318                return null;
319            }
320        } finally {
321            folderCursor.close();
322        }
323
324        return Utils.createViewFolderIntent(this, folder.folderUri.fullUri, account);
325    }
326}
327