Merge "Implement non-ui logic for delete/undo delete voicemails"
diff --git a/java/com/android/dialer/app/res/values/strings.xml b/java/com/android/dialer/app/res/values/strings.xml
index e8377ec..047f9e3 100644
--- a/java/com/android/dialer/app/res/values/strings.xml
+++ b/java/com/android/dialer/app/res/values/strings.xml
@@ -51,9 +51,6 @@
   <!-- Text for snackbar to undo a voicemail delete. [CHAR LIMIT=30] -->
   <string name="snackbar_voicemail_deleted">Voicemail deleted</string>
 
-  <!-- Text for undo button in snackbar for voicemail deletion. [CHAR LIMIT=10] -->
-  <string name="snackbar_voicemail_deleted_undo">UNDO</string>
-
   <!-- Title of the confirmation dialog for clearing the call log. [CHAR LIMIT=37]  -->
   <string name="clearCallLogConfirmation_title">Clear call history?</string>
 
diff --git a/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java b/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
index 010f55f..05da8e7 100644
--- a/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
+++ b/java/com/android/dialer/app/voicemail/VoicemailPlaybackLayout.java
@@ -114,7 +114,7 @@
                   Snackbar.LENGTH_LONG)
               .setDuration(VOICEMAIL_DELETE_DELAY_MS)
               .setAction(
-                  R.string.snackbar_voicemail_deleted_undo,
+                  R.string.snackbar_undo,
                   new View.OnClickListener() {
                     @Override
                     public void onClick(View view) {
diff --git a/java/com/android/dialer/blocking/BlockNumberDialogFragment.java b/java/com/android/dialer/blocking/BlockNumberDialogFragment.java
index f723a9c..dfd6ef0 100644
--- a/java/com/android/dialer/blocking/BlockNumberDialogFragment.java
+++ b/java/com/android/dialer/blocking/BlockNumberDialogFragment.java
@@ -244,7 +244,7 @@
                 };
 
             Snackbar.make(parentView, message, Snackbar.LENGTH_LONG)
-                .setAction(R.string.block_number_undo, undoListener)
+                .setAction(R.string.snackbar_undo, undoListener)
                 .setActionTextColor(actionTextColor)
                 .show();
 
@@ -295,7 +295,7 @@
                 };
 
             Snackbar.make(parentView, message, Snackbar.LENGTH_LONG)
-                .setAction(R.string.block_number_undo, undoListener)
+                .setAction(R.string.snackbar_undo, undoListener)
                 .setActionTextColor(actionTextColor)
                 .show();
 
diff --git a/java/com/android/dialer/blocking/res/values/strings.xml b/java/com/android/dialer/blocking/res/values/strings.xml
index a660731..7b344da 100644
--- a/java/com/android/dialer/blocking/res/values/strings.xml
+++ b/java/com/android/dialer/blocking/res/values/strings.xml
@@ -78,9 +78,6 @@
     <xliff:g example="(555) 555-5555" id="number">%1$s</xliff:g>unblocked
   </string>
 
-  <!-- Text for undo button in snackbar for blocking/unblocking number. [CHAR LIMIT=10] -->
-  <string name="block_number_undo">UNDO</string>
-
   <!-- Error toast message for when send to voicemail import fails. [CHAR LIMIT=40] -->
   <string name="send_to_voicemail_import_failed">Import failed</string>
 
diff --git a/java/com/android/dialer/common/res/values/strings.xml b/java/com/android/dialer/common/res/values/strings.xml
index 53a2b56..cc0594d 100644
--- a/java/com/android/dialer/common/res/values/strings.xml
+++ b/java/com/android/dialer/common/res/values/strings.xml
@@ -19,4 +19,6 @@
   <string name="network_name_mobile">Mobile</string>
   <!-- Content description for the overflow menu button. [CHAR LIMIT=NONE] -->
   <string name="content_description_overflow">More options</string>
+  <!-- Text for undo button in snackbar for voicemail deletion/blocking/unblocking number. [CHAR LIMIT=10] -->
+  <string name="snackbar_undo">UNDO</string>
 </resources>
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
index 07a37c3..5eeb0fb 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java
@@ -29,6 +29,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.WorkerThread;
+import android.support.design.widget.Snackbar;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
 import android.util.ArrayMap;
@@ -36,11 +37,11 @@
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import com.android.dialer.calllogutils.CallLogDates;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
-import com.android.dialer.common.concurrent.DialerExecutor.Worker;
 import com.android.dialer.common.concurrent.DialerExecutorComponent;
 import com.android.dialer.common.concurrent.ThreadUtil;
 import com.android.dialer.glidephotomanager.GlidePhotoManager;
@@ -62,6 +63,8 @@
 final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder>
     implements NewVoicemailViewHolderListener {
 
+  private static final int VOICEMAIL_DELETE_DELAY_MS = 3000;
+
   /** IntDef for the different types of rows that can be shown in the call log. */
   @Retention(RetentionPolicy.SOURCE)
   @IntDef({RowType.HEADER, RowType.VOICEMAIL_ENTRY, RowType.VOICEMAIL_ALERT})
@@ -640,37 +643,107 @@
 
     collapseExpandedViewHolder(expandedViewHolder);
 
-    Worker<Pair<Context, Uri>, Void> deleteVoicemail = this::deleteVoicemail;
-
-    DialerExecutorComponent.get(context)
-        .dialerExecutorFactory()
-        .createNonUiTaskBuilder(deleteVoicemail)
-        .build()
-        .executeSerial(new Pair<>(context, voicemailUri));
-
-    notifyItemRemoved(expandedViewHolder.getAdapterPosition());
+    showUndoSnackbar(
+        context,
+        expandedViewHolder.getMediaPlayerView(),
+        expandedViewHolder.getAdapterPosition(),
+        voicemailUri);
   }
 
-  @WorkerThread
-  private Void deleteVoicemail(Pair<Context, Uri> contextUriPair) {
-    Assert.isWorkerThread();
-    LogUtil.enterBlock("NewVoicemailAdapter.deleteVoicemail");
+  private void showUndoSnackbar(
+      Context context, View newVoicemailMediaPlayerView, int position, Uri voicemailUri) {
+    LogUtil.i(
+        "NewVoicemailAdapter.showUndoSnackbar",
+        "position:%d and uri:%s",
+        position,
+        String.valueOf(voicemailUri));
+    Snackbar undoSnackbar =
+        Snackbar.make(
+            newVoicemailMediaPlayerView,
+            R.string.snackbar_voicemail_deleted,
+            VOICEMAIL_DELETE_DELAY_MS);
+    undoSnackbar.addCallback(
+        new Snackbar.Callback() {
+          @Override
+          public void onShown(Snackbar sb) {
+            notifyItemRemoved(position);
+            LogUtil.i(
+                "NewVoicemailAdapter.showUndoSnackbar",
+                "onShown for position:%d and uri:%s",
+                position,
+                voicemailUri);
+            super.onShown(sb);
+          }
 
-    Context context = contextUriPair.first;
-    Uri uri = contextUriPair.second;
-    LogUtil.i("NewVoicemailAdapter.deleteVoicemail", "deleting uri:%s", String.valueOf(uri));
-    ContentValues values = new ContentValues();
-    values.put(Voicemails.DELETED, "1");
+          @Override
+          public void onDismissed(Snackbar transientBottomBar, int event) {
+            LogUtil.i(
+                "NewVoicemailAdapter.showUndoSnackbar",
+                "onDismissed for event:%d, position:%d and uri:%s",
+                event,
+                position,
+                String.valueOf(voicemailUri));
 
-    int numRowsUpdated = context.getContentResolver().update(uri, values, null, null);
+            switch (event) {
+              case DISMISS_EVENT_SWIPE:
+              case DISMISS_EVENT_ACTION:
+              case DISMISS_EVENT_MANUAL:
+                LogUtil.i(
+                    "NewVoicemailAdapter.showUndoSnackbar",
+                    "Not proceeding with deleting the voicemail");
+                deletedVoicemailPosition.remove(position);
+                notifyItemChanged(position);
+                break;
+              case DISMISS_EVENT_TIMEOUT:
+              case DISMISS_EVENT_CONSECUTIVE:
+                LogUtil.i(
+                    "NewVoicemailAdapter.showUndoSnackbar", "Proceeding with deleting voicemail");
 
-    LogUtil.i("NewVoicemailAdapter.onVoicemailDeleted", "return value:%d", numRowsUpdated);
-    Assert.checkArgument(numRowsUpdated == 1, "voicemail delete was not successful");
+                DialerExecutorComponent.get(context)
+                    .dialerExecutorFactory()
+                    .createNonUiTaskBuilder(this::deleteVoicemail)
+                    .build()
+                    .executeSerial(new Pair<>(context, voicemailUri));
+                break;
+              default:
+                Assert.checkArgument(event <= 4 && event >= 0, "unknown event");
+            }
+          }
 
-    Intent intent = new Intent(VoicemailClient.ACTION_UPLOAD);
-    intent.setPackage(context.getPackageName());
-    context.sendBroadcast(intent);
-    return null;
+          @WorkerThread
+          private Void deleteVoicemail(Pair<Context, Uri> contextUriPair) {
+            Assert.isWorkerThread();
+            Context context = contextUriPair.first;
+            Uri uri = contextUriPair.second;
+            LogUtil.i(
+                "NewVoicemailAdapter.deleteVoicemail", "deleting uri:%s", String.valueOf(uri));
+            ContentValues values = new ContentValues();
+            values.put(Voicemails.DELETED, "1");
+
+            int numRowsUpdated = context.getContentResolver().update(uri, values, null, null);
+
+            LogUtil.i("NewVoicemailAdapter.deleteVoicemail", "return value:%d", numRowsUpdated);
+            Assert.checkArgument(numRowsUpdated == 1, "voicemail delete was not successful");
+
+            Intent intent = new Intent(VoicemailClient.ACTION_UPLOAD);
+            intent.setPackage(context.getPackageName());
+            context.sendBroadcast(intent);
+            return null;
+          }
+        });
+
+    undoSnackbar
+        .setAction(
+            R.string.snackbar_undo,
+            new OnClickListener() {
+              @Override
+              public void onClick(View v) {
+                // does nothing, but needed for the undo button to show
+              }
+            })
+        .setActionTextColor(
+            context.getResources().getColor(R.color.dialer_snackbar_action_text_color))
+        .show();
   }
 
   /**
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
index 3a5e72b..72f0ab5 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
@@ -205,7 +205,7 @@
 
   /** Shows the view when there are no voicemails to be displayed * */
   private void showEmptyVoicemailFragmentView() {
-    LogUtil.enterBlock("NewVoicemailFragmentListener.showEmptyVoicemailFragmentView");
+    LogUtil.enterBlock("NewVoicemailFragment.showEmptyVoicemailFragmentView");
 
     showView(emptyContentView);
 
@@ -215,7 +215,7 @@
   }
 
   private void showView(View view) {
-    LogUtil.i("NewVoicemailFragmentListener.showView", "Showing view: " + view);
+    LogUtil.i("NewVoicemailFragment.showView", "Showing view: " + view);
     emptyContentView.setVisibility(view == emptyContentView ? View.VISIBLE : View.GONE);
     recyclerView.setVisibility(view == recyclerView ? View.VISIBLE : View.GONE);
   }
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java b/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java
index 1d42e64..1f85784 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java
@@ -94,6 +94,10 @@
     viewHolderVoicemailUri = null;
   }
 
+  public NewVoicemailMediaPlayerView getMediaPlayerView() {
+    return Assert.isNotNull(mediaPlayerView);
+  }
+
   /**
    * When the {@link RecyclerView} displays voicemail entries, it might recycle the views upon
    * scrolling. In that case we need to ensure that the member variables of this {@link
diff --git a/java/com/android/dialer/voicemail/listui/res/values/strings.xml b/java/com/android/dialer/voicemail/listui/res/values/strings.xml
index d12a71e..322912e 100644
--- a/java/com/android/dialer/voicemail/listui/res/values/strings.xml
+++ b/java/com/android/dialer/voicemail/listui/res/values/strings.xml
@@ -60,4 +60,7 @@
   <!-- Text displayed when the list of voicemails is empty [CHAR LIMIT=NONE] -->
   <string name="empty_voicemail_tab_text">Your voicemail inbox is empty.</string>
 
+  <!-- Text for snackbar to undo a voicemail delete. [CHAR LIMIT=30] -->
+  <string name="snackbar_voicemail_deleted">Voicemail deleted</string>
+
 </resources>