MailService.java revision 96c5af40d639d629267794f4f0338a267ff94ce5
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.service;
18
19import java.util.ArrayList;
20import java.util.HashMap;
21
22import android.app.AlarmManager;
23import android.app.Notification;
24import android.app.NotificationManager;
25import android.app.PendingIntent;
26import android.app.Service;
27import android.content.Context;
28import android.content.Intent;
29import android.net.Uri;
30import android.os.IBinder;
31import android.os.SystemClock;
32import android.text.TextUtils;
33import android.util.Config;
34import android.util.Log;
35
36import com.android.email.Account;
37import com.android.email.Email;
38import com.android.email.MessagingController;
39import com.android.email.MessagingListener;
40import com.android.email.Preferences;
41import com.android.email.R;
42import com.android.email.activity.Accounts;
43import com.android.email.activity.FolderMessageList;
44
45/**
46 */
47public class MailService extends Service {
48    private static final String ACTION_CHECK_MAIL = "com.android.email.intent.action.MAIL_SERVICE_WAKEUP";
49    private static final String ACTION_RESCHEDULE = "com.android.email.intent.action.MAIL_SERVICE_RESCHEDULE";
50    private static final String ACTION_CANCEL = "com.android.email.intent.action.MAIL_SERVICE_CANCEL";
51
52    private Listener mListener = new Listener();
53
54    private int mStartId;
55
56    public static void actionReschedule(Context context) {
57        Intent i = new Intent();
58        i.setClass(context, MailService.class);
59        i.setAction(MailService.ACTION_RESCHEDULE);
60        context.startService(i);
61    }
62
63    public static void actionCancel(Context context)  {
64        Intent i = new Intent();
65        i.setClass(context, MailService.class);
66        i.setAction(MailService.ACTION_CANCEL);
67        context.startService(i);
68    }
69
70    @Override
71    public void onStart(Intent intent, int startId) {
72        super.onStart(intent, startId);
73        this.mStartId = startId;
74
75        MessagingController controller = MessagingController.getInstance(getApplication());
76        controller.addListener(mListener);
77        if (ACTION_CHECK_MAIL.equals(intent.getAction())) {
78            if (Config.LOGD && Email.DEBUG) {
79                Log.d(Email.LOG_TAG, "*** MailService: checking mail");
80            }
81            // Only check mail for accounts that have enabled automatic checking.  There is still
82            // a bug here in that we check every enabled account, on every refresh - irrespective
83            // of that account's refresh frequency - but this fixes the worst case of checking
84            // accounts that should not have been checked at all.
85            // Also note:  Due to the organization of this service, you must gather the accounts
86            // and make a single call to controller.checkMail().
87            ArrayList<Account> accountsToCheck = new ArrayList<Account>();
88            for (Account account : Preferences.getPreferences(this).getAccounts()) {
89                if (account.getAutomaticCheckIntervalMinutes() != -1) {
90                    accountsToCheck.add(account);
91                }
92            }
93            Account[] accounts = accountsToCheck.toArray(new Account[accountsToCheck.size()]);
94            controller.checkMail(this, accounts, mListener);
95        }
96        else if (ACTION_CANCEL.equals(intent.getAction())) {
97            if (Config.LOGD && Email.DEBUG) {
98                Log.d(Email.LOG_TAG, "*** MailService: cancel");
99            }
100            cancel();
101            stopSelf(startId);
102        }
103        else if (ACTION_RESCHEDULE.equals(intent.getAction())) {
104            if (Config.LOGD && Email.DEBUG) {
105                Log.d(Email.LOG_TAG, "*** MailService: reschedule");
106            }
107            reschedule();
108            stopSelf(startId);
109        }
110    }
111
112    @Override
113    public void onDestroy() {
114        super.onDestroy();
115        MessagingController.getInstance(getApplication()).removeListener(mListener);
116    }
117
118    private void cancel() {
119        AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
120        Intent i = new Intent();
121        i.setClassName("com.android.email", "com.android.email.service.MailService");
122        i.setAction(ACTION_CHECK_MAIL);
123        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
124        alarmMgr.cancel(pi);
125    }
126
127    private void reschedule() {
128        AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
129        Intent i = new Intent();
130        i.setClassName("com.android.email", "com.android.email.service.MailService");
131        i.setAction(ACTION_CHECK_MAIL);
132        PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
133
134        int shortestInterval = -1;
135        for (Account account : Preferences.getPreferences(this).getAccounts()) {
136            if (account.getAutomaticCheckIntervalMinutes() != -1
137                    && (account.getAutomaticCheckIntervalMinutes() < shortestInterval || shortestInterval == -1)) {
138                shortestInterval = account.getAutomaticCheckIntervalMinutes();
139            }
140        }
141
142        if (shortestInterval == -1) {
143            alarmMgr.cancel(pi);
144        }
145        else {
146            alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
147                    + (shortestInterval * (60 * 1000)), pi);
148        }
149    }
150
151    public IBinder onBind(Intent intent) {
152        return null;
153    }
154
155    class Listener extends MessagingListener {
156        HashMap<Account, Integer> accountsWithNewMail = new HashMap<Account, Integer>();
157
158        // TODO this should be redone because account is usually null, not very interesting.
159        // I think it would make more sense to pass Account[] here in case anyone uses it
160        // In any case, it should be noticed that this is called once per cycle
161        @Override
162        public void checkMailStarted(Context context, Account account) {
163            accountsWithNewMail.clear();
164        }
165
166        // Called once per checked account
167        @Override
168        public void checkMailFailed(Context context, Account account, String reason) {
169            if (Config.LOGD && Email.DEBUG) {
170                Log.d(Email.LOG_TAG, "*** MailService: checkMailFailed: " + reason);
171            }
172            reschedule();
173            stopSelf(mStartId);
174        }
175
176        // Called once per checked account
177        @Override
178        public void synchronizeMailboxFinished(
179                Account account,
180                String folder,
181                int totalMessagesInMailbox,
182                int numNewMessages) {
183            if (Config.LOGD && Email.DEBUG) {
184                Log.d(Email.LOG_TAG, "*** MailService: synchronizeMailboxFinished: total=" +
185                        totalMessagesInMailbox + " new=" + numNewMessages);
186            }
187            if (account.isNotifyNewMail() && numNewMessages > 0) {
188                accountsWithNewMail.put(account, numNewMessages);
189            }
190        }
191
192        // TODO this should be redone because account is usually null, not very interesting.
193        // I think it would make more sense to pass Account[] here in case anyone uses it
194        // In any case, it should be noticed that this is called once per cycle
195        @Override
196        public void checkMailFinished(Context context, Account account) {
197            if (Config.LOGD && Email.DEBUG) {
198                Log.d(Email.LOG_TAG, "*** MailService: checkMailFinished");
199            }
200            NotificationManager notifMgr = (NotificationManager)context
201                    .getSystemService(Context.NOTIFICATION_SERVICE);
202
203            if (accountsWithNewMail.size() > 0) {
204                Notification notif = new Notification(R.drawable.stat_notify_email_generic,
205                        getString(R.string.notification_new_title), System.currentTimeMillis());
206                boolean vibrate = false;
207                String ringtone = null;
208                if (accountsWithNewMail.size() > 1) {
209                    for (Account account1 : accountsWithNewMail.keySet()) {
210                        if (account1.isVibrate()) vibrate = true;
211                        ringtone = account1.getRingtone();
212                    }
213                    Intent i = new Intent(context, Accounts.class);
214                    PendingIntent pi = PendingIntent.getActivity(context, 0, i, 0);
215                    notif.setLatestEventInfo(context, getString(R.string.notification_new_title),
216                            getString(R.string.notification_new_multi_account_fmt,
217                                    accountsWithNewMail.size()), pi);
218                } else {
219                    Account account1 = accountsWithNewMail.keySet().iterator().next();
220                    int totalNewMails = accountsWithNewMail.get(account1);
221                    Intent i = FolderMessageList.actionHandleAccountIntent(context, account1, Email.INBOX);
222                    PendingIntent pi = PendingIntent.getActivity(context, 0, i, 0);
223                    notif.setLatestEventInfo(context, getString(R.string.notification_new_title),
224                            getString(R.string.notification_new_one_account_fmt, totalNewMails,
225                                    account1.getDescription()), pi);
226                    vibrate = account1.isVibrate();
227                    ringtone = account1.getRingtone();
228                }
229                notif.defaults = Notification.DEFAULT_LIGHTS;
230                notif.sound = TextUtils.isEmpty(ringtone) ? null : Uri.parse(ringtone);
231                if (vibrate) {
232                    notif.defaults |= Notification.DEFAULT_VIBRATE;
233                }
234                notifMgr.notify(1, notif);
235            }
236
237            reschedule();
238            stopSelf(mStartId);
239        }
240    }
241}
242