Show voicemail error messages for NUI

This CL shows the voicemail error messages for both VVM and OMTP type voicemails. It hooks upto the existing error framework and retrieves the voicemail error states and then displays it.

Bug: 71700117
Test: Unit Tests
PiperOrigin-RevId: 183301455
Change-Id: Ib2b7fb957fbfd2af2227747d327cef466259eb6f
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
index 5519aa4..318f797 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
 import android.support.annotation.WorkerThread;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -43,9 +44,15 @@
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.time.Clock;
 import com.android.dialer.voicemail.listui.NewVoicemailViewHolder.NewVoicemailViewHolderListener;
+import com.android.dialer.voicemail.listui.error.VoicemailErrorMessage;
+import com.android.dialer.voicemail.listui.error.VoicemailErrorMessageCreator;
+import com.android.dialer.voicemail.listui.error.VoicemailStatus;
 import com.android.dialer.voicemail.model.VoicemailEntry;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
 
@@ -66,6 +73,7 @@
   }
 
   private Cursor cursor;
+  private Cursor voicemailStatusCursor;
   private final Clock clock;
 
   /** {@link Integer#MAX_VALUE} when the "Today" header should not be displayed. */
@@ -81,6 +89,8 @@
   /** A valid id for {@link VoicemailEntry} is greater than 0 */
   private int currentlyExpandedViewHolderId = -1;
 
+  private VoicemailErrorMessage voicemailErrorMessage;
+
   /**
    * It takes time to delete voicemails from the server, so we "remove" them and remember the
    * positions we removed until a new cursor is ready.
@@ -248,34 +258,14 @@
     if (viewHolder instanceof NewVoicemailHeaderViewHolder) {
       LogUtil.i(
           "NewVoicemailAdapter.onBindViewHolder", "view holder at pos:%d is a header", position);
-      NewVoicemailHeaderViewHolder headerViewHolder = (NewVoicemailHeaderViewHolder) viewHolder;
-      @RowType int viewType = getItemViewType(position);
-      if (position == todayHeaderPosition) {
-        headerViewHolder.setHeader(R.string.new_voicemail_header_today);
-      } else if (position == yesterdayHeaderPosition) {
-        headerViewHolder.setHeader(R.string.new_voicemail_header_yesterday);
-      } else if (position == olderHeaderPosition) {
-        headerViewHolder.setHeader(R.string.new_voicemail_header_older);
-      } else {
-        throw Assert.createIllegalStateFailException(
-            "Unexpected view type " + viewType + " at position: " + position);
-      }
+      onBindHeaderViewHolder(viewHolder, position);
       return;
     }
 
     if (viewHolder instanceof NewVoicemailAlertViewHolder) {
       LogUtil.i(
           "NewVoicemailAdapter.onBindViewHolder", "view holder at pos:%d is a alert", position);
-      NewVoicemailAlertViewHolder alertViewHolder = (NewVoicemailAlertViewHolder) viewHolder;
-      @RowType int viewType = getItemViewType(position);
-      Assert.checkArgument(position == 0);
-      if (position == voicemailAlertPosition) {
-        // TODO(a bug): Update this with the alert messages
-        alertViewHolder.setHeader("Temporary placeholder, update this with the alert messages");
-      } else {
-        throw Assert.createIllegalStateFailException(
-            "Unexpected view type " + viewType + " at position: " + position);
-      }
+      onBindAlertViewHolder(viewHolder, position);
       return;
     }
 
@@ -285,26 +275,13 @@
         position);
 
     NewVoicemailViewHolder newVoicemailViewHolder = (NewVoicemailViewHolder) viewHolder;
-
-    int previousHeaders = 0;
-    if (voicemailAlertPosition != Integer.MAX_VALUE && position > voicemailAlertPosition) {
-      previousHeaders++;
-    }
-    if (todayHeaderPosition != Integer.MAX_VALUE && position > todayHeaderPosition) {
-      previousHeaders++;
-    }
-    if (yesterdayHeaderPosition != Integer.MAX_VALUE && position > yesterdayHeaderPosition) {
-      previousHeaders++;
-    }
-    if (olderHeaderPosition != Integer.MAX_VALUE && position > olderHeaderPosition) {
-      previousHeaders++;
-    }
+    int nonVoicemailEntryHeaders = getHeaderCountAtPosition(position);
 
     LogUtil.i(
         "NewVoicemailAdapter.onBindViewHolder",
-        "view holder at pos:%d, prevHeaderCount:%d",
+        "view holder at pos:%d, nonVoicemailEntryHeaders:%d",
         position,
-        previousHeaders);
+        nonVoicemailEntryHeaders);
 
     // Remove if the viewholder is being recycled.
     if (newVoicemailViewHolderArrayMap.containsKey(newVoicemailViewHolder.getViewHolderId())) {
@@ -322,7 +299,7 @@
     }
 
     newVoicemailViewHolder.reset();
-    cursor.moveToPosition(position - previousHeaders);
+    cursor.moveToPosition(position - nonVoicemailEntryHeaders);
     newVoicemailViewHolder.bindViewHolderValuesFromAdapter(
         cursor, fragmentManager, mediaPlayer, position, currentlyExpandedViewHolderId);
 
@@ -378,6 +355,72 @@
     printArrayMap();
   }
 
+  private int getHeaderCountAtPosition(int position) {
+    int previousHeaders = 0;
+    if (voicemailAlertPosition != Integer.MAX_VALUE && position > voicemailAlertPosition) {
+      previousHeaders++;
+    }
+    if (todayHeaderPosition != Integer.MAX_VALUE && position > todayHeaderPosition) {
+      previousHeaders++;
+    }
+    if (yesterdayHeaderPosition != Integer.MAX_VALUE && position > yesterdayHeaderPosition) {
+      previousHeaders++;
+    }
+    if (olderHeaderPosition != Integer.MAX_VALUE && position > olderHeaderPosition) {
+      previousHeaders++;
+    }
+    return previousHeaders;
+  }
+
+  private void onBindAlertViewHolder(ViewHolder viewHolder, int position) {
+    LogUtil.i(
+        "NewVoicemailAdapter.onBindAlertViewHolder",
+        "pos:%d, voicemailAlertPosition:%d",
+        position,
+        voicemailAlertPosition);
+
+    NewVoicemailAlertViewHolder alertViewHolder = (NewVoicemailAlertViewHolder) viewHolder;
+    @RowType int viewType = getItemViewType(position);
+
+    Assert.checkArgument(position == 0, "position is not 0");
+    Assert.checkArgument(
+        position == voicemailAlertPosition,
+        String.format(
+            Locale.US,
+            "position:%d and voicemailAlertPosition:%d are different",
+            position,
+            voicemailAlertPosition));
+    Assert.checkArgument(viewType == RowType.VOICEMAIL_ALERT, "Invalid row type: " + viewType);
+    Assert.checkArgument(
+        voicemailErrorMessage.getActions().size() <= 2,
+        "Too many actions: " + voicemailErrorMessage.getActions().size());
+
+    alertViewHolder.setTitle(voicemailErrorMessage.getTitle());
+    alertViewHolder.setDescription(voicemailErrorMessage.getDescription());
+
+    if (!voicemailErrorMessage.getActions().isEmpty()) {
+      alertViewHolder.setPrimaryButton(voicemailErrorMessage.getActions().get(0));
+    }
+    if (voicemailErrorMessage.getActions().size() > 1) {
+      alertViewHolder.setSecondaryButton(voicemailErrorMessage.getActions().get(1));
+    }
+  }
+
+  private void onBindHeaderViewHolder(ViewHolder viewHolder, int position) {
+    NewVoicemailHeaderViewHolder headerViewHolder = (NewVoicemailHeaderViewHolder) viewHolder;
+    @RowType int viewType = getItemViewType(position);
+    if (position == todayHeaderPosition) {
+      headerViewHolder.setHeader(R.string.new_voicemail_header_today);
+    } else if (position == yesterdayHeaderPosition) {
+      headerViewHolder.setHeader(R.string.new_voicemail_header_yesterday);
+    } else if (position == olderHeaderPosition) {
+      headerViewHolder.setHeader(R.string.new_voicemail_header_older);
+    } else {
+      throw Assert.createIllegalStateFailException(
+          "Unexpected view type " + viewType + " at position: " + position);
+    }
+  }
+
   private void printArrayMap() {
     LogUtil.i(
         "NewVoicemailAdapter.printArrayMap",
@@ -958,4 +1001,47 @@
       LogUtil.i("NewVoicemailAdapter.checkAndPlayVoicemail", "not playing downloaded voicemail");
     }
   }
+
+  @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+  public void setVoicemailStatusCursor(Cursor voicemailStatusCursor) {
+    this.voicemailStatusCursor = voicemailStatusCursor;
+  }
+
+  // TODO(uabdullah): Handle ToS properly
+  public void updateAlert(Context context) {
+    if (voicemailStatusCursor == null) {
+      LogUtil.i("NewVoicemailAdapter.updateAlert", "status cursor was null");
+      return;
+    }
+
+    LogUtil.i(
+        "NewVoicemailAdapter.updateAlert",
+        "status cursor size was " + voicemailStatusCursor.getCount());
+
+    List<VoicemailStatus> statuses = new ArrayList<>();
+
+    while (voicemailStatusCursor.moveToNext()) {
+      VoicemailStatus status = new VoicemailStatus(context, voicemailStatusCursor);
+      if (status.isActive()) {
+        statuses.add(status);
+        // TODO(uabdullah): addServiceStateListener
+      }
+    }
+
+    voicemailErrorMessage = null;
+    VoicemailErrorMessageCreator messageCreator = new VoicemailErrorMessageCreator();
+
+    for (VoicemailStatus status : statuses) {
+      voicemailErrorMessage = messageCreator.create(context, status, null);
+      if (voicemailErrorMessage != null) {
+        break;
+      }
+    }
+
+    if (voicemailErrorMessage != null) {
+      voicemailAlertPosition = 0;
+      updateHeaderPositions();
+      notifyItemChanged(0);
+    }
+  }
 }
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java
index ec603b5..ac989a8 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAlertViewHolder.java
@@ -18,19 +18,43 @@
 
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.view.View;
+import android.widget.Button;
 import android.widget.TextView;
+import com.android.dialer.voicemail.listui.error.VoicemailErrorMessage.Action;
 
 /** ViewHolder for {@link NewVoicemailAdapter} to display voicemail error states. */
 final class NewVoicemailAlertViewHolder extends ViewHolder {
 
-  private final TextView errorTextView;
+  private final TextView voicemailErrorTitleTextView;
+  private final TextView voicemailErrorDetailsTextView;
+  private final Button primaryButton;
+  private final Button secondaryButton;
 
   NewVoicemailAlertViewHolder(View view) {
     super(view);
-    errorTextView = view.findViewById(R.id.new_voicemail_alert_text);
+    voicemailErrorTitleTextView = view.findViewById(R.id.voicemail_alert_header);
+    voicemailErrorDetailsTextView = view.findViewById(R.id.voicemail_alert_details);
+    primaryButton = view.findViewById(R.id.voicemail_alert_primary_button);
+    secondaryButton = view.findViewById(R.id.voicemail_alert_primary_button);
   }
 
-  void setHeader(String error) {
-    errorTextView.setText(error);
+  void setTitle(CharSequence error) {
+    voicemailErrorTitleTextView.setText(error);
+  }
+
+  void setDescription(CharSequence error) {
+    voicemailErrorDetailsTextView.setText(error);
+  }
+
+  void setPrimaryButton(Action action) {
+    primaryButton.setVisibility(View.VISIBLE);
+    primaryButton.setText(action.getText());
+    primaryButton.setOnClickListener(action.getListener());
+  }
+
+  void setSecondaryButton(Action action) {
+    secondaryButton.setVisibility(View.VISIBLE);
+    secondaryButton.setText(action.getText());
+    secondaryButton.setOnClickListener(action.getListener());
   }
 }
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
index b4be424..0d91f01 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
@@ -28,11 +28,15 @@
 import android.view.View;
 import android.view.ViewGroup;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.CallLogQueryHandler;
+import com.android.dialer.database.CallLogQueryHandler.Listener;
 
+// TODO(uabdullah): Register content observer for VoicemailContract.Status.CONTENT_URI in onStart
 /** Fragment for Dialer Voicemail Tab. */
 public final class NewVoicemailFragment extends Fragment implements LoaderCallbacks<Cursor> {
 
   private RecyclerView recyclerView;
+  private CallLogQueryHandler callLogQueryHandler;
 
   @Nullable
   @Override
@@ -72,6 +76,31 @@
       ((NewVoicemailAdapter) recyclerView.getAdapter()).updateCursor(data);
       ((NewVoicemailAdapter) recyclerView.getAdapter()).checkAndPlayVoicemail();
     }
+    callLogQueryHandler =
+        new CallLogQueryHandler(
+            getContext(), getContext().getContentResolver(), new NewVoicemailFragmentListener());
+    callLogQueryHandler.fetchVoicemailStatus();
+  }
+
+  private final class NewVoicemailFragmentListener implements Listener {
+
+    @Override
+    public void onVoicemailStatusFetched(Cursor statusCursor) {
+      LogUtil.enterBlock("NewVoicemailFragmentListener.onVoicemailStatusFetched");
+      ((NewVoicemailAdapter) recyclerView.getAdapter()).setVoicemailStatusCursor(statusCursor);
+      ((NewVoicemailAdapter) recyclerView.getAdapter()).updateAlert(getContext());
+    }
+
+    @Override
+    public void onVoicemailUnreadCountFetched(Cursor cursor) {}
+
+    @Override
+    public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
+
+    @Override
+    public boolean onCallsFetched(Cursor combinedCursor) {
+      return false;
+    }
   }
 
   @Override
diff --git a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml
index e8dcd02..28d6391 100644
--- a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml
+++ b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_entry_alert.xml
@@ -14,17 +14,96 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<RelativeLayout
+
+<!-- TODO(uabdullah): Use a relative layout instead of nested linear layouts.-->
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="48dp"
+    android:id="@+id/voicemail_alert_content"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-  <!-- TODO(uabdullah): Confirm with UX on mocks -->
-  <TextView
-      android:id="@+id/new_voicemail_alert_text"
-      style="@style/SecondaryText"
-      android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+  <LinearLayout
+      android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:layout_marginStart="@dimen/voicemail_header_margin_start"
-      android:layout_centerVertical="true"/>
-</RelativeLayout>
+      android:paddingTop="@dimen/alert_main_padding"
+      android:paddingBottom="@dimen/alert_main_padding"
+      android:paddingStart="@dimen/alert_main_padding"
+      android:paddingEnd="@dimen/alert_main_padding"
+      android:gravity="top"
+      android:orientation="horizontal">
+
+    <ImageView
+        android:id="@+id/voicemail_alert_icon"
+        android:layout_width="@dimen/voicemail_promo_card_icon_size"
+        android:layout_height="@dimen/voicemail_promo_card_icon_size"
+        android:layout_gravity="top"
+        android:src="@drawable/ic_voicemail_error_24px"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/voicemail_promo_card_main_padding"
+        android:gravity="center_vertical"
+        android:orientation="vertical">
+
+      <TextView
+          android:id="@+id/voicemail_alert_header"
+          android:textStyle="bold"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_marginBottom="@dimen/voicemail_promo_card_title_padding"
+          android:layout_gravity="center_vertical"
+          android:singleLine="false"
+          android:text="Voicemail Alert"
+          android:textSize="@dimen/voicemail_promo_card_title_text_size"/>
+
+      <TextView
+          android:id="@+id/voicemail_alert_details"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:lineSpacingExtra="@dimen/voicemail_promo_card_line_spacing"
+          android:singleLine="false"
+          android:text="This is a voicemail alert message."
+          android:textSize="@dimen/voicemail_promo_card_message_size"/>
+    </LinearLayout>
+  </LinearLayout>
+
+  <LinearLayout
+      android:id="@+id/voicemail_alert_button"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_gravity="end"
+      android:paddingTop="10dp"
+      android:paddingBottom="10dp"
+      android:paddingStart="16dp"
+      android:paddingEnd="16dp"
+      android:gravity="end"
+      android:minHeight="56dp"
+      android:orientation="horizontal">
+    <Button
+        android:id="@+id/voicemail_alert_secondary_button"
+        style="@style/TosButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/dialer_theme_color"/>
+
+    <Button
+        android:id="@+id/voicemail_alert_primary_button"
+        style="@style/TosButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/dialer_theme_color"/>
+  </LinearLayout>
+
+  <LinearLayout
+      android:layout_width="0dip"
+      android:layout_height="match_parent"
+      android:layout_weight="1"
+      android:layout_gravity="center"
+      android:divider="?android:dividerHorizontal"
+      android:gravity="center"
+      android:orientation="vertical"
+      android:showDividers="middle">
+  </LinearLayout>
+</LinearLayout>
diff --git a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
index 59da7f2..960d327 100644
--- a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
+++ b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
@@ -41,4 +41,13 @@
 
   <dimen name="voicemail_tos_image_size">280dp</dimen>
 
+  <!-- Dimensions for voicemail alert -->
+  <dimen name="alert_main_padding">24dp</dimen>
+  <dimen name="voicemail_promo_card_icon_size">24dp</dimen>
+  <dimen name="voicemail_promo_card_main_padding">24dp</dimen>
+  <dimen name="voicemail_promo_card_title_padding">12dp</dimen>
+  <dimen name="voicemail_promo_card_title_text_size">16sp</dimen>
+  <dimen name="voicemail_promo_card_line_spacing">4dp</dimen>
+  <dimen name="voicemail_promo_card_message_size">14sp</dimen>
+
 </resources>