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