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