Create a new notification service

We can remove the preferences stuff 'cuz the service "should be" longer
living. And, even if the service is terminated (either by the user or by
the system) we'll receive a new notification when the service comes back.
This is probably desired behaviour anyway.

Change-Id: I4850a9473401536e8fb20385b780d4736ce80a8e
This commit is contained in:
Todd Kennedy 2011-05-10 17:22:08 -07:00
parent 83693a6aca
commit 71bd208ddd
7 changed files with 68 additions and 240 deletions

View File

@ -339,6 +339,12 @@
>
</service>
<service
android:name=".service.NotificationService"
android:enabled="false"
>
</service>
<!--Required stanza to register the PopImapAuthenticatorService with AccountManager -->
<service
android:name=".service.PopImapAuthenticatorService"

View File

@ -20,10 +20,12 @@ import com.android.email.activity.AccountShortcutPicker;
import com.android.email.activity.MessageCompose;
import com.android.email.service.AttachmentDownloadService;
import com.android.email.service.MailService;
import com.android.email.service.NotificationService;
import com.android.emailcommon.Logging;
import com.android.emailcommon.TempDirectory;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility;
import android.app.Application;
@ -106,7 +108,7 @@ public class Email extends Application {
* @param context
*/
public static void setServicesEnabledAsync(final Context context) {
Utility.runAsync(new Runnable() {
EmailAsyncTask.runAsyncParallel(new Runnable() {
@Override
public void run() {
setServicesEnabledSync(context);
@ -172,6 +174,11 @@ public class Email extends Application {
enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(
new ComponentName(context, NotificationService.class),
enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
if (enabled && pm.getComponentEnabledSetting(
new ComponentName(context, MailService.class)) ==
PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
@ -181,8 +188,19 @@ public class Email extends Application {
*/
MailService.actionReschedule(context);
}
// Start/stop the AttachmentDownloadService, depending on whether there are any accounts
Intent intent = new Intent(context, AttachmentDownloadService.class);
// Start/stop the various services depending on whether there are any accounts
startOrStopService(enabled, context, new Intent(context, AttachmentDownloadService.class));
startOrStopService(enabled, context, new Intent(context, NotificationService.class));
}
/**
* Starts or stops the service as necessary.
* @param enabled If {@code true}, the service will be started. Otherwise, it will be stopped.
* @param context The context to manage the service with.
* @param intent The intent of the service to be managed.
*/
private static void startOrStopService(boolean enabled, Context context, Intent intent) {
if (enabled) {
context.startService(intent);
} else {

View File

@ -75,10 +75,6 @@ public class NotificationController {
Account.FLAGS + "&" + Account.FLAGS_NOTIFY_NEW_MAIL + " != 0";
/** special account ID for the new message notification APIs to specify "all accounts" */
private static final long ALL_ACCOUNTS = -1L;
/** Index into notification table returned from system preferences */
private static final int NOTIFIED_MESSAGE_ID_INDEX = 0;
/** Index into notification table returned from system preferences */
private static final int NOTIFIED_MESSAGE_COUNT_INDEX = 1;
private static NotificationThread sNewMessageThread;
private static Handler sNewMessageHandler;
@ -212,13 +208,6 @@ public class NotificationController {
ContentResolver resolver = mContext.getContentResolver();
HashMap<Long, long[]> table;
if (!watch) {
table = new HashMap<Long, long[]>();
for (Long key : mNotificationMap.keySet()) {
MessageData data = mNotificationMap.get(key);
table.put(key,
new long[] { data.mNotifiedMessageId, data.mNotifiedMessageCount });
}
Preferences.getPreferences(mContext).setMessageNotificationTable(table);
unregisterMessageNotification(ALL_ACCOUNTS);
// TODO cancel existing account observers
@ -231,18 +220,6 @@ public class NotificationController {
// otherwise, start new observers for all notified accounts
registerMessageNotification(ALL_ACCOUNTS);
// Need to load preferences _after_ starting the notifications. Otherwise, the
// notification map will not be built.
table = Preferences.getPreferences(mContext).getMessageNotificationTable();
for (Long key : table.keySet()) {
MessageData data = mNotificationMap.get(key);
if (data != null) {
long[] value = table.get(key);
data.mNotifiedMessageId = value[NOTIFIED_MESSAGE_ID_INDEX];
data.mNotifiedMessageCount = (int) value[NOTIFIED_MESSAGE_COUNT_INDEX];
}
}
// Loop through the observers and fire them once
for (MessageData data : mNotificationMap.values()) {
if (data.mObserver != null) {

View File

@ -23,7 +23,6 @@ import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;
import java.util.HashMap;
import java.util.UUID;
public class Preferences {
@ -45,7 +44,6 @@ public class Preferences {
private static final String AUTO_ADVANCE_DIRECTION = "autoAdvance";
private static final String TEXT_ZOOM = "textZoom";
private static final String BACKGROUND_ATTACHMENTS = "backgroundAttachments";
private static final String MESSAGE_NOTIFICATION_TABLE = "messageNotificationTable";
public static final int AUTO_ADVANCE_NEWER = 0;
public static final int AUTO_ADVANCE_OLDER = 1;
@ -251,55 +249,6 @@ public class Preferences {
mSharedPreferences.edit().putBoolean(BACKGROUND_ATTACHMENTS, allowed).apply();
}
public HashMap<Long, long[]> getMessageNotificationTable() {
HashMap<Long, long[]> table = new HashMap<Long, long[]>();
// The table is encoded as a string with the following format:
// K:V1,V2;K:V1,V2;...
// Where 'K' is the table key and 'V1' and 'V2' are the array values associated with the key
// Multiple key/value pairs are separated from one another by a ';'.
String preference = mSharedPreferences.getString(MESSAGE_NOTIFICATION_TABLE, "");
String[] entries = preference.split(";");
for (String entry : entries) {
try {
String hash[] = entry.split(":");
if (hash.length != 2) continue;
String stringValues[] = hash[1].split(",");
if (stringValues.length != 2) continue;
long key = Long.parseLong(hash[0]);
long[] value = new long[2];
value[0] = Long.parseLong(stringValues[0]);
value[1] = Long.parseLong(stringValues[1]);
table.put(key, value);
} catch (NumberFormatException e) {
Log.w(Logging.LOG_TAG, "notification table preference corrupt");
continue;
}
}
return table;
}
/**
* Sets the message notification table.
* @throws IllegalArgumentException if the given table is null or any of the value arrays do
* not have exactly 2 elements.
*/
public void setMessageNotificationTable(HashMap<Long, long[]> notificationTable) {
StringBuffer sb = new StringBuffer();
boolean first = true;
if (notificationTable == null) throw new IllegalArgumentException("table cannot be null");
for (Long key : notificationTable.keySet()) {
if (!first) {
sb.append(';');
}
long[] value = notificationTable.get(key);
if (value == null || value.length != 2) {
throw new IllegalArgumentException("value array must contain 2 elements");
}
sb.append(key).append(':').append(value[0]).append(',').append(value[1]);
first = false;
}
mSharedPreferences.edit().putString(MESSAGE_NOTIFICATION_TABLE, sb.toString()).apply();
}
public void save() {
}

View File

@ -212,7 +212,6 @@ public class MailService extends Service {
mContext = this;
final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
NotificationController.getInstance(mContext).watchForMessages(true);
if (ACTION_CHECK_MAIL.equals(action)) {
// DB access required to satisfy this intent, so offload from UI thread
@ -341,7 +340,6 @@ public class MailService extends Service {
@Override
public void onDestroy() {
super.onDestroy();
NotificationController.getInstance(mContext).watchForMessages(false);
Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.service;
import com.android.email.NotificationController;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class NotificationService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationController.getInstance(this).watchForMessages(true);
return START_STICKY;
}
@Override
public void onDestroy() {
NotificationController.getInstance(this).watchForMessages(false);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View File

@ -21,8 +21,6 @@ import android.net.Uri;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import java.util.HashMap;
/**
* This is a series of unit tests for the Preferences class.
*
@ -44,27 +42,6 @@ public class PreferencesUnitTests extends AndroidTestCase {
mPreferences = Preferences.getPreferences(mMockContext);
}
/** Just because this does exist anywhere else */
private void assertEquals(long[] expected, long[] actual) {
assertNotNull(actual);
assertEquals(expected.length, actual.length);
for (int i = expected.length - 1; i >= 0; i--) {
if (expected[i] != actual[i]) {
fail("expected array element[" + i + "]:<"
+ expected[i] + "> but was:<" + actual[i] + '>');
}
}
}
private void assertEquals(HashMap<Long, long[]> expected, HashMap<Long, long[]> actual) {
assertNotNull(actual);
assertEquals(expected.size(), actual.size());
for (Long key : expected.keySet()) {
assertTrue(actual.containsKey(key));
long[] expectedArray = expected.get(key);
long[] actualArray = actual.get(key);
assertEquals(expectedArray, actualArray);
}
}
/**
* Test the new getAccountByContentUri() API. This should return null if no
* accounts are configured, or the Uri doesn't match, and it should return a desired account
@ -97,142 +74,4 @@ public class PreferencesUnitTests extends AndroidTestCase {
lookup = mPreferences.getAccountByContentUri(testAccountUri);
assertNull(lookup);
}
public void testSetMessageNotificationTable() {
HashMap<Long, long[]> testTable = new HashMap<Long, long[]>();
String testString;
// One account
testTable.clear();
testTable.put(5L, new long[] { 2L, 3L });
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "value_unset").apply();
mPreferences.setMessageNotificationTable(testTable);
testString = mPreferences.mSharedPreferences.getString("messageNotificationTable", null);
assertEquals("5:2,3", testString);
// Multiple accounts
// NOTE: This assumes a very specific order in the hash map and is fragile; if the hash
// map is ever changed, this may break unexpectedly.
testTable.clear();
testTable.put(5L, new long[] { 2L, 3L });
testTable.put(3L, new long[] { 1L, 8L });
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "value_unset").apply();
mPreferences.setMessageNotificationTable(testTable);
testString = mPreferences.mSharedPreferences.getString("messageNotificationTable", null);
assertEquals("5:2,3;3:1,8", testString);
// Wrong number of elements in the array
// NOTE: This assumes a very specific order in the hash map and is fragile; if the hash
// map is ever changed, this may break unexpectedly.
testTable.clear();
testTable.put(5L, new long[] { 2L, 3L, 8L }); // too many
testTable.put(3L, new long[] { 1L, 8L });
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "value_unset_1").apply();
try {
mPreferences.setMessageNotificationTable(testTable);
fail("expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
}
testString = mPreferences.mSharedPreferences.getString("messageNotificationTable", null);
assertEquals("value_unset_1", testString);
testTable.clear();
testTable.put(5L, new long[] { 2L }); // too few
testTable.put(3L, new long[] { 1L, 8L });
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "value_unset_2").apply();
try {
mPreferences.setMessageNotificationTable(testTable);
fail("expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
}
testString = mPreferences.mSharedPreferences.getString("messageNotificationTable", null);
assertEquals("value_unset_2", testString);
// Nulls in strange places
testTable.clear();
testTable.put(5L, null); // no array
testTable.put(3L, new long[] { 1L, 8L });
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "value_unset_3").apply();
try {
mPreferences.setMessageNotificationTable(testTable);
fail("expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
}
testString = mPreferences.mSharedPreferences.getString("messageNotificationTable", null);
assertEquals("value_unset_3", testString);
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "value_unset_4").apply();
try {
mPreferences.setMessageNotificationTable(null); // no table
fail("expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
}
testString = mPreferences.mSharedPreferences.getString("messageNotificationTable", null);
assertEquals("value_unset_4", testString);
}
public void testGetMessageNotificationTable() {
HashMap<Long, long[]> testTable;
HashMap<Long, long[]> expectedTable = new HashMap<Long, long[]>();
// Test initial condition
assertFalse(mPreferences.mSharedPreferences.contains("messageNotificationTable"));
// One account
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "5:2,3").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
expectedTable.put(5L, new long[] { 2L, 3L });
assertEquals(expectedTable, testTable);
// Multiple accounts
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "5:2,3;3:1,8;6:5,3").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
expectedTable.put(5L, new long[] { 2L, 3L });
expectedTable.put(3L, new long[] { 1L, 8L });
expectedTable.put(6L, new long[] { 5L, 3L });
assertEquals(expectedTable, testTable);
// Empty account
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "5:2,3;").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
expectedTable.put(5L, new long[] { 2L, 3L });
assertEquals(expectedTable, testTable);
// Empty fields
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "5:2,3;3:").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
expectedTable.put(5L, new long[] { 2L, 3L });
assertEquals(expectedTable, testTable);
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "5:2,3;3:1,").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
expectedTable.put(5L, new long[] { 2L, 3L });
assertEquals(expectedTable, testTable);
// Garbage
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "blahblahblah").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
assertEquals(expectedTable, testTable); // empty table
mPreferences.mSharedPreferences.edit()
.putString("messageNotificationTable", "5:2,3;blahblahblah").apply();
testTable = mPreferences.getMessageNotificationTable();
expectedTable.clear();
expectedTable.put(5L, new long[] { 2L, 3L });
assertEquals(expectedTable, testTable);
}
}