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 com.android.email.activity.AccountShortcutPicker; 20 import com.android.email.activity.Debug; 21 import com.android.email.activity.MessageCompose; 22 import com.android.email.provider.EmailContent; 23 import com.android.email.service.MailService; 24 25 import android.app.Application; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.pm.PackageManager; 29 import android.database.Cursor; 30 import android.text.format.DateUtils; 31 import android.util.Log; 32 33 import java.io.File; 34 import java.util.HashMap; 35 36 public class Email extends Application { 37 public static final String LOG_TAG = "Email"; 38 39 /** 40 * If this is enabled there will be additional logging information sent to 41 * Log.d, including protocol dumps. 42 * 43 * This should only be used for logs that are useful for debbuging user problems, 44 * not for internal/development logs. 45 * 46 * This can be enabled by typing "debug" in the AccountFolderList activity. 47 * Changing the value to 'true' here will likely have no effect at all! 48 * 49 * TODO: rename this to sUserDebug, and rename LOGD below to DEBUG. 50 */ 51 public static boolean DEBUG = false; 52 53 /** 54 * If this is enabled than logging that normally hides sensitive information 55 * like passwords will show that information. 56 */ 57 public static boolean DEBUG_SENSITIVE = false; 58 59 /** 60 * Set this to 'true' to enable as much Email logging as possible. 61 * Do not check-in with it set to 'true'! 62 */ 63 public static final boolean LOGD = false; 64 65 /** 66 * The MIME type(s) of attachments we're willing to send via attachments. 67 * 68 * Any attachments may be added via Intents with Intent.ACTION_SEND or ACTION_SEND_MULTIPLE. 69 */ 70 public static final String[] ACCEPTABLE_ATTACHMENT_SEND_INTENT_TYPES = new String[] { 71 "*/*", 72 }; 73 74 /** 75 * The MIME type(s) of attachments we're willing to send from the internal UI. 76 * 77 * NOTE: At the moment it is not possible to open a chooser with a list of filter types, so 78 * the chooser is only opened with the first item in the list. 79 */ 80 public static final String[] ACCEPTABLE_ATTACHMENT_SEND_UI_TYPES = new String[] { 81 "image/*", 82 "video/*", 83 }; 84 85 /** 86 * The MIME type(s) of attachments we're willing to view. 87 */ 88 public static final String[] ACCEPTABLE_ATTACHMENT_VIEW_TYPES = new String[] { 89 "*/*", 90 }; 91 92 /** 93 * The MIME type(s) of attachments we're not willing to view. 94 */ 95 public static final String[] UNACCEPTABLE_ATTACHMENT_VIEW_TYPES = new String[] { 96 }; 97 98 /** 99 * The MIME type(s) of attachments we're willing to download to SD. 100 */ 101 public static final String[] ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] { 102 "image/*", 103 }; 104 105 /** 106 * The MIME type(s) of attachments we're not willing to download to SD. 107 */ 108 public static final String[] UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] { 109 }; 110 111 /** 112 * Specifies how many messages will be shown in a folder by default. This number is set 113 * on each new folder and can be incremented with "Load more messages..." by the 114 * VISIBLE_LIMIT_INCREMENT 115 */ 116 public static final int VISIBLE_LIMIT_DEFAULT = 25; 117 118 /** 119 * Number of additional messages to load when a user selects "Load more messages..." 120 */ 121 public static final int VISIBLE_LIMIT_INCREMENT = 25; 122 123 /** 124 * The maximum size of an attachment we're willing to download (either View or Save) 125 * Attachments that are base64 encoded (most) will be about 1.375x their actual size 126 * so we should probably factor that in. A 5MB attachment will generally be around 127 * 6.8MB downloaded but only 5MB saved. 128 */ 129 public static final int MAX_ATTACHMENT_DOWNLOAD_SIZE = (5 * 1024 * 1024); 130 131 /** 132 * The maximum size of an attachment we're willing to upload (measured as stored on disk). 133 * Attachments that are base64 encoded (most) will be about 1.375x their actual size 134 * so we should probably factor that in. A 5MB attachment will generally be around 135 * 6.8MB uploaded. 136 */ 137 public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024); 138 139 private static HashMap<Long, Long> sMailboxSyncTimes = new HashMap<Long, Long>(); 140 private static final long UPDATE_INTERVAL = 5 * DateUtils.MINUTE_IN_MILLIS; 141 142 /** 143 * This is used to force stacked UI to return to the "welcome" screen any time we change 144 * the accounts list (e.g. deleting accounts in the Account Manager preferences.) 145 */ 146 private static boolean sAccountsChangedNotification = false; 147 148 public static final String EXCHANGE_ACCOUNT_MANAGER_TYPE = "com.android.exchange"; 149 150 // The color chip resources and the RGB color values in the array below must be kept in sync 151 private static final int[] ACCOUNT_COLOR_CHIP_RES_IDS = new int[] { 152 R.drawable.appointment_indicator_leftside_1, 153 R.drawable.appointment_indicator_leftside_2, 154 R.drawable.appointment_indicator_leftside_3, 155 R.drawable.appointment_indicator_leftside_4, 156 R.drawable.appointment_indicator_leftside_5, 157 R.drawable.appointment_indicator_leftside_6, 158 R.drawable.appointment_indicator_leftside_7, 159 R.drawable.appointment_indicator_leftside_8, 160 R.drawable.appointment_indicator_leftside_9, 161 }; 162 163 private static final int[] ACCOUNT_COLOR_CHIP_RGBS = new int[] { 164 0x71aea7, 165 0x621919, 166 0x18462f, 167 0xbf8e52, 168 0x001f79, 169 0xa8afc2, 170 0x6b64c4, 171 0x738359, 172 0x9d50a4, 173 }; 174 175 private static File sTempDirectory; 176 getColorIndexFromAccountId(long accountId)177 /* package for testing */ static int getColorIndexFromAccountId(long accountId) { 178 // Account id is 1-based, so - 1. 179 // Use abs so that it won't possibly return negative. 180 return Math.abs((int) (accountId - 1) % ACCOUNT_COLOR_CHIP_RES_IDS.length); 181 } 182 getAccountColorResourceId(long accountId)183 public static int getAccountColorResourceId(long accountId) { 184 return ACCOUNT_COLOR_CHIP_RES_IDS[getColorIndexFromAccountId(accountId)]; 185 } 186 getAccountColor(long accountId)187 public static int getAccountColor(long accountId) { 188 return ACCOUNT_COLOR_CHIP_RGBS[getColorIndexFromAccountId(accountId)]; 189 } 190 setTempDirectory(Context context)191 public static void setTempDirectory(Context context) { 192 sTempDirectory = context.getCacheDir(); 193 } 194 getTempDirectory()195 public static File getTempDirectory() { 196 if (sTempDirectory == null) { 197 throw new RuntimeException( 198 "TempDirectory not set. " + 199 "If in a unit test, call Email.setTempDirectory(context) in setUp()."); 200 } 201 return sTempDirectory; 202 } 203 204 /** 205 * Called throughout the application when the number of accounts has changed. This method 206 * enables or disables the Compose activity, the boot receiver and the service based on 207 * whether any accounts are configured. Returns true if there are any accounts configured. 208 */ setServicesEnabled(Context context)209 public static boolean setServicesEnabled(Context context) { 210 Cursor c = null; 211 try { 212 c = context.getContentResolver().query( 213 EmailContent.Account.CONTENT_URI, 214 EmailContent.Account.ID_PROJECTION, 215 null, null, null); 216 boolean enable = c.getCount() > 0; 217 setServicesEnabled(context, enable); 218 return enable; 219 } finally { 220 if (c != null) { 221 c.close(); 222 } 223 } 224 } 225 setServicesEnabled(Context context, boolean enabled)226 public static void setServicesEnabled(Context context, boolean enabled) { 227 PackageManager pm = context.getPackageManager(); 228 if (!enabled && pm.getComponentEnabledSetting(new ComponentName(context, MailService.class)) == 229 PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { 230 /* 231 * If no accounts now exist but the service is still enabled we're about to disable it 232 * so we'll reschedule to kill off any existing alarms. 233 */ 234 MailService.actionReschedule(context); 235 } 236 pm.setComponentEnabledSetting( 237 new ComponentName(context, MessageCompose.class), 238 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 239 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 240 PackageManager.DONT_KILL_APP); 241 pm.setComponentEnabledSetting( 242 new ComponentName(context, AccountShortcutPicker.class), 243 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 244 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 245 PackageManager.DONT_KILL_APP); 246 pm.setComponentEnabledSetting( 247 new ComponentName(context, MailService.class), 248 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : 249 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 250 PackageManager.DONT_KILL_APP); 251 if (enabled && pm.getComponentEnabledSetting(new ComponentName(context, MailService.class)) == 252 PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { 253 /* 254 * And now if accounts do exist then we've just enabled the service and we want to 255 * schedule alarms for the new accounts. 256 */ 257 MailService.actionReschedule(context); 258 } 259 } 260 261 @Override onCreate()262 public void onCreate() { 263 super.onCreate(); 264 Preferences prefs = Preferences.getPreferences(this); 265 DEBUG = prefs.getEnableDebugLogging(); 266 DEBUG_SENSITIVE = prefs.getEnableSensitiveLogging(); 267 setTempDirectory(this); 268 269 // Reset all accounts to default visible window 270 Controller.getInstance(this).resetVisibleLimits(); 271 272 // Enable logging in the EAS service, so it starts up as early as possible. 273 Debug.updateLoggingFlags(this); 274 } 275 276 /** 277 * Internal, utility method for logging. 278 * The calls to log() must be guarded with "if (Email.LOGD)" for performance reasons. 279 */ log(String message)280 public static void log(String message) { 281 Log.d(LOG_TAG, message); 282 } 283 284 /** 285 * Update the time when the mailbox is refreshed 286 * @param mailboxId mailbox which need to be updated 287 */ updateMailboxRefreshTime(long mailboxId)288 public static void updateMailboxRefreshTime(long mailboxId) { 289 synchronized (sMailboxSyncTimes) { 290 sMailboxSyncTimes.put(mailboxId, System.currentTimeMillis()); 291 } 292 } 293 294 /** 295 * Check if the mailbox is need to be refreshed 296 * @param mailboxId mailbox checked the need of refreshing 297 * @return the need of refreshing 298 */ mailboxRequiresRefresh(long mailboxId)299 public static boolean mailboxRequiresRefresh(long mailboxId) { 300 synchronized (sMailboxSyncTimes) { 301 return 302 !sMailboxSyncTimes.containsKey(mailboxId) 303 || (System.currentTimeMillis() - sMailboxSyncTimes.get(mailboxId) 304 > UPDATE_INTERVAL); 305 } 306 } 307 308 /** 309 * Called by the accounts reconciler to notify that accounts have changed, or by "Welcome" 310 * to clear the flag. 311 * @param setFlag true to set the notification flag, false to clear it 312 */ setNotifyUiAccountsChanged(boolean setFlag)313 public static synchronized void setNotifyUiAccountsChanged(boolean setFlag) { 314 sAccountsChangedNotification = setFlag; 315 } 316 317 /** 318 * Called from activity onResume() functions to check for an accounts-changed condition, at 319 * which point they should finish() and jump to the Welcome activity. 320 */ getNotifyUiAccountsChanged()321 public static synchronized boolean getNotifyUiAccountsChanged() { 322 return sAccountsChangedNotification; 323 } 324 } 325