AlertService.java revision 146de36083f6ce8b7e8a1f974d3990594a36bfec
1146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/*
2146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
3146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *
4146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * you may not use this file except in compliance with the License.
6146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * You may obtain a copy of the License at
7146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *
8146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *
10146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * See the License for the specific language governing permissions and
14146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * limitations under the License.
15146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */
16146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
17146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpackage com.android.calendar;
18146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
19146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.AlarmManager;
20146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Notification;
21146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.NotificationManager;
22146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.app.Service;
23146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentResolver;
24146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentValues;
25146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context;
26146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Intent;
27146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.SharedPreferences;
28146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.database.Cursor;
29146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri;
30146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Bundle;
31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Handler;
32146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.HandlerThread;
33146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.IBinder;
34146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Looper;
35146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Message;
36146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Process;
37146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.preference.PreferenceManager;
38146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar;
39146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar.Attendees;
40146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar.CalendarAlerts;
41146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar.Instances;
42146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.provider.Calendar.Reminders;
43146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.TextUtils;
44146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateUtils;
45146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.Time;
46146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.util.Log;
47146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.LayoutInflater;
48146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.View;
49146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
50146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/**
51146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * This service is used to handle calendar event reminders.
52146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */
53146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpublic class AlertService extends Service {
54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String TAG = "AlertService";
55146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
56146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private volatile Looper mServiceLooper;
57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private volatile ServiceHandler mServiceHandler;
58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] ALERT_PROJECTION = new String[] {
60146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts._ID,                     // 0
61146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.EVENT_ID,                // 1
62146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.STATE,                   // 2
63146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.TITLE,                   // 3
64146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.EVENT_LOCATION,          // 4
65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.SELF_ATTENDEE_STATUS,    // 5
66146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.ALL_DAY,                 // 6
67146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.ALARM_TIME,              // 7
68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.MINUTES,                 // 8
69146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.BEGIN,                   // 9
70146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
71146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
72146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // We just need a simple projection that returns any column
73146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] ALERT_PROJECTION_SMALL = new String[] {
74146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts._ID,                     // 0
75146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
76146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
77146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_ID = 0;
78146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_EVENT_ID = 1;
79146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_STATE = 2;
80146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_TITLE = 3;
81146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_EVENT_LOCATION = 4;
82146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5;
83146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_ALL_DAY = 6;
84146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_ALARM_TIME = 7;
85146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_MINUTES = 8;
86146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ALERT_INDEX_BEGIN = 9;
87146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
88146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] INSTANCE_PROJECTION = { Instances.BEGIN, Instances.END };
89146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int INSTANCES_INDEX_BEGIN = 0;
90146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int INSTANCES_INDEX_END = 1;
91146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
92146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // We just need a simple projection that returns any column
93146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] REMINDER_PROJECTION_SMALL = new String[] {
94146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Reminders._ID,                     // 0
95146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
96146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
97146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void processMessage(Message msg) {
98146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Bundle bundle = (Bundle) msg.obj;
99146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // On reboot, update the notification bar with the contents of the
101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // CalendarAlerts table.
102146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String action = bundle.getString("action");
103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (action.equals(Intent.ACTION_BOOT_COMPLETED)
104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                || action.equals(Intent.ACTION_TIME_CHANGED)) {
105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            doTimeChanged();
106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // The Uri specifies an entry in the CalendarAlerts table
110146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Uri alertUri = Uri.parse(bundle.getString("uri"));
111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (Log.isLoggable(TAG, Log.DEBUG)) {
112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Log.d(TAG, "uri: " + alertUri);
113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ContentResolver cr = getContentResolver();
116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Cursor alertCursor = cr.query(alertUri, ALERT_PROJECTION,
117146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selection */, null, null /* sort order */);
118146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
119146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long alertId, eventId, instanceId, alarmTime;
120146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int minutes;
121146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String eventName;
122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String location;
123146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean allDay;
124146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean declined = false;
125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        try {
126146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (alertCursor == null || !alertCursor.moveToFirst()) {
127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // This can happen if the event was deleted.
128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (Log.isLoggable(TAG, Log.DEBUG)) {
129146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    Log.d(TAG, "alert not found");
130146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
131146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
132146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
133146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertId = alertCursor.getLong(ALERT_INDEX_ID);
134146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            eventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID);
135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            minutes = alertCursor.getInt(ALERT_INDEX_MINUTES);
136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            eventName = alertCursor.getString(ALERT_INDEX_TITLE);
137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            location = alertCursor.getString(ALERT_INDEX_EVENT_LOCATION);
138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            allDay = alertCursor.getInt(ALERT_INDEX_ALL_DAY) != 0;
139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alarmTime = alertCursor.getLong(ALERT_INDEX_ALARM_TIME);
140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            declined = alertCursor.getInt(ALERT_INDEX_SELF_ATTENDEE_STATUS) ==
141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    Attendees.ATTENDEE_STATUS_DECLINED;
142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If the event was declined, then mark the alarm DISMISSED,
144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // otherwise, mark the alarm FIRED.
145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int newState = CalendarAlerts.FIRED;
146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (declined) {
147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                newState = CalendarAlerts.DISMISSED;
148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertCursor.updateInt(ALERT_INDEX_STATE, newState);
150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertCursor.commitUpdates();
151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } finally {
152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (alertCursor != null) {
153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                alertCursor.close();
154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Do not show an alert if the event was declined
158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (declined) {
159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (Log.isLoggable(TAG, Log.DEBUG)) {
160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Log.d(TAG, "event declined, alert cancelled");
161146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long beginTime = bundle.getLong(Calendar.EVENT_BEGIN_TIME, 0);
166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long endTime = bundle.getLong(Calendar.EVENT_END_TIME, 0);
167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Check if this alarm is still valid.  The time of the event may
169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // have been changed, or the reminder may have been changed since
170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // this alarm was set. First, search for an instance in the Instances
171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // that has the same event id and the same begin and end time.
172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Then check for a reminder in the Reminders table to ensure that
173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // the reminder minutes is consistent with this alarm.
174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String selection = Instances.EVENT_ID + "=" + eventId;
175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Cursor instanceCursor = Instances.query(cr, INSTANCE_PROJECTION,
176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                beginTime, endTime, selection, Instances.DEFAULT_SORT_ORDER);
177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long instanceBegin = 0, instanceEnd = 0;
178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        try {
179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (instanceCursor == null || !instanceCursor.moveToFirst()) {
180146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Delete this alarm from the CalendarAlerts table
181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                cr.delete(alertUri, null /* selection */, null /* selection args */);
182146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (Log.isLoggable(TAG, Log.DEBUG)) {
183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    Log.d(TAG, "instance not found, alert cancelled");
184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            instanceBegin = instanceCursor.getLong(INSTANCES_INDEX_BEGIN);
188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            instanceEnd = instanceCursor.getLong(INSTANCES_INDEX_END);
189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } finally {
190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (instanceCursor != null) {
191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                instanceCursor.close();
192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Check that a reminder for this event exists with the same number
196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // of minutes.  But snoozed alarms have minutes = 0, so don't do this
197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // check for snoozed alarms.
198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (minutes > 0) {
199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            selection = Reminders.EVENT_ID + "=" + eventId
200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                + " AND " + Reminders.MINUTES + "=" + minutes;
201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Cursor reminderCursor = cr.query(Reminders.CONTENT_URI, REMINDER_PROJECTION_SMALL,
202146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    selection, null /* selection args */, null /* sort order */);
203146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            try {
204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (reminderCursor == null || reminderCursor.getCount() == 0) {
205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Delete this alarm from the CalendarAlerts table
206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    cr.delete(alertUri, null /* selection */, null /* selection args */);
207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (Log.isLoggable(TAG, Log.DEBUG)) {
208146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        Log.d(TAG, "reminder not found, alert cancelled");
209146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
210146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    return;
211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } finally {
213146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (reminderCursor != null) {
214146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    reminderCursor.close();
215146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
217146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the event time was changed and the event has already ended,
220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // then don't sound the alarm.
221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (alarmTime > instanceEnd) {
222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Delete this alarm from the CalendarAlerts table
223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cr.delete(alertUri, null /* selection */, null /* selection args */);
224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (Log.isLoggable(TAG, Log.DEBUG)) {
225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Log.d(TAG, "event ended, alert cancelled");
226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
230146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If minutes > 0, then this is a normal alarm (not a snoozed alarm)
231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // so check for duplicate alarms.  A duplicate alarm can occur when
232146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // the start time of an event is changed to an earlier time.  The
233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // later alarm (that was first scheduled for the later event time)
234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // should be discarded.
235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long computedAlarmTime = instanceBegin - minutes * DateUtils.MINUTE_IN_MILLIS;
236146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (minutes > 0 && computedAlarmTime != alarmTime) {
237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If the event time was changed to a later time, then the computed
238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // alarm time is in the future and we shouldn't sound this alarm.
239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (computedAlarmTime > alarmTime) {
240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Delete this alarm from the CalendarAlerts table
241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                cr.delete(alertUri, null /* selection */, null /* selection args */);
242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (Log.isLoggable(TAG, Log.DEBUG)) {
243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    Log.d(TAG, "event postponed, alert cancelled");
244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
246146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check for another alarm in the CalendarAlerts table that has the
249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // same event id and the same "minutes".  This can occur
250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // if the event start time was changed to an earlier time and the
251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // alarm for the later time goes off.  To avoid discarding alarms
252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // for repeating events (that have the same event id), we check
253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // that the other alarm fired recently (within an hour of this one).
254146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            long recently = alarmTime - 60 * DateUtils.MINUTE_IN_MILLIS;
255146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            selection = CalendarAlerts.EVENT_ID + "=" + eventId
256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " AND " + CalendarAlerts.TABLE_NAME + "." + CalendarAlerts._ID
257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + "!=" + alertId
258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " AND " + CalendarAlerts.MINUTES + "=" + minutes
259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " AND " + CalendarAlerts.ALARM_TIME + ">" + recently
260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " AND " + CalendarAlerts.ALARM_TIME + "<=" + alarmTime;
261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertCursor = CalendarAlerts.query(cr, ALERT_PROJECTION_SMALL, selection, null);
262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (alertCursor != null) {
263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                try {
264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (alertCursor.getCount() > 0) {
265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Delete this alarm from the CalendarAlerts table
266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        cr.delete(alertUri, null /* selection */, null /* selection args */);
267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (Log.isLoggable(TAG, Log.DEBUG)) {
268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            Log.d(TAG, "duplicate alarm, alert cancelled");
269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        return;
271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
272146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } finally {
273146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    alertCursor.close();
274146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Find all the alerts that have fired but have not been dismissed
279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selection = CalendarAlerts.STATE + "=" + CalendarAlerts.FIRED;
280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        alertCursor = CalendarAlerts.query(cr, ALERT_PROJECTION, selection, null);
281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (alertCursor == null || alertCursor.getCount() == 0) {
283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (Log.isLoggable(TAG, Log.DEBUG)) {
284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Log.d(TAG, "no fired alarms found");
285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numReminders = alertCursor.getCount();
290146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        try {
291146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            while (alertCursor.moveToNext()) {
292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long otherEventId = alertCursor.getLong(ALERT_INDEX_EVENT_ID);
293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long otherAlertId = alertCursor.getLong(ALERT_INDEX_ID);
294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int otherAlarmState = alertCursor.getInt(ALERT_INDEX_STATE);
295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long otherBeginTime = alertCursor.getLong(ALERT_INDEX_BEGIN);
296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (otherEventId == eventId && otherAlertId != alertId
297146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        && otherAlarmState == CalendarAlerts.FIRED
298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        && otherBeginTime == beginTime) {
299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This event already has an alert that fired and has not
300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // been dismissed.  This can happen if an event has
301146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // multiple reminders.  Do not count this as a separate
302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // reminder.  But we do want to sound the alarm and vibrate
303146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // the phone, if necessary.
304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (Log.isLoggable(TAG, Log.DEBUG)) {
305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        Log.d(TAG, "multiple alarms for this event");
306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    numReminders -= 1;
308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } finally {
311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertCursor.close();
312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (Log.isLoggable(TAG, Log.DEBUG)) {
315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Log.d(TAG, "creating new alarm notification, numReminders: " + numReminders);
316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Notification notification = AlertReceiver.makeNewAlertNotification(this, eventName,
318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                location, numReminders);
319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Generate either a pop-up dialog, status bar notification, or
321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // neither. Pop-up dialog and status bar notification may include a
322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // sound, an alert, or both. A status bar notification also includes
323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // a toast.
324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
325146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String reminderType = prefs.getString(CalendarPreferenceActivity.KEY_ALERTS_TYPE,
326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                CalendarPreferenceActivity.ALERT_TYPE_STATUS_BAR);
327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_OFF)) {
329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (Log.isLoggable(TAG, Log.DEBUG)) {
330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Log.d(TAG, "alert preference is OFF");
331146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
333146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        NotificationManager nm =
336146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean reminderVibrate =
338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prefs.getBoolean(CalendarPreferenceActivity.KEY_ALERTS_VIBRATE, false);
339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String reminderRingtone =
340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prefs.getString(CalendarPreferenceActivity.KEY_ALERTS_RINGTONE, null);
341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Possibly generate a vibration
343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (reminderVibrate) {
344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            notification.defaults |= Notification.DEFAULT_VIBRATE;
345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Possibly generate a sound.  If 'Silent' is chosen, the ringtone string will be empty.
348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        notification.sound = TextUtils.isEmpty(reminderRingtone) ? null : Uri
349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                .parse(reminderRingtone);
350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (reminderType.equals(CalendarPreferenceActivity.ALERT_TYPE_ALERTS)) {
352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Intent alertIntent = new Intent();
353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertIntent.setClass(this, AlertActivity.class);
354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            alertIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            startActivity(alertIntent);
356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            LayoutInflater inflater;
358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            View view = inflater.inflate(R.layout.alert_toast, null);
360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
361146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            AlertAdapter.updateView(this, view, eventName, location, beginTime, endTime, allDay);
362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Record the notify time in the CalendarAlerts table.
365146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // This is used for debugging missed alarms.
366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ContentValues values = new ContentValues();
367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long currentTime = System.currentTimeMillis();
368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        values.put(CalendarAlerts.NOTIFY_TIME, currentTime);
369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cr.update(alertUri, values, null /* where */, null /* args */);
370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // The notification time should be pretty close to the reminder time
372146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // that the user set for this event.  If the notification is late, then
373146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // that's a bug and we should log an error.
374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (currentTime > alarmTime + DateUtils.MINUTE_IN_MILLIS) {
375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            long minutesLate = (currentTime - alarmTime) / DateUtils.MINUTE_IN_MILLIS;
376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int flags = DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_TIME;
377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            String alarmTimeStr = DateUtils.formatDateTime(this, alarmTime, flags);
378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            String currentTimeStr = DateUtils.formatDateTime(this, currentTime, flags);
379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Log.w(TAG, "Calendar reminder alarm for event id " + eventId
380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " is " + minutesLate + " minute(s) late;"
381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " expected alarm at: " + alarmTimeStr
382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + " but got it at: " + currentTimeStr);
383146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        nm.notify(0, notification);
386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void doTimeChanged() {
389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ContentResolver cr = getContentResolver();
390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Object service = getSystemService(Context.ALARM_SERVICE);
391146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        AlarmManager manager = (AlarmManager) service;
392146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        CalendarAlerts.rescheduleMissedAlarms(cr, this, manager);
393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        AlertReceiver.updateAlertNotification(this);
394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
395146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
396146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private final class ServiceHandler extends Handler {
397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public ServiceHandler(Looper looper) {
398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            super(looper);
399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
400146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
401146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        @Override
402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void handleMessage(Message msg) {
403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            processMessage(msg);
404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // NOTE: We MUST not call stopSelf() directly, since we need to
405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // make sure the wake lock acquired by AlertReceiver is released.
406146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            AlertReceiver.finishStartingService(AlertService.this, msg.arg1);
407146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
409146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onCreate() {
412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        HandlerThread thread = new HandlerThread("AlertService",
413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Process.THREAD_PRIORITY_BACKGROUND);
414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        thread.start();
415146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mServiceLooper = thread.getLooper();
417146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mServiceHandler = new ServiceHandler(mServiceLooper);
418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
420146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
421146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onStart(Intent intent, int startId) {
422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Message msg = mServiceHandler.obtainMessage();
423146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        msg.arg1 = startId;
424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        msg.obj = intent.getExtras();
425146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mServiceHandler.sendMessage(msg);
426146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onDestroy() {
430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mServiceLooper.quit();
431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public IBinder onBind(Intent intent) {
435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return null;
436146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
437146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project}
438