Implementation of business logic for voicemail status.
The new class VoicemailStatusHelper implements all the business logic to
determine what status messages needs to be shown to the user based on
the current status of various voicemail sources. It also orders the
messages in the order of priority.
A follow up cl would use this class to show the message and action to
the user.
Change-Id: I1d0321598356d7451715f027d00ceb3c09418669
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0e56ef4..f3b1b21 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1613,4 +1613,26 @@
<!-- The header in the call log used to identify items that have been already consumed [CHAR LIMIT=10] -->
<string name="call_log_old_header">Older</string>
+
+ <!-- Voicemail status message shown at the top of call log to notify the user that no new
+ voicemails are currently available. This can happen when both notification as well as data
+ connection to the voicemail server is lost. [CHAR LIMIT=64] -->
+ <string name="voicemail_status_voicemail_not_available">Cannot connect to voicemail server.</string>
+ <!-- Voicemail status message shown at the top of call log to notify the user that there is no
+ data connection to the voicemail server, but there are new voicemails waiting on the server.
+ [CHAR LIMIT=64] -->
+ <string name="voicemail_status_messages_waiting">Cannot connect to voicemail server. New voicemails waiting.</string>
+ <!-- Voicemail status message shown at the top of call log to invite the user to configure
+ visual voicemail. [CHAR LIMIT=64] -->
+ <string name="voicemail_status_configure_voicemail">Configure your voicemail.</string>
+ <!-- Voicemail status message shown at the top of call details screen to notify the user that
+ the audio of this voicemail is not available. [CHAR LIMIT=64] -->
+ <string name="voicemail_status_audio_not_available">Audio not available.</string>
+
+ <!-- User action prompt shown next to a voicemail status message to let the user configure
+ visual voicemail. [CHAR LIMIT=20] -->
+ <string name="voicemail_status_action_configure">Configure</string>
+ <!-- User action prompt shown next to a voicemail status message to let the user call voicemail
+ server directly to listen to the voicemails. [CHAR LIMIT=20] -->
+ <string name="voicemail_status_action_call_server">Call voicemail</string>
</resources>
diff --git a/src/com/android/contacts/calllog/VoicemailStatusHelper.java b/src/com/android/contacts/calllog/VoicemailStatusHelper.java
new file mode 100644
index 0000000..d4a3965
--- /dev/null
+++ b/src/com/android/contacts/calllog/VoicemailStatusHelper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.contacts.calllog;
+
+import android.net.Uri;
+
+import java.util.List;
+
+/**
+ * Interface used by the call log UI to determine what user message, if any, related to voicemail
+ * source status needs to be shown. The messages are returned in the order of importance.
+ * <p>
+ * The implementation of this interface interacts with the voicemail content provider to fetch
+ * statuses of all the registered voicemail sources and determines if any status message needs to
+ * be shown. The user of this interface must observe/listen to provider changes and invoke
+ * this class to check if any message needs to be shown.
+ */
+public interface VoicemailStatusHelper {
+ public class Message {
+ /** Package of the source on behalf of which this message has to be shown.*/
+ public final String sourcePackage;
+ /** The string resource id of the status message that should be shown. */
+ public final int statusMessageId;
+ /** The string resource id of the action message that should be shown. */
+ public final int actionMessageId;
+ /** URI for the corrective action, where applicable. Null if no action URI is available. */
+ public final Uri actionUri;
+ public Message(String sourcePackage, int statusMessageId, int actionMessageId,
+ Uri actionUri) {
+ this.sourcePackage = sourcePackage;
+ this.statusMessageId = statusMessageId;
+ this.actionMessageId = actionMessageId;
+ this.actionUri = actionUri;
+ }
+ }
+
+ /**
+ * Returns a list of messages, in the order or priority that should be shown to the user. An
+ * empty list is returned if no message needs to be shown.
+ */
+ public List<Message> getStatusMessages();
+}
diff --git a/src/com/android/contacts/calllog/VoicemailStatusHelperImpl.java b/src/com/android/contacts/calllog/VoicemailStatusHelperImpl.java
new file mode 100644
index 0000000..9738fd7
--- /dev/null
+++ b/src/com/android/contacts/calllog/VoicemailStatusHelperImpl.java
@@ -0,0 +1,245 @@
+/*
+ * 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.contacts.calllog;
+
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_OK;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_OK;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
+
+import com.android.common.io.MoreCloseables;
+import com.android.contacts.R;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.VoicemailContract.Status;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/** Implementation of {@link VoicemailStatusHelper}. */
+public class VoicemailStatusHelperImpl implements VoicemailStatusHelper {
+ private static final int SOURCE_PACKAGE_INDEX = 0;
+ private static final int CONFIGURATION_STATE_INDEX = 1;
+ private static final int DATA_CHANNEL_STATE_INDEX = 2;
+ private static final int NOTIFICATION_CHANNEL_STATE_INDEX = 3;
+ private static final int SETTINGS_URI_INDEX = 4;
+ private static final int VOICEMAIL_ACCESS_URI_INDEX = 5;
+ private static final int NUM_COLUMNS = 6;
+ private static final String[] PROJECTION = new String[NUM_COLUMNS];
+ static {
+ PROJECTION[SOURCE_PACKAGE_INDEX] = Status.SOURCE_PACKAGE;
+ PROJECTION[CONFIGURATION_STATE_INDEX] = Status.CONFIGURATION_STATE;
+ PROJECTION[DATA_CHANNEL_STATE_INDEX] = Status.DATA_CHANNEL_STATE;
+ PROJECTION[NOTIFICATION_CHANNEL_STATE_INDEX] = Status.NOTIFICATION_CHANNEL_STATE;
+ PROJECTION[SETTINGS_URI_INDEX] = Status.SETTINGS_URI;
+ PROJECTION[VOICEMAIL_ACCESS_URI_INDEX] = Status.VOICEMAIL_ACCESS_URI;
+ }
+
+ /** Possible user actions. */
+ public static enum Action {
+ NONE(-1),
+ CALL_VOICEMAIL(R.string.voicemail_status_action_call_server),
+ CONFIGURE_VOICEMAIL(R.string.voicemail_status_action_configure);
+
+ private final int mMessageId;
+ private Action(int messageId) {
+ mMessageId = messageId;
+ }
+
+ public int getMessageId() {
+ return mMessageId;
+ }
+ }
+
+ /**
+ * Overall state of the source status. Each state is associated with the corresponding display
+ * string and the corrective action. The states are also assigned a relative priority which is
+ * used to order the messages from different sources.
+ */
+ private static enum OverallState {
+ // TODO: Add separate string for call details and call log pages for the states that needs
+ // to be shown in both.
+ /** Both notification and data channel are not working. */
+ NO_CONNECTION(0, Action.CALL_VOICEMAIL, R.string.voicemail_status_voicemail_not_available),
+ /** Notifications working, but data channel is not working. Audio cannot be downloaded. */
+ NO_DATA(1, Action.CALL_VOICEMAIL, R.string.voicemail_status_audio_not_available),
+ /** Messages are known to be waiting but data channel is not working. */
+ MESSAGE_WAITING(2, Action.CALL_VOICEMAIL, R.string.voicemail_status_messages_waiting),
+ /** Notification channel not working, but data channel is. */
+ NO_NOTIFICATIONS(3, Action.CALL_VOICEMAIL,
+ R.string.voicemail_status_voicemail_not_available),
+ /** Invite user to set up voicemail. */
+ INVITE_FOR_CONFIGURATION(4, Action.CONFIGURE_VOICEMAIL,
+ R.string.voicemail_status_configure_voicemail),
+ /**
+ * No detailed notifications, but data channel is working.
+ * This is normal mode of operation for certain sources. No action needed.
+ */
+ NO_DETAILED_NOTIFICATION(5, Action.NONE, -1),
+ /** Visual voicemail not yet set up. No local action needed. */
+ NOT_CONFIGURED(6, Action.NONE, -1),
+ /** Everything is OK. */
+ OK(7, Action.NONE, -1),
+ /** If one or more state value set by the source is not valid. */
+ INVALID(8, Action.NONE, -1);
+
+ private final int mPriority;
+ private final Action mAction;
+ private final int mMessageId;
+
+ private OverallState(int priority, Action action, int messageId) {
+ mPriority = priority;
+ mAction = action;
+ mMessageId = messageId;
+ }
+
+ public Action getAction() {
+ return mAction;
+ }
+
+ public int getPriority() {
+ return mPriority;
+ }
+
+ public int getMessageId() {
+ return mMessageId;
+ }
+ }
+
+ private final ContentResolver mContentResolver;
+
+ public VoicemailStatusHelperImpl(ContentResolver contentResolver) {
+ mContentResolver = contentResolver;
+ }
+
+ /** A wrapper on {@link Message} which additionally stores the priority of the message. */
+ private static class MessageWrapper {
+ private final Message mMessage;
+ private final int mPriority;
+
+ public MessageWrapper(Message message, int priority) {
+ mMessage = message;
+ mPriority = priority;
+ }
+ }
+
+ @Override
+ public List<Message> getStatusMessages() {
+ Cursor cursor = null;
+ try {
+ cursor = mContentResolver.query(Status.CONTENT_URI, PROJECTION, null, null, null);
+ List<MessageWrapper> messages =
+ new ArrayList<VoicemailStatusHelperImpl.MessageWrapper>();
+ while(cursor.moveToNext()) {
+ MessageWrapper message = getMessageForStatusEntry(cursor);
+ if (message != null) {
+ messages.add(message);
+ }
+ }
+ // Finally reorder the messages by their priority.
+ return reorderMessages(messages);
+ } finally {
+ MoreCloseables.closeQuietly(cursor);
+ }
+ }
+
+ private List<Message> reorderMessages(List<MessageWrapper> messageWrappers) {
+ Collections.sort(messageWrappers, new Comparator<MessageWrapper>() {
+ @Override
+ public int compare(MessageWrapper msg1, MessageWrapper msg2) {
+ return msg1.mPriority - msg2.mPriority;
+ }
+ });
+ List<Message> reorderMessages = new ArrayList<VoicemailStatusHelper.Message>();
+ // Copy the ordered message objects into the final list.
+ for (MessageWrapper messageWrapper : messageWrappers) {
+ reorderMessages.add(messageWrapper.mMessage);
+ }
+ return reorderMessages;
+ }
+
+ /**
+ * Returns the message for the status entry pointed to by the cursor.
+ */
+ private MessageWrapper getMessageForStatusEntry(Cursor cursor) {
+ final String sourcePackage = cursor.getString(SOURCE_PACKAGE_INDEX);
+ if (sourcePackage == null) {
+ return null;
+ }
+ final OverallState overallState = getOverallState(cursor.getInt(CONFIGURATION_STATE_INDEX),
+ cursor.getInt(DATA_CHANNEL_STATE_INDEX),
+ cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
+ final Action action = overallState.getAction();
+
+ // No source package or no action, means no message shown.
+ if (action == Action.NONE) {
+ return null;
+ }
+
+ Uri actionUri = null;
+ if (action == Action.CALL_VOICEMAIL) {
+ actionUri = Uri.parse(cursor.getString(VOICEMAIL_ACCESS_URI_INDEX));
+ } else if (action == Action.CONFIGURE_VOICEMAIL) {
+ actionUri = Uri.parse(cursor.getString(SETTINGS_URI_INDEX));
+ }
+ return new MessageWrapper(
+ new Message(sourcePackage, overallState.getMessageId(), action.getMessageId(),
+ actionUri),
+ overallState.getPriority());
+ }
+
+ private OverallState getOverallState(int configurationState, int dataChannelState,
+ int notificationChannelState) {
+ if (configurationState == CONFIGURATION_STATE_OK) {
+ // Voicemail is configured. Let's see how is the data channel.
+ if (dataChannelState == DATA_CHANNEL_STATE_OK) {
+ // Data channel is fine. What about notification channel?
+ if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
+ return OverallState.OK;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
+ return OverallState.NO_DETAILED_NOTIFICATION;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
+ return OverallState.NO_NOTIFICATIONS;
+ }
+ } else if (dataChannelState == DATA_CHANNEL_STATE_NO_CONNECTION) {
+ // Data channel is not working. What about notification channel?
+ if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_OK) {
+ return OverallState.NO_DATA;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING) {
+ return OverallState.MESSAGE_WAITING;
+ } else if (notificationChannelState == NOTIFICATION_CHANNEL_STATE_NO_CONNECTION) {
+ return OverallState.NO_CONNECTION;
+ }
+ }
+ } else if (configurationState == CONFIGURATION_STATE_CAN_BE_CONFIGURED) {
+ // Voicemail not configured. data/notification channel states are irrelevant.
+ return OverallState.INVITE_FOR_CONFIGURATION;
+ } else if (configurationState == Status.CONFIGURATION_STATE_NOT_CONFIGURED) {
+ // Voicemail not configured. data/notification channel states are irrelevant.
+ return OverallState.NOT_CONFIGURED;
+ }
+ // Will reach here only if the source has set an invalid value.
+ return OverallState.INVALID;
+ }
+}
diff --git a/tests/src/com/android/contacts/calllog/VoicemailStatusHelperImplTest.java b/tests/src/com/android/contacts/calllog/VoicemailStatusHelperImplTest.java
new file mode 100644
index 0000000..d577d4c
--- /dev/null
+++ b/tests/src/com/android/contacts/calllog/VoicemailStatusHelperImplTest.java
@@ -0,0 +1,246 @@
+/*
+ * 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.contacts.calllog;
+
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE;
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
+import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_NOT_CONFIGURED;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_NO_CONNECTION;
+import static android.provider.VoicemailContract.Status.DATA_CHANNEL_STATE_OK;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_NO_CONNECTION;
+import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
+
+import com.android.contacts.R;
+import com.android.contacts.calllog.VoicemailStatusHelper.Message;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.VoicemailContract.Status;
+import android.test.AndroidTestCase;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link VoicemailStatusHelperImpl}.
+ */
+public class VoicemailStatusHelperImplTest extends AndroidTestCase {
+ private static final String TEST_PACKAGE_1 = "com.test.package1";
+ private static final String TEST_PACKAGE_2 = "com.test.package2";
+
+ private static final Uri TEST_SETTINGS_URI = Uri.parse("http://www.visual.voicemail.setup");
+ private static final Uri TEST_VOICEMAIL_URI = Uri.parse("tel:901");
+
+ private static final int ACTION_MSG_CALL_VOICEMAIL = R.string.voicemail_status_action_call_server;
+ private static final int ACTION_MSG_CONFIGURE = R.string.voicemail_status_action_configure;
+
+ private static final int STATUS_MSG_VOICEMAIL_NOT_AVAILABLE =
+ R.string.voicemail_status_voicemail_not_available;
+ private static final int STATUS_MSG_AUDIO_NOT_AVAIALABLE =
+ R.string.voicemail_status_audio_not_available;
+ private static final int STATUS_MSG_MESSAGE_WAITING = R.string.voicemail_status_messages_waiting;
+ private static final int STATUS_MSG_INVITE_FOR_CONFIGURATION =
+ R.string.voicemail_status_configure_voicemail;
+
+ // The packages whose status entries have been added during the test and needs to be cleaned
+ // up in teardown.
+ private Set<String> mPackagesToCleanup = new HashSet<String>();
+ // Object under test.
+ private VoicemailStatusHelper mStatusHelper;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mStatusHelper = new VoicemailStatusHelperImpl(getContentResolver());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ for (String sourcePackage : mPackagesToCleanup) {
+ deleteEntryForPackage(sourcePackage);
+ }
+ mPackagesToCleanup.clear();
+ // Set member variables to null so that they are garbage collected across different runs
+ // of the tests.
+ mStatusHelper = null;
+ mPackagesToCleanup = null;
+ super.tearDown();
+ }
+
+ public void testNoStatusEntries() {
+ assertEquals(0, mStatusHelper.getStatusMessages().size());
+ }
+
+ public void testAllOK() {
+ insertEntryForPackage(TEST_PACKAGE_1, getAllOkStatusValues());
+ insertEntryForPackage(TEST_PACKAGE_2, getAllOkStatusValues());
+ assertEquals(0, mStatusHelper.getStatusMessages().size());
+ }
+
+ public void testNotAllOKForOnePackage() {
+ insertEntryForPackage(TEST_PACKAGE_1, getAllOkStatusValues());
+ insertEntryForPackage(TEST_PACKAGE_2, getAllOkStatusValues());
+
+ ContentValues values = new ContentValues();
+ // No notification + good data channel - for now same as no connection.
+ values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_OK);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkExpectedMessage(TEST_PACKAGE_2, values, STATUS_MSG_VOICEMAIL_NOT_AVAILABLE,
+ ACTION_MSG_CALL_VOICEMAIL);
+
+ // Message waiting + good data channel - no action.
+ values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING);
+ values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_OK);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkNoMessages(TEST_PACKAGE_2, values);
+
+ // Notification OK + no data channel - call voicemail/no audio.
+ values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_OK);
+ values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkExpectedMessage(TEST_PACKAGE_2, values, STATUS_MSG_AUDIO_NOT_AVAIALABLE,
+ ACTION_MSG_CALL_VOICEMAIL);
+
+ // No notification + no data channel - call voicemail/no connection.
+ values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkExpectedMessage(TEST_PACKAGE_2, values, STATUS_MSG_VOICEMAIL_NOT_AVAILABLE,
+ ACTION_MSG_CALL_VOICEMAIL);
+
+ // Message waiting + no data channel - call voicemail.
+ values.put(NOTIFICATION_CHANNEL_STATE, NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING);
+ values.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkExpectedMessage(TEST_PACKAGE_2, values, STATUS_MSG_MESSAGE_WAITING,
+ ACTION_MSG_CALL_VOICEMAIL);
+
+ // Not configured. No user action, so no message.
+ values.put(CONFIGURATION_STATE, CONFIGURATION_STATE_NOT_CONFIGURED);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkNoMessages(TEST_PACKAGE_2, values);
+
+ // Can be configured - invite user for configure voicemail.
+ values.put(CONFIGURATION_STATE, CONFIGURATION_STATE_CAN_BE_CONFIGURED);
+ updateEntryForPackage(TEST_PACKAGE_2, values);
+ checkExpectedMessage(TEST_PACKAGE_2, values, STATUS_MSG_INVITE_FOR_CONFIGURATION,
+ ACTION_MSG_CONFIGURE, TEST_SETTINGS_URI);
+ }
+
+ // Test that priority of messages are handled well.
+ public void testMessageOrdering() {
+ insertEntryForPackage(TEST_PACKAGE_1, getAllOkStatusValues());
+ insertEntryForPackage(TEST_PACKAGE_2, getAllOkStatusValues());
+
+ final ContentValues valuesNoNotificationGoodDataChannel = new ContentValues();
+ valuesNoNotificationGoodDataChannel.put(NOTIFICATION_CHANNEL_STATE,
+ NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ valuesNoNotificationGoodDataChannel.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_OK);
+
+ final ContentValues valuesNoNotificationNoDataChannel = new ContentValues();
+ valuesNoNotificationNoDataChannel.put(NOTIFICATION_CHANNEL_STATE,
+ NOTIFICATION_CHANNEL_STATE_NO_CONNECTION);
+ valuesNoNotificationNoDataChannel.put(DATA_CHANNEL_STATE, DATA_CHANNEL_STATE_NO_CONNECTION);
+
+ // Package1 with valuesNoNotificationGoodDataChannel and
+ // package2 with valuesNoNotificationNoDataChannel. Package2 should be above.
+ updateEntryForPackage(TEST_PACKAGE_1, valuesNoNotificationGoodDataChannel);
+ updateEntryForPackage(TEST_PACKAGE_2, valuesNoNotificationNoDataChannel);
+ List<Message> messages = mStatusHelper.getStatusMessages();
+ assertEquals(2, messages.size());
+ assertEquals(TEST_PACKAGE_1, messages.get(1).sourcePackage);
+ assertEquals(TEST_PACKAGE_2, messages.get(0).sourcePackage);
+
+ // Now reverse the values - ordering should be reversed as well.
+ updateEntryForPackage(TEST_PACKAGE_1, valuesNoNotificationNoDataChannel);
+ updateEntryForPackage(TEST_PACKAGE_2, valuesNoNotificationGoodDataChannel);
+ messages = mStatusHelper.getStatusMessages();
+ assertEquals(2, messages.size());
+ assertEquals(TEST_PACKAGE_1, messages.get(0).sourcePackage);
+ assertEquals(TEST_PACKAGE_2, messages.get(1).sourcePackage);
+ }
+
+ /** Checks for the expected message with given values and actionUri as TEST_VOICEMAIL_URI. */
+ private void checkExpectedMessage(String sourcePackage, ContentValues values,
+ int expectedStatusMsg, int expectedActionMsg) {
+ checkExpectedMessage(sourcePackage, values, expectedStatusMsg, expectedActionMsg,
+ TEST_VOICEMAIL_URI);
+ }
+
+ private void checkExpectedMessage(String sourcePackage, ContentValues values,
+ int expectedStatusMsg, int expectedActionMsg, Uri expectedUri) {
+ List<Message> messages = mStatusHelper.getStatusMessages();
+ assertEquals(1, messages.size());
+ checkMessageMatches(messages.get(0), sourcePackage, expectedStatusMsg, expectedActionMsg,
+ expectedUri);
+ }
+
+ private void checkMessageMatches(Message message, String expectedSourcePackage,
+ int expectedStatusMsg, int expectedActionMsg, Uri expectedUri) {
+ assertEquals(expectedSourcePackage, message.sourcePackage);
+ assertEquals(expectedStatusMsg, message.statusMessageId);
+ assertEquals(expectedActionMsg, message.actionMessageId);
+ if (expectedUri == null) {
+ assertNull(message.actionUri);
+ } else {
+ assertEquals(expectedUri, message.actionUri);
+ }
+ }
+
+ private void checkNoMessages(String sourcePackage, ContentValues values) {
+ assertEquals(1, updateEntryForPackage(sourcePackage, values));
+ List<Message> messages = mStatusHelper.getStatusMessages();
+ assertEquals(0, messages.size());
+ }
+
+ private ContentValues getAllOkStatusValues() {
+ ContentValues values = new ContentValues();
+ values.put(Status.SETTINGS_URI, TEST_SETTINGS_URI.toString());
+ values.put(Status.VOICEMAIL_ACCESS_URI, TEST_VOICEMAIL_URI.toString());
+ values.put(Status.CONFIGURATION_STATE, Status.CONFIGURATION_STATE_OK);
+ values.put(Status.DATA_CHANNEL_STATE, Status.DATA_CHANNEL_STATE_OK);
+ values.put(Status.NOTIFICATION_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE_OK);
+ return values;
+ }
+
+ private void insertEntryForPackage(String sourcePackage, ContentValues values) {
+ // If insertion fails then try update as the record might already exist.
+ if (getContentResolver().insert(Status.buildSourceUri(sourcePackage), values) == null) {
+ updateEntryForPackage(sourcePackage, values);
+ }
+ mPackagesToCleanup.add(sourcePackage);
+ }
+
+ private void deleteEntryForPackage(String sourcePackage) {
+ getContentResolver().delete(Status.buildSourceUri(sourcePackage), null, null);
+ }
+
+ private int updateEntryForPackage(String sourcePackage, ContentValues values) {
+ return getContentResolver().update(
+ Status.buildSourceUri(sourcePackage), values, null, null);
+ }
+
+ private ContentResolver getContentResolver() {
+ return getContext().getContentResolver();
+ }
+}