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