Changes in CallDetailActivity to show voicemail status message.

- call details page now show a voicemail status message if the call in
  question is a voicemail and does not have audio available.
- moved VoicemailStatusHelper and related classes into
  contacts.voicemail package as it is used by CallDetailActivity as well
  as calllog.CallLogFragment

Bug: 5040099
Change-Id: I9ad0c1fb60526ef55d7f38dbd2157b14af9d5b55
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index d2c193e..23436b4 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -20,6 +20,9 @@
 import com.android.contacts.calllog.CallTypeHelper;
 import com.android.contacts.calllog.PhoneNumberHelper;
 import com.android.contacts.voicemail.VoicemailPlaybackFragment;
+import com.android.contacts.voicemail.VoicemailStatusHelper;
+import com.android.contacts.voicemail.VoicemailStatusHelper.StatusMessage;
+import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
 
 import android.app.FragmentManager;
 import android.app.ListActivity;
@@ -87,6 +90,14 @@
     /* package */ Resources mResources;
     /** Helper to load contact photos. */
     private ContactPhotoManager mContactPhotoManager;
+    /** Helper to make async queries to content resolver. */
+    private CallDetailActivityQueryHandler mAsyncQueryHandler;
+    /** Helper to get voicemail status messages. */
+    private VoicemailStatusHelper mVoicemailStatusHelper;
+    // Views related to voicemail status message.
+    private View mStatusMessageView;
+    private TextView mStatusMessageText;
+    private TextView mStatusMessageAction;
 
     static final String[] CALL_LOG_PROJECTION = new String[] {
         CallLog.Calls.DATE,
@@ -137,6 +148,11 @@
         mPhoneNumberHelper = new PhoneNumberHelper(mResources, getVoicemailNumber());
         mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(this, mResources, mCallTypeHelper,
                 mPhoneNumberHelper);
+        mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
+        mAsyncQueryHandler = new CallDetailActivityQueryHandler(this);
+        mStatusMessageView = findViewById(R.id.voicemail_status);
+        mStatusMessageText = (TextView) findViewById(R.id.voicemail_status_message);
+        mStatusMessageAction = (TextView) findViewById(R.id.voicemail_status_action);
         mHomeActionView = findViewById(R.id.action_bar_home);
         mMainActionView = (ImageView) findViewById(R.id.main_action);
         mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
@@ -153,11 +169,18 @@
         });
     }
 
+
     @Override
     public void onResume() {
         super.onResume();
         updateData(getCallLogEntryUris());
-        optionallyHandleVoicemail();
+        Uri voicemailUri = getIntent().getExtras().getParcelable(EXTRA_VOICEMAIL_URI);
+        optionallyHandleVoicemail(voicemailUri);
+        if (voicemailUri != null) {
+            mAsyncQueryHandler.startVoicemailStatusQuery(voicemailUri);
+        } else {
+            mStatusMessageView.setVisibility(View.GONE);
+        }
     }
 
     /**
@@ -166,11 +189,10 @@
      * If the Intent used to start this Activity contains the suitable extras, then start voicemail
      * playback.  If it doesn't, then hide the voicemail ui.
      */
-    private void optionallyHandleVoicemail() {
+    private void optionallyHandleVoicemail(Uri voicemailUri) {
         FragmentManager manager = getFragmentManager();
         VoicemailPlaybackFragment fragment = (VoicemailPlaybackFragment) manager.findFragmentById(
                 R.id.voicemail_playback_fragment);
-        Uri voicemailUri = getIntent().getExtras().getParcelable(EXTRA_VOICEMAIL_URI);
         if (voicemailUri == null) {
             // No voicemail uri: hide the voicemail fragment.
             manager.beginTransaction().hide(fragment).commit();
@@ -554,4 +576,48 @@
             ContactsSearchManager.startSearch(this, initialQuery);
         }
     }
+
+    protected void updateVoicemailStatusMessage(Cursor statusCursor) {
+        if (statusCursor == null) {
+            mStatusMessageView.setVisibility(View.GONE);
+            return;
+        }
+        final StatusMessage message = getStatusMessage(statusCursor);
+        if (message == null || !message.showInCallDetails()) {
+            mStatusMessageView.setVisibility(View.GONE);
+            return;
+        }
+
+        mStatusMessageView.setVisibility(View.VISIBLE);
+        mStatusMessageText.setText(message.callDetailsMessageId);
+        if (message.actionMessageId != -1) {
+            mStatusMessageAction.setText(message.actionMessageId);
+        }
+        if (message.actionUri != null) {
+            mStatusMessageAction.setClickable(true);
+            mStatusMessageAction.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    startActivity(new Intent(Intent.ACTION_VIEW, message.actionUri));
+                }
+            });
+        } else {
+            mStatusMessageAction.setClickable(false);
+        }
+    }
+
+    private StatusMessage getStatusMessage(Cursor statusCursor) {
+        List<StatusMessage> messages = mVoicemailStatusHelper.getStatusMessages(statusCursor);
+        Log.d(TAG, "Num status messages: " + messages.size());
+        if (messages.size() == 0) {
+            return null;
+        }
+        // There can only be a single status message per source package, so num of messages can
+        // at most be 1.
+        if (messages.size() > 1) {
+            Log.w(TAG, String.format("Expected 1, found (%d) num of status messages." +
+                    " Will use the first one.", messages.size()));
+        }
+        return messages.get(0);
+    }
 }
diff --git a/src/com/android/contacts/CallDetailActivityQueryHandler.java b/src/com/android/contacts/CallDetailActivityQueryHandler.java
new file mode 100644
index 0000000..c1d87b2
--- /dev/null
+++ b/src/com/android/contacts/CallDetailActivityQueryHandler.java
@@ -0,0 +1,94 @@
+/*
+ * 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;
+
+import com.android.common.io.MoreCloseables;
+import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
+
+import android.content.AsyncQueryHandler;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.VoicemailContract.Status;
+import android.provider.VoicemailContract.Voicemails;
+import android.util.Log;
+
+/**
+ * Class used by {@link CallDetailActivity} to fire async content resolver queries.
+ */
+public class CallDetailActivityQueryHandler extends AsyncQueryHandler {
+    private static final String TAG = "CallDetail";
+    private static final int QUERY_VOICEMAIL_CONTENT_TOKEN = 101;
+    private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 102;
+
+    private final String[] VOICEMAIL_CONTENT_PROJECTION = new String[] {
+        Voicemails.SOURCE_PACKAGE,
+        Voicemails.HAS_CONTENT
+    };
+    private static final int SOURCE_PACKAGE_COLUMN_INDEX = 0;
+    private static final int HAS_CONTENT_COLUMN_INDEX = 1;
+
+    private final CallDetailActivity mCallDetailActivity;
+
+    public CallDetailActivityQueryHandler(CallDetailActivity callDetailActivity) {
+        super(callDetailActivity.getContentResolver());
+        mCallDetailActivity = callDetailActivity;
+    }
+
+    /**
+     * Fires a query to update voicemail status for the given voicemail record. On completion of the
+     * query a call to {@link CallDetailActivity#updateVoicemailStatusMessage(Cursor)} is made.
+     * <p>
+     * if this is a voicemail record then it makes up to two asynchronous content resolver queries.
+     * The first one to fetch voicemail content details and check if the voicemail record has audio.
+     * If the voicemail record does not have an audio yet then it fires the second query to get the
+     * voicemail status of the associated source.
+     */
+    public void startVoicemailStatusQuery(Uri voicemaiUri) {
+        startQuery(QUERY_VOICEMAIL_CONTENT_TOKEN, null, voicemaiUri, VOICEMAIL_CONTENT_PROJECTION,
+                null, null, null);
+    }
+
+    @Override
+    protected synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
+        try {
+            if (token == QUERY_VOICEMAIL_CONTENT_TOKEN) {
+                // Query voicemail status only if this voicemail record does not have audio.
+                if (cursor.moveToFirst() && hasNoAudio(cursor)) {
+                    startQuery(QUERY_VOICEMAIL_STATUS_TOKEN, null,
+                            Status.buildSourceUri(getSourcePackage(cursor)),
+                            VoicemailStatusHelperImpl.PROJECTION, null, null, null);
+                } else {
+                    mCallDetailActivity.updateVoicemailStatusMessage(null);
+                }
+            } else if (token == QUERY_VOICEMAIL_STATUS_TOKEN) {
+                mCallDetailActivity.updateVoicemailStatusMessage(cursor);
+            } else {
+                Log.w(TAG, "Unknown query completed: ignoring: " + token);
+            }
+        } finally {
+            MoreCloseables.closeQuietly(cursor);
+        }
+    }
+
+    private boolean hasNoAudio(Cursor voicemailCursor) {
+        return voicemailCursor.getInt(HAS_CONTENT_COLUMN_INDEX) == 0;
+    }
+
+    private String getSourcePackage(Cursor voicemailCursor) {
+        return voicemailCursor.getString(SOURCE_PACKAGE_COLUMN_INDEX);
+    }
+}
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 1aa4d3d..28f642c 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -25,8 +25,10 @@
 import com.android.contacts.R;
 import com.android.contacts.activities.DialtactsActivity;
 import com.android.contacts.activities.DialtactsActivity.ViewPagerVisibilityListener;
-import com.android.contacts.calllog.VoicemailStatusHelper.StatusMessage;
 import com.android.contacts.util.ExpirableCache;
+import com.android.contacts.voicemail.VoicemailStatusHelper;
+import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
+import com.android.contacts.voicemail.VoicemailStatusHelper.StatusMessage;
 import com.android.internal.telephony.CallerInfo;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
diff --git a/src/com/android/contacts/calllog/CallLogQueryHandler.java b/src/com/android/contacts/calllog/CallLogQueryHandler.java
index 62e308c..8569190 100644
--- a/src/com/android/contacts/calllog/CallLogQueryHandler.java
+++ b/src/com/android/contacts/calllog/CallLogQueryHandler.java
@@ -18,6 +18,7 @@
 
 import com.android.common.io.MoreCloseables;
 import com.android.contacts.calllog.CallLogFragment.CallLogQuery;
+import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
 
 import android.content.AsyncQueryHandler;
 import android.content.ContentValues;
diff --git a/src/com/android/contacts/calllog/VoicemailStatusHelper.java b/src/com/android/contacts/voicemail/VoicemailStatusHelper.java
similarity index 98%
rename from src/com/android/contacts/calllog/VoicemailStatusHelper.java
rename to src/com/android/contacts/voicemail/VoicemailStatusHelper.java
index f6def45..1eab749 100644
--- a/src/com/android/contacts/calllog/VoicemailStatusHelper.java
+++ b/src/com/android/contacts/voicemail/VoicemailStatusHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.contacts.calllog;
+package com.android.contacts.voicemail;
 
 import android.database.Cursor;
 import android.net.Uri;
diff --git a/src/com/android/contacts/calllog/VoicemailStatusHelperImpl.java b/src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java
similarity index 98%
rename from src/com/android/contacts/calllog/VoicemailStatusHelperImpl.java
rename to src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java
index 972fbd5..7332e89 100644
--- a/src/com/android/contacts/calllog/VoicemailStatusHelperImpl.java
+++ b/src/com/android/contacts/voicemail/VoicemailStatusHelperImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.contacts.calllog;
+package com.android.contacts.voicemail;
 
 import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
 import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_OK;
@@ -46,7 +46,7 @@
     private static final int VOICEMAIL_ACCESS_URI_INDEX = 5;
     private static final int NUM_COLUMNS = 6;
     /** Projection on the voicemail_status table used by this class. */
-    protected static final String[] PROJECTION = new String[NUM_COLUMNS];
+    public static final String[] PROJECTION = new String[NUM_COLUMNS];
     static {
         PROJECTION[SOURCE_PACKAGE_INDEX] = Status.SOURCE_PACKAGE;
         PROJECTION[CONFIGURATION_STATE_INDEX] = Status.CONFIGURATION_STATE;
diff --git a/tests/src/com/android/contacts/calllog/VoicemailStatusHelperImplTest.java b/tests/src/com/android/contacts/voicemail/VoicemailStatusHelperImplTest.java
similarity index 97%
rename from tests/src/com/android/contacts/calllog/VoicemailStatusHelperImplTest.java
rename to tests/src/com/android/contacts/voicemail/VoicemailStatusHelperImplTest.java
index 9b293c4..7ac7e49 100644
--- a/tests/src/com/android/contacts/calllog/VoicemailStatusHelperImplTest.java
+++ b/tests/src/com/android/contacts/voicemail/VoicemailStatusHelperImplTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.contacts.calllog;
+package com.android.contacts.voicemail;
 
 import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE;
 import static android.provider.VoicemailContract.Status.CONFIGURATION_STATE_CAN_BE_CONFIGURED;
@@ -28,7 +28,9 @@
 import static android.provider.VoicemailContract.Status.NOTIFICATION_CHANNEL_STATE_OK;
 
 import com.android.contacts.R;
-import com.android.contacts.calllog.VoicemailStatusHelper.StatusMessage;
+import com.android.contacts.voicemail.VoicemailStatusHelper;
+import com.android.contacts.voicemail.VoicemailStatusHelperImpl;
+import com.android.contacts.voicemail.VoicemailStatusHelper.StatusMessage;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;