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.email; 18 19 import android.app.Application; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.database.Cursor; 25 import android.util.Log; 26 27 import com.android.email.activity.MessageCompose; 28 import com.android.email.activity.ShortcutPicker; 29 import com.android.email.service.AttachmentDownloadService; 30 import com.android.email.service.MailService; 31 import com.android.email.widget.WidgetConfiguration; 32 import com.android.emailcommon.Logging; 33 import com.android.emailcommon.TempDirectory; 34 import com.android.emailcommon.provider.Account; 35 import com.android.emailcommon.service.EmailServiceProxy; 36 import com.android.emailcommon.utility.EmailAsyncTask; 37 import com.android.emailcommon.utility.Utility; 38 39 public class Email extends Application { 40 /** 41 * If this is enabled there will be additional logging information sent to 42 * Log.d, including protocol dumps. 43 * 44 * This should only be used for logs that are useful for debbuging user problems, 45 * not for internal/development logs. 46 * 47 * This can be enabled by typing "debug" in the AccountFolderList activity. 48 * Changing the value to 'true' here will likely have no effect at all! 49 * 50 * TODO: rename this to sUserDebug, and rename LOGD below to DEBUG. 51 */ 52 public static boolean DEBUG; 53 54 // Exchange debugging flags (passed to Exchange, when available, via EmailServiceProxy) 55 public static boolean DEBUG_EXCHANGE; 56 public static boolean DEBUG_EXCHANGE_VERBOSE; 57 public static boolean DEBUG_EXCHANGE_FILE; 58 59 /** 60 * If true, inhibit hardware graphics acceleration in UI (for a/b testing) 61 */ 62 public static boolean sDebugInhibitGraphicsAcceleration = false; 63 64 /** 65 * Specifies how many messages will be shown in a folder by default. This number is set 66 * on each new folder and can be incremented with "Load more messages..." by the 67 * VISIBLE_LIMIT_INCREMENT 68 */ 69 public static final int VISIBLE_LIMIT_DEFAULT = 25; 70 71 /** 72 * Number of additional messages to load when a user selects "Load more messages..." 73 */ 74 public static final int VISIBLE_LIMIT_INCREMENT = 25; 75 76 /** 77 * This is used to force stacked UI to return to the "welcome" screen any time we change 78 * the accounts list (e.g. deleting accounts in the Account Manager preferences.) 79 */ 80 private static boolean sAccountsChangedNotification = false; 81 82 private static String sMessageDecodeErrorString; 83 84 private static Thread sUiThread; 85 86 /** 87 * Asynchronous version of {@link #setServicesEnabledSync(Context)}. Use when calling from 88 * UI thread (or lifecycle entry points.) 89 * 90 * @param context 91 */ setServicesEnabledAsync(final Context context)92 public static void setServicesEnabledAsync(final Context context) { 93 EmailAsyncTask.runAsyncParallel(new Runnable() { 94 @Override 95 public void run() { 96 setServicesEnabledSync(context); 97 } 98 }); 99 } 100 101 /** 102 * Called throughout the application when the number of accounts has changed. This method 103 * enables or disables the Compose activity, the boot receiver and the service based on 104 * whether any accounts are configured. 105 * 106 * Blocking call - do not call from UI/lifecycle threads. 107 * 108 * @param context 109 * @return true if there are any accounts configured. 110 */ setServicesEnabledSync(Context context)111 public static boolean setServicesEnabledSync(Context context) { 112 Cursor c = null; 113 try { 114 c = context.getContentResolver().query( 115 Account.CONTENT_URI, 116 Account.ID_PROJECTION, 117 null, null, null); 118 boolean enable = c.getCount() > 0; 119 setServicesEnabled(context, enable); 120 return enable; 121 } finally { 122 if (c != null) { 123 c.close(); 124 } 125 } 126 } 127 setServicesEnabled(Context context, boolean enabled)128 private static void setServicesEnabled(Context context, boolean enabled) { 129 PackageManager pm = context.getPackageManager(); 130 if (!enabled && pm.getComponentEnabledSetting( 131 new ComponentName(context, MailService.class)) == 132 PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { 133 /* 134 * If no accounts now exist but the service is still enabled we're about to disable it 135 * so we'll reschedule to kill off any existing alarms. 136 */ 137 MailService.actionReschedule(context); 138 } 139 pm.setComponentEnabledSetting( 140 new ComponentName(context, MessageCompose.class), 141 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 142 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 143 PackageManager.DONT_KILL_APP); 144 pm.setComponentEnabledSetting( 145 new ComponentName(context, ShortcutPicker.class), 146 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 147 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 148 PackageManager.DONT_KILL_APP); 149 pm.setComponentEnabledSetting( 150 new ComponentName(context, MailService.class), 151 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 152 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 153 PackageManager.DONT_KILL_APP); 154 pm.setComponentEnabledSetting( 155 new ComponentName(context, AttachmentDownloadService.class), 156 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 157 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 158 PackageManager.DONT_KILL_APP); 159 if (enabled && pm.getComponentEnabledSetting( 160 new ComponentName(context, MailService.class)) == 161 PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { 162 /* 163 * And now if accounts do exist then we've just enabled the service and we want to 164 * schedule alarms for the new accounts. 165 */ 166 MailService.actionReschedule(context); 167 } 168 169 // Note - the Email widget is always enabled as it will show a warning if no accounts are 170 // configured. In previous releases, this was disabled if no accounts were set, so we 171 // need to unconditionally enable it here. 172 pm.setComponentEnabledSetting( 173 new ComponentName(context, WidgetConfiguration.class), 174 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 175 PackageManager.DONT_KILL_APP); 176 177 // Start/stop the various services depending on whether there are any accounts 178 startOrStopService(enabled, context, new Intent(context, AttachmentDownloadService.class)); 179 NotificationController.getInstance(context).watchForMessages(enabled); 180 } 181 182 /** 183 * Starts or stops the service as necessary. 184 * @param enabled If {@code true}, the service will be started. Otherwise, it will be stopped. 185 * @param context The context to manage the service with. 186 * @param intent The intent of the service to be managed. 187 */ startOrStopService(boolean enabled, Context context, Intent intent)188 private static void startOrStopService(boolean enabled, Context context, Intent intent) { 189 if (enabled) { 190 context.startService(intent); 191 } else { 192 context.stopService(intent); 193 } 194 } 195 196 @Override onCreate()197 public void onCreate() { 198 super.onCreate(); 199 sUiThread = Thread.currentThread(); 200 Preferences prefs = Preferences.getPreferences(this); 201 DEBUG = prefs.getEnableDebugLogging(); 202 sDebugInhibitGraphicsAcceleration = prefs.getInhibitGraphicsAcceleration(); 203 enableStrictMode(prefs.getEnableStrictMode()); 204 TempDirectory.setTempDirectory(this); 205 206 // Tie MailRefreshManager to the Controller. 207 RefreshManager.getInstance(this); 208 // Reset all accounts to default visible window 209 Controller.getInstance(this).resetVisibleLimits(); 210 211 // Enable logging in the EAS service, so it starts up as early as possible. 212 updateLoggingFlags(this); 213 214 // Get a helper string used deep inside message decoders (which don't have context) 215 sMessageDecodeErrorString = getString(R.string.message_decode_error); 216 217 // Make sure all required services are running when the app is started (can prevent 218 // issues after an adb sync/install) 219 setServicesEnabledAsync(this); 220 } 221 222 /** 223 * Load enabled debug flags from the preferences and update the EAS debug flag. 224 */ updateLoggingFlags(Context context)225 public static void updateLoggingFlags(Context context) { 226 Preferences prefs = Preferences.getPreferences(context); 227 int debugLogging = prefs.getEnableDebugLogging() ? EmailServiceProxy.DEBUG_BIT : 0; 228 int verboseLogging = 229 prefs.getEnableExchangeLogging() ? EmailServiceProxy.DEBUG_VERBOSE_BIT : 0; 230 int fileLogging = 231 prefs.getEnableExchangeFileLogging() ? EmailServiceProxy.DEBUG_FILE_BIT : 0; 232 int enableStrictMode = 233 prefs.getEnableStrictMode() ? EmailServiceProxy.DEBUG_ENABLE_STRICT_MODE : 0; 234 int debugBits = debugLogging | verboseLogging | fileLogging | enableStrictMode; 235 Controller.getInstance(context).serviceLogging(debugBits); 236 } 237 238 /** 239 * Internal, utility method for logging. 240 * The calls to log() must be guarded with "if (Email.LOGD)" for performance reasons. 241 */ log(String message)242 public static void log(String message) { 243 Log.d(Logging.LOG_TAG, message); 244 } 245 246 /** 247 * Called by the accounts reconciler to notify that accounts have changed, or by "Welcome" 248 * to clear the flag. 249 * @param setFlag true to set the notification flag, false to clear it 250 */ setNotifyUiAccountsChanged(boolean setFlag)251 public static synchronized void setNotifyUiAccountsChanged(boolean setFlag) { 252 sAccountsChangedNotification = setFlag; 253 } 254 255 /** 256 * Called from activity onResume() functions to check for an accounts-changed condition, at 257 * which point they should finish() and jump to the Welcome activity. 258 */ getNotifyUiAccountsChanged()259 public static synchronized boolean getNotifyUiAccountsChanged() { 260 return sAccountsChangedNotification; 261 } 262 warnIfUiThread()263 public static void warnIfUiThread() { 264 if (Thread.currentThread().equals(sUiThread)) { 265 Log.w(Logging.LOG_TAG, "Method called on the UI thread", new Exception("STACK TRACE")); 266 } 267 } 268 269 /** 270 * Retrieve a simple string that can be used when message decoders encounter bad data. 271 * This is provided here because the protocol decoders typically don't have mContext. 272 */ getMessageDecodeErrorString()273 public static String getMessageDecodeErrorString() { 274 return sMessageDecodeErrorString != null ? sMessageDecodeErrorString : ""; 275 } 276 enableStrictMode(boolean enabled)277 public static void enableStrictMode(boolean enabled) { 278 Utility.enableStrictMode(enabled); 279 } 280 } 281