Merge "Log various call initiation entry points" into ub-contactsdialer-a-dev
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index 5a62a7d..aa994d2 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -156,9 +156,13 @@
             boolean isVoicemail = PhoneNumberUtil.isVoicemailNumber(context, accountHandle, number);
             boolean shouldLookupNumber =
                     PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
-            ContactInfo info = shouldLookupNumber
-                            ? contactInfoHelper.lookupNumber(number, countryIso)
-                            : ContactInfo.EMPTY;
+
+            ContactInfo info = ContactInfo.EMPTY;
+            if (shouldLookupNumber) {
+                ContactInfo lookupInfo = contactInfoHelper.lookupNumber(number, countryIso);
+                info = lookupInfo != null ? lookupInfo : ContactInfo.EMPTY;
+            }
+
             PhoneCallDetails details = new PhoneCallDetails(
                     context, number, numberPresentation, info.formattedNumber, isVoicemail);
 
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 8762f18..26e3965 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -34,6 +34,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Message;
 import android.provider.CallLog;
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract;
@@ -89,6 +90,10 @@
 
     private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
 
+    private static final int EVENT_UPDATE_DISPLAY = 1;
+
+    private static final long MILLIS_IN_MINUTE = 60 * 1000;
+
     private RecyclerView mRecyclerView;
     private LinearLayoutManager mLayoutManager;
     private CallLogAdapter mAdapter;
@@ -106,6 +111,18 @@
     private boolean mCallLogFetched;
     private boolean mVoicemailStatusFetched;
 
+    private final Handler mDisplayUpdateHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case EVENT_UPDATE_DISPLAY:
+                    refreshData();
+                    rescheduleDisplayUpdate();
+                    break;
+            }
+        }
+    };
+
     private final Handler mHandler = new Handler();
 
     private class CustomContentObserver extends ContentObserver {
@@ -343,10 +360,14 @@
         mHasReadCallLogPermission = hasReadCallLogPermission;
         refreshData();
         mAdapter.startCache();
+
+        rescheduleDisplayUpdate();
     }
 
     @Override
     public void onPause() {
+        cancelDisplayUpdate();
+
         if (mVoicemailPlaybackPresenter != null) {
             mVoicemailPlaybackPresenter.onPause();
         }
@@ -517,4 +538,25 @@
             }
         }
     }
+
+    /**
+     * Schedules an update to the relative call times (X mins ago).
+     */
+    private void rescheduleDisplayUpdate() {
+        if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) {
+            long time = System.currentTimeMillis();
+            // This value allows us to change the display relatively close to when the time changes
+            // from one minute to the next.
+            long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE);
+            mDisplayUpdateHandler.sendEmptyMessageDelayed(
+                    EVENT_UPDATE_DISPLAY, millisUtilNextMinute);
+        }
+    }
+
+    /**
+     * Cancels any pending update requests to update the relative call times (X mins ago).
+     */
+    private void cancelDisplayUpdate() {
+        mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY);
+    }
 }
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 133da36..14c5473 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -42,6 +42,7 @@
 import com.android.dialer.R;
 import com.android.dialer.calllog.CallLogAsyncTaskUtil;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 import java.util.concurrent.TimeUnit;
@@ -411,4 +412,9 @@
         }
         return String.format("%02d:%02d", minutes, seconds);
     }
+
+    @VisibleForTesting
+    public String getStateText() {
+        return mStateText.getText().toString();
+    }
 }
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 62da942..95622bf 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -781,4 +781,9 @@
     public boolean isSpeakerphoneOn() {
         return mIsSpeakerphoneOn;
     }
+
+    @VisibleForTesting
+    public void clearInstance() {
+        sInstance = null;
+    }
 }
diff --git a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
index 95558bc..9bdcd40 100644
--- a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
@@ -66,14 +66,14 @@
     public void testAddGroups_OneCall() {
         addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
         mBuilder.addGroups(mCursor);
-        assertEquals(0, mFakeGroupCreator.groups.size());
+        assertEquals(1, mFakeGroupCreator.groups.size());
     }
 
     public void testAddGroups_TwoCallsNotMatching() {
         addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
         addCallLogEntry(TEST_NUMBER2, Calls.INCOMING_TYPE);
         mBuilder.addGroups(mCursor);
-        assertEquals(0, mFakeGroupCreator.groups.size());
+        assertEquals(2, mFakeGroupCreator.groups.size());
     }
 
     public void testAddGroups_ThreeCallsMatching() {
@@ -136,21 +136,25 @@
 
     public void testAddGroups_Mixed() {
         addMultipleCallLogEntries(TEST_NUMBER1,
-                Calls.VOICEMAIL_TYPE,  // Stand-alone
-                Calls.INCOMING_TYPE,  // Group 1: 1-4
+                Calls.VOICEMAIL_TYPE,   // Group 1:Stand-alone
+                Calls.INCOMING_TYPE,    // Group 2: 1-4
                 Calls.OUTGOING_TYPE,
                 Calls.MISSED_TYPE,
                 Calls.MISSED_TYPE,
-                Calls.VOICEMAIL_TYPE,  // Stand-alone
-                Calls.INCOMING_TYPE,  // Stand-alone
-                Calls.VOICEMAIL_TYPE,  // Stand-alone
-                Calls.MISSED_TYPE, // Group 2: 8-10
+                Calls.VOICEMAIL_TYPE,   // Group 3: Stand-alone
+                Calls.INCOMING_TYPE,    // Group 4: Stand-alone
+                Calls.VOICEMAIL_TYPE,   // Group 5: Stand-alone
+                Calls.MISSED_TYPE,      // Group 6: 8-10
                 Calls.MISSED_TYPE,
                 Calls.OUTGOING_TYPE);
         mBuilder.addGroups(mCursor);
-        assertEquals(2, mFakeGroupCreator.groups.size());
-        assertGroupIs(1, 4, mFakeGroupCreator.groups.get(0));
-        assertGroupIs(8, 3, mFakeGroupCreator.groups.get(1));
+        assertEquals(6, mFakeGroupCreator.groups.size());
+        assertGroupIs(0, 1, mFakeGroupCreator.groups.get(0));
+        assertGroupIs(1, 4, mFakeGroupCreator.groups.get(1));
+        assertGroupIs(5, 1, mFakeGroupCreator.groups.get(2));
+        assertGroupIs(6, 1, mFakeGroupCreator.groups.get(3));
+        assertGroupIs(7, 1, mFakeGroupCreator.groups.get(4));
+        assertGroupIs(8, 3, mFakeGroupCreator.groups.get(5));
     }
 
     public void testEqualPhoneNumbers() {
@@ -237,7 +241,7 @@
         clearFakeGroupCreator();
         addMultipleCallLogEntries(TEST_NUMBER1, types);
         mBuilder.addGroups(mCursor);
-        assertEquals(0, mFakeGroupCreator.groups.size());
+        assertEquals(types.length, mFakeGroupCreator.groups.size());
     }
 
     /** Adds a set of calls with the given types, all from the same number, in the old section. */
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
index 6c7cf77..420a17c 100644
--- a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
+++ b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
@@ -17,6 +17,7 @@
 package com.android.dialer.voicemail;
 
 import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT;
+import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_CONTENT_AFTER_CHANGE;
 
 import android.app.Activity;
 import android.content.ContentResolver;
@@ -31,7 +32,6 @@
 import android.view.View;
 import android.widget.TextView;
 
-import com.android.contacts.common.test.IntegrationTestUtils;
 import com.android.dialer.R;
 import com.android.dialer.calllog.CallLogActivity;
 import com.android.dialer.util.AsyncTaskExecutors;
@@ -61,7 +61,6 @@
     private VoicemailPlaybackLayout mLayout;
 
     private Uri mVoicemailUri;
-    private IntegrationTestUtils mTestUtils;
     private LocaleTestUtils mLocaleTestUtils;
     private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
 
@@ -75,7 +74,6 @@
 
         mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
         AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
-        mTestUtils = new IntegrationTestUtils(getInstrumentation());
 
         // Some of the tests rely on the text - safest to force a specific locale.
         mLocaleTestUtils = new LocaleTestUtils(getInstrumentation().getTargetContext());
@@ -95,54 +93,72 @@
         mLocaleTestUtils.restoreLocale();
         mLocaleTestUtils = null;
 
-        mLayout = null;
-        mPresenter = null;
-        mTestUtils = null;
+        mPresenter.clearInstance();
         AsyncTaskExecutors.setFactoryForTest(null);
 
+        mActivity = null;
+        mPresenter = null;
+        mLayout = null;
+
         super.tearDown();
     }
 
     public void testFetchingVoicemail() throws Throwable {
-        setUriForRealFileVoicemailEntry();
+        setUriForUnfetchedVoicemailEntry();
         setPlaybackViewForPresenter();
-        assertHasOneTextViewContaining("Loading voicemail");
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mPresenter.resumePlayback();
+            }
+        });
+        mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
+        getInstrumentation().waitForIdleSync();
+
+        assertStateTextContains("Loading voicemail");
     }
 
     public void testWhenCheckForContentCompletes() throws Throwable {
         setUriForRealFileVoicemailEntry();
         setPlaybackViewForPresenter();
 
-        // There is a background check that is testing to see if we have the content available.
-        // Once that task completes, we shouldn't be showing the fetching message.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mPresenter.resumePlayback();
+            }
+        });
         mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
         getInstrumentation().waitForIdleSync();
 
-        assertHasOneTextViewContaining("Buffering");
-        assertHasZeroTextViewsContaining("Loading voicemail");
+        // Since the content is already fetched, don't show the loading message.
+        assertStateTextNotContains("Loading voicemail");
     }
 
     public void testInvalidVoicemailShowsErrorMessage() throws Throwable {
         setUriForInvalidVoicemailEntry();
         setPlaybackViewForPresenter();
 
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mPresenter.resumePlayback();
+            }
+        });
         mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
         getInstrumentation().waitForIdleSync();
 
         // The media player will have thrown an IOException since the file doesn't exist.
         // This should have put a failed to play message on screen, buffering is gone.
-        assertHasOneTextViewContaining("Couldn't play voicemail");
-        assertHasZeroTextViewsContaining("Buffering");
+        assertStateTextContains("Couldn't play voicemail");
+        assertStateTextNotContains("Buffering");
     }
 
     public void testClickingSpeakerphoneButton() throws Throwable {
         setUriForRealFileVoicemailEntry();
         setPlaybackViewForPresenter();
 
-        // Wait for check for content to complete.
-        mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
-        getInstrumentation().waitForIdleSync();
-
         // Check that the speakerphone is false to start.
         assertFalse(mPresenter.isSpeakerphoneOn());
 
@@ -176,6 +192,18 @@
         }
     }
 
+    private void setUriForUnfetchedVoicemailEntry() {
+        assertNull(mVoicemailUri);
+        ContentValues values = new ContentValues();
+        values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis()));
+        values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
+        values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE);
+        values.put(VoicemailContract.Voicemails.HAS_CONTENT, 0);
+        String packageName = getInstrumentation().getTargetContext().getPackageName();
+        mVoicemailUri = getContentResolver().insert(
+                VoicemailContract.Voicemails.buildSourceUri(packageName), values);
+    }
+
     private void setUriForInvalidVoicemailEntry() {
         assertNull(mVoicemailUri);
         ContentResolver contentResolver = getContentResolver();
@@ -204,18 +232,14 @@
         }
     }
 
-    private void assertHasOneTextViewContaining(String text) throws Throwable {
+    private void assertStateTextContains(String text) throws Throwable {
         assertNotNull(mLayout);
-        List<TextView> views = mTestUtils.getTextViewsWithString(mLayout, text);
-        assertEquals("There should have been one TextView with text '" + text + "' but found "
-                + views, 1, views.size());
+        assertTrue(mLayout.getStateText().contains(text));
     }
 
-    private void assertHasZeroTextViewsContaining(String text) throws Throwable {
+    private void assertStateTextNotContains(String text) throws Throwable {
         assertNotNull(mLayout);
-        List<TextView> views = mTestUtils.getTextViewsWithString(mLayout, text);
-        assertEquals("There should have been no TextViews with text '" + text + "' but found "
-                + views, 0,  views.size());
+        assertFalse(mLayout.getStateText().contains(text));
     }
 
     private ContentResolver getContentResolver() {