Email.java revision 5b81690de1ea15035ab0539df683acd8e28b0ebd
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.MessageCompose;
21import com.android.email.provider.EmailContent;
22import com.android.email.service.AttachmentDownloadService;
23import com.android.email.service.MailService;
24import com.android.exchange.Eas;
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     * If true, enable the UI thread check when accessing the filesystem.
74     */
75    public static final boolean DEBUG_THREAD_CHECK = false; // DON'T SUBMIT WITH TRUE
76
77    /**
78     * If true, inhibit hardware graphics acceleration in UI (for a/b testing)
79     */
80    public static boolean sDebugInhibitGraphicsAcceleration = false;
81
82    /**
83     * The MIME type(s) of attachments we're willing to send via attachments.
84     *
85     * Any attachments may be added via Intents with Intent.ACTION_SEND or ACTION_SEND_MULTIPLE.
86     */
87    public static final String[] ACCEPTABLE_ATTACHMENT_SEND_INTENT_TYPES = new String[] {
88        "*/*",
89    };
90
91    /**
92     * The MIME type(s) of attachments we're willing to send from the internal UI.
93     *
94     * NOTE:  At the moment it is not possible to open a chooser with a list of filter types, so
95     * the chooser is only opened with the first item in the list.
96     */
97    public static final String[] ACCEPTABLE_ATTACHMENT_SEND_UI_TYPES = new String[] {
98        "image/*",
99        "video/*",
100    };
101
102    /**
103     * The MIME type(s) of attachments we're willing to view.
104     */
105    public static final String[] ACCEPTABLE_ATTACHMENT_VIEW_TYPES = new String[] {
106        "*/*",
107    };
108
109    /**
110     * The MIME type(s) of attachments we're not willing to view.
111     */
112    public static final String[] UNACCEPTABLE_ATTACHMENT_VIEW_TYPES = new String[] {
113    };
114
115    /**
116     * The MIME type(s) of attachments we're willing to download to SD.
117     */
118    public static final String[] ACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
119        "image/*",
120    };
121
122    /**
123     * The MIME type(s) of attachments we're not willing to download to SD.
124     */
125    public static final String[] UNACCEPTABLE_ATTACHMENT_DOWNLOAD_TYPES = new String[] {
126    };
127
128    /**
129     * Specifies how many messages will be shown in a folder by default. This number is set
130     * on each new folder and can be incremented with "Load more messages..." by the
131     * VISIBLE_LIMIT_INCREMENT
132     */
133    public static final int VISIBLE_LIMIT_DEFAULT = 25;
134
135    /**
136     * Number of additional messages to load when a user selects "Load more messages..."
137     */
138    public static final int VISIBLE_LIMIT_INCREMENT = 25;
139
140    /**
141     * The maximum size of an attachment we're willing to download (either View or Save)
142     * Attachments that are base64 encoded (most) will be about 1.375x their actual size
143     * so we should probably factor that in. A 5MB attachment will generally be around
144     * 6.8MB downloaded but only 5MB saved.
145     */
146    public static final int MAX_ATTACHMENT_DOWNLOAD_SIZE = (5 * 1024 * 1024);
147
148    /**
149     * The maximum size of an attachment we're willing to upload (measured as stored on disk).
150     * Attachments that are base64 encoded (most) will be about 1.375x their actual size
151     * so we should probably factor that in. A 5MB attachment will generally be around
152     * 6.8MB uploaded.
153     */
154    public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024);
155
156    /**
157     * This is used to force stacked UI to return to the "welcome" screen any time we change
158     * the accounts list (e.g. deleting accounts in the Account Manager preferences.)
159     */
160    private static boolean sAccountsChangedNotification = false;
161
162    public static final String EXCHANGE_ACCOUNT_MANAGER_TYPE = "com.android.exchange";
163    public static final String POP_IMAP_ACCOUNT_MANAGER_TYPE = "com.android.email";
164
165    private static File sTempDirectory;
166
167    private static Thread sUiThread;
168
169    public static void setTempDirectory(Context context) {
170        sTempDirectory = context.getCacheDir();
171    }
172
173    public static File getTempDirectory() {
174        if (sTempDirectory == null) {
175            throw new RuntimeException(
176                    "TempDirectory not set.  " +
177                    "If in a unit test, call Email.setTempDirectory(context) in setUp().");
178        }
179        return sTempDirectory;
180    }
181
182    /**
183     * Called throughout the application when the number of accounts has changed. This method
184     * enables or disables the Compose activity, the boot receiver and the service based on
185     * whether any accounts are configured.   Returns true if there are any accounts configured.
186     */
187    public static boolean setServicesEnabled(Context context) {
188        Cursor c = null;
189        try {
190            c = context.getContentResolver().query(
191                    EmailContent.Account.CONTENT_URI,
192                    EmailContent.Account.ID_PROJECTION,
193                    null, null, null);
194            boolean enable = c.getCount() > 0;
195            setServicesEnabled(context, enable);
196            return enable;
197        } finally {
198            if (c != null) {
199                c.close();
200            }
201        }
202    }
203
204    public static void setServicesEnabled(Context context, boolean enabled) {
205        PackageManager pm = context.getPackageManager();
206        if (!enabled && pm.getComponentEnabledSetting(
207                new ComponentName(context, MailService.class)) ==
208                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
209            /*
210             * If no accounts now exist but the service is still enabled we're about to disable it
211             * so we'll reschedule to kill off any existing alarms.
212             */
213            MailService.actionReschedule(context);
214        }
215        pm.setComponentEnabledSetting(
216                new ComponentName(context, MessageCompose.class),
217                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
218                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
219                PackageManager.DONT_KILL_APP);
220        pm.setComponentEnabledSetting(
221                new ComponentName(context, AccountShortcutPicker.class),
222                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
223                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
224                PackageManager.DONT_KILL_APP);
225        pm.setComponentEnabledSetting(
226                new ComponentName(context, MailService.class),
227                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
228                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
229                PackageManager.DONT_KILL_APP);
230        pm.setComponentEnabledSetting(
231                new ComponentName(context, AttachmentDownloadService.class),
232                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
233                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
234                PackageManager.DONT_KILL_APP);
235        if (enabled && pm.getComponentEnabledSetting(
236                new ComponentName(context, MailService.class)) ==
237                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
238            /*
239             * And now if accounts do exist then we've just enabled the service and we want to
240             * schedule alarms for the new accounts.
241             */
242            MailService.actionReschedule(context);
243        }
244        // Start/stop the AttachmentDownloadService, depending on whether there are any accounts
245        Intent intent = new Intent(context, AttachmentDownloadService.class);
246        if (enabled) {
247            context.startService(intent);
248        } else {
249            context.stopService(intent);
250        }
251    }
252
253    @Override
254    public void onCreate() {
255        super.onCreate();
256        sUiThread = Thread.currentThread();
257        Preferences prefs = Preferences.getPreferences(this);
258        DEBUG = prefs.getEnableDebugLogging();
259        sDebugInhibitGraphicsAcceleration = prefs.getInhibitGraphicsAcceleration();
260        setTempDirectory(this);
261
262        // Tie MailRefreshManager to the Controller.
263        RefreshManager.getInstance(this);
264        // Reset all accounts to default visible window
265        Controller.getInstance(this).resetVisibleLimits();
266
267        // Enable logging in the EAS service, so it starts up as early as possible.
268        updateLoggingFlags(this);
269    }
270
271    /**
272     * Load enabled debug flags from the preferences and update the EAS debug flag.
273     */
274    public static void updateLoggingFlags(Context context) {
275        //EXCHANGE-REMOVE-SECTION-START
276        Preferences prefs = Preferences.getPreferences(context);
277        int debugLogging = prefs.getEnableDebugLogging() ? Eas.DEBUG_BIT : 0;
278        int exchangeLogging = prefs.getEnableExchangeLogging() ? Eas.DEBUG_EXCHANGE_BIT : 0;
279        int fileLogging = prefs.getEnableExchangeFileLogging() ? Eas.DEBUG_FILE_BIT : 0;
280        int debugBits = debugLogging | exchangeLogging | fileLogging;
281        Controller.getInstance(context).serviceLogging(debugBits);
282        //EXCHANGE-REMOVE-SECTION-END
283    }
284
285    /**
286     * Internal, utility method for logging.
287     * The calls to log() must be guarded with "if (Email.LOGD)" for performance reasons.
288     */
289    public static void log(String message) {
290        Log.d(LOG_TAG, message);
291    }
292
293    /**
294     * Called by the accounts reconciler to notify that accounts have changed, or by  "Welcome"
295     * to clear the flag.
296     * @param setFlag true to set the notification flag, false to clear it
297     */
298    public static synchronized void setNotifyUiAccountsChanged(boolean setFlag) {
299        sAccountsChangedNotification = setFlag;
300    }
301
302    /**
303     * Called from activity onResume() functions to check for an accounts-changed condition, at
304     * which point they should finish() and jump to the Welcome activity.
305     */
306    public static synchronized boolean getNotifyUiAccountsChanged() {
307        return sAccountsChangedNotification;
308    }
309
310    public static void warnIfUiThread() {
311        if (Thread.currentThread().equals(sUiThread)) {
312            Log.w(Email.LOG_TAG, "Method called on the UI thread", new Exception("STACK TRACE"));
313        }
314    }
315}
316