Merge changes I84b69953,Ic996a314

* changes:
  Properly display date and/or time of entries in the new call log.
  Add simulator enriched call related to interfaces and stubs.
diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
index 474f666..7d277d4 100644
--- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
+++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java
@@ -30,6 +30,7 @@
 import com.android.dialer.precall.impl.PreCallModule;
 import com.android.dialer.preferredsim.suggestion.stub.StubSimSuggestionModule;
 import com.android.dialer.simulator.impl.SimulatorModule;
+import com.android.dialer.simulator.stub.StubSimulatorEnrichedCallModule;
 import com.android.dialer.spam.StubSpamModule;
 import com.android.dialer.storage.StorageModule;
 import com.android.dialer.strictmode.impl.SystemStrictModeModule;
@@ -53,6 +54,7 @@
     PreCallModule.class,
     SharedPrefConfigProviderModule.class,
     SimulatorModule.class,
+    StubSimulatorEnrichedCallModule.class,
     StorageModule.class,
     StubCallLocationModule.class,
     StubDuoModule.class,
diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
index 12038b7..ff1b09d 100644
--- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
+++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java
@@ -30,6 +30,7 @@
 import com.android.dialer.precall.impl.PreCallModule;
 import com.android.dialer.preferredsim.suggestion.stub.StubSimSuggestionModule;
 import com.android.dialer.simulator.impl.SimulatorModule;
+import com.android.dialer.simulator.stub.StubSimulatorEnrichedCallModule;
 import com.android.dialer.spam.StubSpamModule;
 import com.android.dialer.storage.StorageModule;
 import com.android.dialer.strictmode.impl.SystemStrictModeModule;
@@ -59,6 +60,7 @@
     SharedPrefConfigProviderModule.class,
     SimulatorModule.class,
     StorageModule.class,
+    StubSimulatorEnrichedCallModule.class,
     StubDuoModule.class,
     StubEnrichedCallModule.class,
     StubFeedbackModule.class,
diff --git a/java/com/android/dialer/calllogutils/CallLogDates.java b/java/com/android/dialer/calllogutils/CallLogDates.java
index 84e52df..bdf6215 100644
--- a/java/com/android/dialer/calllogutils/CallLogDates.java
+++ b/java/com/android/dialer/calllogutils/CallLogDates.java
@@ -35,29 +35,52 @@
    * <p>Rules:
    *
    * <pre>
-   *   if < 1 minute ago: "Now";
-   *   else if today: "12:15 PM"
-   *   else if < 3 days ago: "Wednesday";
-   *   else: "Jan 15"
+   *   if < 1 minute ago: "Just now";
+   *   else if < 1 hour ago: time relative to now (e.g., "8 min. ago");
+   *   else if today: time (e.g., "12:15 PM");
+   *   else if < 7 days: abbreviated day of week (e.g., "Wed");
+   *   else if < 1 year: date with abbreviated month, day, but no year (e.g., "Jan 15");
+   *   else: date with abbreviated month, day, and year (e.g., "Jan 15, 2018").
    * </pre>
    */
   public static CharSequence newCallLogTimestampLabel(
       Context context, long nowMillis, long timestampMillis) {
+    // For calls logged less than 1 minute ago, display "Just now".
     if (nowMillis - timestampMillis < TimeUnit.MINUTES.toMillis(1)) {
-      return context.getString(R.string.now);
+      return context.getString(R.string.just_now);
     }
-    if (isSameDay(nowMillis, timestampMillis)) {
-      return DateUtils.formatDateTime(
-          context, timestampMillis, DateUtils.FORMAT_SHOW_TIME); // e.g. 12:15 PM
+
+    // For calls logged less than 1 hour ago, display time relative to now (e.g., "8 min. ago").
+    if (nowMillis - timestampMillis < TimeUnit.HOURS.toMillis(1)) {
+      return DateUtils.getRelativeTimeSpanString(
+          timestampMillis, nowMillis, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
     }
-    if (getDayDifference(nowMillis, timestampMillis) < 3) {
-      return formatDayOfWeek(context, timestampMillis); // e.g. "Wednesday"
+
+    int dayDifference = getDayDifference(nowMillis, timestampMillis);
+
+    // For calls logged today, display time (e.g., "12:15 PM").
+    if (dayDifference == 0) {
+      return DateUtils.formatDateTime(context, timestampMillis, DateUtils.FORMAT_SHOW_TIME);
     }
-    return formatAbbreviatedMonthAndDay(context, timestampMillis); // e.g. "Jan 15"
+
+    // For calls logged within a week, display the abbreviated day of week (e.g., "Wed").
+    if (dayDifference < 7) {
+      return formatDayOfWeek(context, timestampMillis);
+    }
+
+    // For calls logged within a year, display abbreviated month, day, but no year (e.g., "Jan 15").
+    if (isWithinOneYear(nowMillis, timestampMillis)) {
+      return formatAbbreviatedDate(context, timestampMillis, /* showYear = */ false);
+    }
+
+    // For calls logged no less than one year ago, display abbreviated month, day, and year
+    // (e.g., "Jan 15, 2018").
+    return formatAbbreviatedDate(context, timestampMillis, /* showYear = */ true);
   }
 
   /**
-   * Formats the provided date into a value suitable for display in the current locale.
+   * Formats the provided timestamp (in milliseconds) into date and time suitable for display in the
+   * current locale.
    *
    * <p>For example, returns a string like "Wednesday, May 25, 2016, 8:02PM" or "Chorshanba, 2016
    * may 25,20:02".
@@ -65,11 +88,11 @@
    * <p>For pre-N devices, the returned value may not start with a capital if the local convention
    * is to not capitalize day names. On N+ devices, the returned value is always capitalized.
    */
-  public static CharSequence formatDate(Context context, long callDateMillis) {
+  public static CharSequence formatDate(Context context, long timestamp) {
     return toTitleCase(
         DateUtils.formatDateTime(
             context,
-            callDateMillis,
+            timestamp,
             DateUtils.FORMAT_SHOW_TIME
                 | DateUtils.FORMAT_SHOW_DATE
                 | DateUtils.FORMAT_SHOW_WEEKDAY
@@ -77,30 +100,36 @@
   }
 
   /**
-   * Formats the provided date into the day of week.
+   * Formats the provided timestamp (in milliseconds) into abbreviated day of week.
    *
-   * <p>For example, returns a string like "Wednesday" or "Chorshanba".
+   * <p>For example, returns a string like "Wed" or "Chor".
    *
    * <p>For pre-N devices, the returned value may not start with a capital if the local convention
    * is to not capitalize day names. On N+ devices, the returned value is always capitalized.
    */
-  private static CharSequence formatDayOfWeek(Context context, long callDateMillis) {
+  private static CharSequence formatDayOfWeek(Context context, long timestamp) {
     return toTitleCase(
-        DateUtils.formatDateTime(context, callDateMillis, DateUtils.FORMAT_SHOW_WEEKDAY));
+        DateUtils.formatDateTime(
+            context, timestamp, DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY));
   }
 
   /**
-   * Formats the provided date into the month abbreviation and day.
+   * Formats the provided timestamp (in milliseconds) into the month abbreviation, day, and
+   * optionally, year.
    *
-   * <p>For example, returns a string like "Jan 15".
+   * <p>For example, returns a string like "Jan 15" or "Jan 15, 2018".
    *
    * <p>For pre-N devices, the returned value may not start with a capital if the local convention
    * is to not capitalize day names. On N+ devices, the returned value is always capitalized.
    */
-  private static CharSequence formatAbbreviatedMonthAndDay(Context context, long callDateMillis) {
-    return toTitleCase(
-        DateUtils.formatDateTime(
-            context, callDateMillis, DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_NO_YEAR));
+  private static CharSequence formatAbbreviatedDate(
+      Context context, long timestamp, boolean showYear) {
+    int flags = DateUtils.FORMAT_ABBREV_MONTH;
+    if (!showYear) {
+      flags |= DateUtils.FORMAT_NO_YEAR;
+    }
+
+    return toTitleCase(DateUtils.formatDateTime(context, timestamp, flags));
   }
 
   private static CharSequence toTitleCase(CharSequence value) {
@@ -146,7 +175,7 @@
    * </ul>
    */
   public static int getDayDifference(long firstTimestamp, long secondTimestamp) {
-    // Ensure secondMillis is no less than firstMillis
+    // Ensure secondTimestamp is no less than firstTimestamp
     if (secondTimestamp < firstTimestamp) {
       long t = firstTimestamp;
       firstTimestamp = secondTimestamp;
@@ -178,16 +207,36 @@
     return dayDifference;
   }
 
-  /** Returns true if the provided timestamps are from the same day in the default time zone. */
-  public static boolean isSameDay(long firstMillis, long secondMillis) {
-    Calendar first = Calendar.getInstance();
-    first.setTimeInMillis(firstMillis);
+  /**
+   * Returns true if the two timestamps are within one year. It is the caller's responsibility to
+   * ensure both timestamps are in milliseconds. Failure to do so will result in undefined behavior.
+   *
+   * <p>Note that the difference is based on 365/366-day periods.
+   *
+   * <p>Examples:
+   *
+   * <ul>
+   *   <li>01/01/2018 00:00 and 12/31/2018 23:59 is within one year.
+   *   <li>12/31/2017 23:59 and 12/31/2018 23:59 is not within one year.
+   *   <li>12/31/2017 23:59 and 01/01/2018 00:00 is within one year.
+   * </ul>
+   */
+  private static boolean isWithinOneYear(long firstTimestamp, long secondTimestamp) {
+    // Ensure secondTimestamp is no less than firstTimestamp
+    if (secondTimestamp < firstTimestamp) {
+      long t = firstTimestamp;
+      firstTimestamp = secondTimestamp;
+      secondTimestamp = t;
+    }
 
-    Calendar second = Calendar.getInstance();
-    second.setTimeInMillis(secondMillis);
+    // Use secondTimestamp as reference
+    Calendar reference = Calendar.getInstance();
+    reference.setTimeInMillis(secondTimestamp);
+    reference.add(Calendar.YEAR, -1);
 
-    return first.get(Calendar.YEAR) == second.get(Calendar.YEAR)
-        && first.get(Calendar.MONTH) == second.get(Calendar.MONTH)
-        && first.get(Calendar.DAY_OF_MONTH) == second.get(Calendar.DAY_OF_MONTH);
+    Calendar other = Calendar.getInstance();
+    other.setTimeInMillis(firstTimestamp);
+
+    return reference.before(other);
   }
 }
diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java
index 1df4458..25fe864 100644
--- a/java/com/android/dialer/calllogutils/CallLogEntryText.java
+++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java
@@ -49,22 +49,25 @@
     return primaryText.toString();
   }
 
-  /** The secondary text to show in the main call log entry list. */
+  /**
+   * The secondary text to show in the main call log entry list.
+   *
+   * <p>Rules: (Duo video, )?$Label|$Location • Date
+   *
+   * <p>Examples:
+   *
+   * <ul>
+   *   <li>Duo Video, Mobile • Now
+   *   <li>Duo Video • 10 min. ago
+   *   <li>Mobile • 11:45 PM
+   *   <li>Mobile • Sun
+   *   <li>Brooklyn, NJ • Jan 15
+   * </ul>
+   *
+   * <p>See {@link CallLogDates#newCallLogTimestampLabel(Context, long, long)} for date rules.
+   */
   public static CharSequence buildSecondaryTextForEntries(
       Context context, Clock clock, CoalescedRow row) {
-    /*
-     * Rules: (Duo video, )?$Label|$Location • Date
-     *
-     * Examples:
-     *   Duo Video, Mobile • Now
-     *   Duo Video • 11:45pm
-     *   Mobile • 11:45pm
-     *   Mobile • Sunday
-     *   Brooklyn, NJ • Jan 15
-     *
-     * Date rules:
-     *   if < 1 minute ago: "Now"; else if today: HH:MM(am|pm); else if < 3 days: day; else: MON D
-     */
     StringBuilder secondaryText = secondaryTextPrefix(context, row);
 
     if (secondaryText.length() > 0) {
diff --git a/java/com/android/dialer/calllogutils/res/values/strings.xml b/java/com/android/dialer/calllogutils/res/values/strings.xml
index b8ba5b1..8784bf8 100644
--- a/java/com/android/dialer/calllogutils/res/values/strings.xml
+++ b/java/com/android/dialer/calllogutils/res/values/strings.xml
@@ -129,7 +129,7 @@
   <string name="voicemail_string">Voicemail</string>
 
   <!-- String to be displayed to indicate in the call log that a call just now occurred. -->
-  <string name="now">Now</string>
+  <string name="just_now">Just now</string>
 
   <!-- Text to show in call log for a video call. [CHAR LIMIT=16] -->
   <string name="new_call_log_video">Video</string>
diff --git a/java/com/android/dialer/simulator/SimulatorComponent.java b/java/com/android/dialer/simulator/SimulatorComponent.java
index dee1882..6fa3f0c 100644
--- a/java/com/android/dialer/simulator/SimulatorComponent.java
+++ b/java/com/android/dialer/simulator/SimulatorComponent.java
@@ -26,6 +26,8 @@
 
   public abstract Simulator getSimulator();
 
+  public abstract SimulatorEnrichedCall getSimulatorEnrichedCall();
+
   public abstract SimulatorConnectionsBank getSimulatorConnectionsBank();
 
   public static SimulatorComponent get(Context context) {
diff --git a/java/com/android/dialer/simulator/SimulatorEnrichedCall.java b/java/com/android/dialer/simulator/SimulatorEnrichedCall.java
new file mode 100644
index 0000000..f6c8a6c
--- /dev/null
+++ b/java/com/android/dialer/simulator/SimulatorEnrichedCall.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.dialer.simulator;
+
+import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener;
+
+/** Setup enriched calling environment for {@link Simulator}. */
+public interface SimulatorEnrichedCall extends StateChangedListener {
+  /** Setup a session for an incoming enriched call. */
+  long setupIncomingEnrichedCall(String number);
+
+  /** Setup a session for outgoing enriched call. */
+  long setupOutgoingEnrichedCall(String number);
+}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorImpl.java b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
index 41c234b..be86763 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorImpl.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
@@ -25,6 +25,7 @@
 
 /** The entry point for the simulator feature. */
 final class SimulatorImpl implements Simulator {
+  
   @Inject
   public SimulatorImpl() {}
 
diff --git a/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java b/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java
new file mode 100644
index 0000000..056722f
--- /dev/null
+++ b/java/com/android/dialer/simulator/stub/SimulatorEnrichedCallStub.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.dialer.simulator.stub;
+
+import com.android.dialer.simulator.SimulatorEnrichedCall;
+import javax.inject.Inject;
+
+/** Stub implementation of {@link SimulatorEnrichedCall}. */
+public class SimulatorEnrichedCallStub implements SimulatorEnrichedCall {
+
+  @Inject
+  public SimulatorEnrichedCallStub() {}
+
+  @Override
+  public long setupIncomingEnrichedCall(String number) {
+    return -1;
+  }
+
+  @Override
+  public long setupOutgoingEnrichedCall(String number) {
+    return -1;
+  }
+
+  @Override
+  public void onEnrichedCallStateChanged() {}
+}
diff --git a/java/com/android/dialer/simulator/stub/StubSimulatorEnrichedCallModule.java b/java/com/android/dialer/simulator/stub/StubSimulatorEnrichedCallModule.java
new file mode 100644
index 0000000..36314e7
--- /dev/null
+++ b/java/com/android/dialer/simulator/stub/StubSimulatorEnrichedCallModule.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.dialer.simulator.stub;
+
+import com.android.dialer.simulator.SimulatorEnrichedCall;
+import dagger.Binds;
+import dagger.Module;
+import javax.inject.Singleton;
+
+/** Provides a stub instance of SimulatorEnrichedCall. */
+@Module
+public abstract class StubSimulatorEnrichedCallModule {
+  @Binds
+  @Singleton
+  public abstract SimulatorEnrichedCall bindsSimulatorEnrichedCall(
+      SimulatorEnrichedCallStub simulatorEnrichedCall);
+}
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
index 86d3860..c9bf6e1 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
@@ -104,7 +104,7 @@
   private final NewVoicemailMediaPlayer mediaPlayer =
       new NewVoicemailMediaPlayer(new MediaPlayer());
 
-  /** @param cursor whose projection is {@link VoicemailCursorLoader.VOICEMAIL_COLUMNS} */
+  /** @param cursor whose projection is {@link VoicemailCursorLoader#VOICEMAIL_COLUMNS} */
   NewVoicemailAdapter(Cursor cursor, Clock clock, FragmentManager fragmentManager) {
     LogUtil.enterBlock("NewVoicemailAdapter");
     this.cursor = cursor;
@@ -133,14 +133,14 @@
     long currentTimeMillis = clock.currentTimeMillis();
     if (cursor.moveToNext()) {
       long firstTimestamp = VoicemailCursorLoader.getTimestamp(cursor);
-      if (CallLogDates.isSameDay(currentTimeMillis, firstTimestamp)) {
+      if (CallLogDates.getDayDifference(currentTimeMillis, firstTimestamp) == 0) {
         this.todayHeaderPosition = 0 + alertOffSet;
         int adapterPosition =
             2 + alertOffSet; // Accounted for the "Alert", "Today" header and first row.
         while (cursor.moveToNext()) {
           long timestamp = VoicemailCursorLoader.getTimestamp(cursor);
 
-          if (CallLogDates.isSameDay(currentTimeMillis, timestamp)) {
+          if (CallLogDates.getDayDifference(currentTimeMillis, timestamp) == 0) {
             adapterPosition++;
           } else {
             this.olderHeaderPosition = adapterPosition;