Show a dialog to discover the removal setting
The first time a user clicks "archive" or "delete", we will show a
dialog, asking if they want to see archive, delete, or both.
Bug: 9296856
Change-Id: Ibb88304eac66bb16a2f80622fb37cb9f57fcb6c6
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4c6ed01..20ffd5e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -860,6 +860,8 @@
<string name="prefDialogTitle_removal_action">Archive & delete actions</string>
<!-- The default value -->
<string translatable="false" name="prefDefault_removal_action">archive</string>
+ <!-- Dialog title for the choosing whether to use archive or delete as remove action, displayed the first time the user archives or deletes a message [CHAR LIMIT=150] -->
+ <string name="prefDialogTitle_removal_action_first_time">Set archive & delete preference\n(change in General settings)</string>
<!-- Settings screen, Reply to all default setting title [CHAR LIMIT=30] -->
<string name="preferences_default_reply_all_title">Reply all</string>
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 350088a..18bf7aa 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -276,7 +276,7 @@
private void destroy(int actionId, final Collection<Conversation> target,
final DestructiveAction action) {
LogUtils.i(LOG_TAG, "About to remove %d converations", target.size());
- mUpdater.delete(actionId, target, action, true);
+ mUpdater.delete(actionId, target, action, true, true /* allowDialog */);
}
/**
diff --git a/src/com/android/mail/preferences/MailPrefs.java b/src/com/android/mail/preferences/MailPrefs.java
index 9d580ec..461bc5d 100644
--- a/src/com/android/mail/preferences/MailPrefs.java
+++ b/src/com/android/mail/preferences/MailPrefs.java
@@ -58,11 +58,12 @@
*/
public static final String CONVERSATION_LIST_SWIPE = "conversation-list-swipe";
- /**
- * A string indicating the user's removal action preference.
- */
+ /** A string indicating the user's removal action preference. */
public static final String REMOVAL_ACTION = "removal-action";
+ /** A boolean indicating that the user has seen the removal action dialog. */
+ public static final String REMOVAL_ACTION_DIALOG_SHOWN = "removal-action-dialog-shown";
+
/** Hidden preference used to cache the active notification set */
private static final String CACHED_ACTIVE_NOTIFICATION_SET =
"cache-active-notification-set";
@@ -75,6 +76,7 @@
.add(DEFAULT_REPLY_ALL)
.add(CONVERSATION_LIST_SWIPE)
.add(REMOVAL_ACTION)
+ .add(REMOVAL_ACTION_DIALOG_SHOWN)
.build();
}
@@ -254,4 +256,17 @@
public void resetConversationPhotoTeaserAlreadyShown() {
getEditor().putBoolean(PreferenceKeys.CONVERSATION_PHOTO_TEASER_SHOWN, false).apply();
}
+
+ /**
+ * @return <code>true</code> if the removal action dialog has been shown to the user,
+ * <code>false</code> otherwise
+ */
+ public boolean hasRemovalActionDialogShown() {
+ return getSharedPreferences().getBoolean(PreferenceKeys.REMOVAL_ACTION_DIALOG_SHOWN, false);
+ }
+
+ public void setRemovalActionDialogShown() {
+ getEditor().putBoolean(PreferenceKeys.REMOVAL_ACTION_DIALOG_SHOWN, true).apply();
+ MailIntentService.broadcastBackupDataChanged(getContext());
+ }
}
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index 4b43c44..20eaf7e 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -91,6 +91,7 @@
import com.android.mail.providers.UIProvider.ConversationOperations;
import com.android.mail.providers.UIProvider.FolderCapabilities;
import com.android.mail.ui.ActionableToastBar.ActionClickedListener;
+import com.android.mail.ui.RemovalActionPreferenceDialogFragment.RemovalActionPreferenceDialogListener;
import com.android.mail.utils.ContentProviderTask;
import com.android.mail.utils.LogTag;
import com.android.mail.utils.LogUtils;
@@ -1170,7 +1171,8 @@
}
case R.id.remove_folder:
delete(R.id.remove_folder, target,
- getDeferredRemoveFolder(target, mFolder, true, isBatch, true), isBatch);
+ getDeferredRemoveFolder(target, mFolder, true, isBatch, true), isBatch,
+ true /* allowDialog */);
break;
case R.id.delete: {
final boolean showDialog = (settings != null && settings.confirmDelete);
@@ -1190,29 +1192,34 @@
case R.id.mark_not_important:
if (mFolder != null && mFolder.isImportantOnly()) {
delete(R.id.mark_not_important, target,
- getDeferredAction(R.id.mark_not_important, target, isBatch), isBatch);
+ getDeferredAction(R.id.mark_not_important, target, isBatch), isBatch,
+ true /* allowDialog */);
} else {
updateConversation(Conversation.listOf(mCurrentConversation),
ConversationColumns.PRIORITY, UIProvider.ConversationPriority.LOW);
}
break;
case R.id.mute:
- delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch), isBatch);
+ delete(R.id.mute, target, getDeferredAction(R.id.mute, target, isBatch), isBatch,
+ true /* allowDialog */);
break;
case R.id.report_spam:
delete(R.id.report_spam, target,
- getDeferredAction(R.id.report_spam, target, isBatch), isBatch);
+ getDeferredAction(R.id.report_spam, target, isBatch), isBatch,
+ true /* allowDialog */);
break;
case R.id.mark_not_spam:
// Currently, since spam messages are only shown in list with
// other spam messages,
// marking a message not as spam is a destructive action
delete(R.id.mark_not_spam, target,
- getDeferredAction(R.id.mark_not_spam, target, isBatch), isBatch);
+ getDeferredAction(R.id.mark_not_spam, target, isBatch), isBatch,
+ true /* allowDialog */);
break;
case R.id.report_phishing:
delete(R.id.report_phishing, target,
- getDeferredAction(R.id.report_phishing, target, isBatch), isBatch);
+ getDeferredAction(R.id.report_phishing, target, isBatch), isBatch,
+ true /* allowDialog */);
break;
case android.R.id.home:
onUpPressed();
@@ -1425,7 +1432,8 @@
// Conversations are neither marked read, nor viewed, and we don't want to show
// the next conversation.
LogUtils.d(LOG_TAG, ". . doing full mark unread");
- markConversationsRead(Collections.singletonList(conv), false, false, false);
+ markConversationsRead(Collections.singletonList(conv), false, false, false,
+ true /* allowDialog */);
} else {
if (LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG)) {
final ConversationInfo info = ConversationInfo.fromBlob(originalConversationInfo);
@@ -1480,28 +1488,28 @@
mConversationListLoadFinishedCallbacks.add(new LoadFinishedCallback() {
@Override
public void onLoadFinished() {
- markConversationsRead(targets, read, viewed, true);
+ markConversationsRead(targets, read, viewed, true, true /* allowDialog */);
}
});
} else {
// We want to show the next conversation if we are marking unread.
- markConversationsRead(targets, read, viewed, true);
+ markConversationsRead(targets, read, viewed, true, true /* allowDialog */);
}
}
private void markConversationsRead(final Collection<Conversation> targets, final boolean read,
- final boolean markViewed, final boolean showNext) {
+ final boolean markViewed, final boolean showNext, final boolean allowDialog) {
LogUtils.d(LOG_TAG, "performing markConversationsRead");
// Auto-advance if requested and the current conversation is being marked unread
if (showNext && !read) {
final Runnable operation = new Runnable() {
@Override
public void run() {
- markConversationsRead(targets, read, markViewed, showNext);
+ markConversationsRead(targets, read, markViewed, showNext, false);
}
};
- if (!showNextConversation(targets, operation)) {
+ if (!showNextConversation(targets, operation, allowDialog)) {
// This method will be called again if the user selects an autoadvance option
return;
}
@@ -1551,7 +1559,7 @@
*/
@Override
public void showNextConversation(final Collection<Conversation> target) {
- showNextConversation(target, null);
+ showNextConversation(target, null, true /* allowDialog */);
}
/**
@@ -1577,7 +1585,7 @@
* <code>true</code> otherwise.
*/
private boolean showNextConversation(final Collection<Conversation> target,
- final Runnable operation) {
+ final Runnable operation, final boolean allowDialog) {
final int viewMode = mViewMode.getMode();
final boolean currentConversationInView = (viewMode == ViewMode.CONVERSATION
|| viewMode == ViewMode.SEARCH_RESULTS_CONVERSATION)
@@ -1586,9 +1594,11 @@
if (currentConversationInView) {
final int autoAdvanceSetting = mAccount.settings.getAutoAdvanceSetting();
- if (autoAdvanceSetting == AutoAdvance.UNSET && mIsTablet) {
+ if (allowDialog && autoAdvanceSetting == AutoAdvance.UNSET && mIsTablet) {
displayAutoAdvanceDialogAndPerformAction(operation);
return false;
+ } else if (allowDialog && displayRemovalActionDialogAndPerformAction(operation)) {
+ return false;
} else {
// If we don't have one set, but we're here, just take the default
final int autoAdvance = (autoAdvanceSetting == AutoAdvance.UNSET) ?
@@ -1602,6 +1612,8 @@
showConversation(next);
return (mAutoAdvanceOp == null);
}
+ } else if (allowDialog && displayRemovalActionDialogAndPerformAction(operation)) {
+ return false;
}
return true;
@@ -1659,6 +1671,46 @@
.show();
}
+ private Runnable mRemovalActionDialogRunnable = null;
+
+ private void attachRemovalActionDialogListener() {
+ final RemovalActionPreferenceDialogFragment fragment =
+ (RemovalActionPreferenceDialogFragment) mActivity.getFragmentManager()
+ .findFragmentByTag(RemovalActionPreferenceDialogFragment.FRAGMENT_TAG);
+
+ if (fragment != null) {
+ fragment.setListener(mRemovalActionPreferenceDialogListener);
+ }
+ }
+
+ private final RemovalActionPreferenceDialogListener mRemovalActionPreferenceDialogListener =
+ new RemovalActionPreferenceDialogListener() {
+ @Override
+ public void onDismiss() {
+ if (mRemovalActionDialogRunnable != null) {
+ mRemovalActionDialogRunnable.run();
+ mRemovalActionDialogRunnable = null;
+ }
+ }
+ };
+
+ /**
+ * Displays a the removal action dialog, and when the user makes a selection, the preference is
+ * stored, and the specified operation is run.
+ *
+ * @return <code>true</code> if the dialog was shown, <code>false</code> otherwise
+ */
+ private boolean displayRemovalActionDialogAndPerformAction(final Runnable operation) {
+ final boolean shown = RemovalActionPreferenceDialogFragment.showIfNecessary(mContext,
+ mAccount, mActivity.getFragmentManager(), mRemovalActionPreferenceDialogListener);
+
+ if (shown) {
+ mRemovalActionDialogRunnable = operation;
+ }
+
+ return shown;
+ }
+
@Override
public void starMessage(ConversationMessage msg, boolean starred) {
if (msg.starred == starred) {
@@ -1728,13 +1780,14 @@
final ConfirmDialogFragment c = ConfirmDialogFragment.newInstance(message);
c.displayDialog(mActivity.getFragmentManager());
} else {
- delete(0, target, getDeferredAction(actionId, target, isBatch), isBatch);
+ delete(0, target, getDeferredAction(actionId, target, isBatch), isBatch,
+ true /* allowDialog */);
}
}
@Override
public void delete(final int actionId, final Collection<Conversation> target,
- final DestructiveAction action, final boolean isBatch) {
+ final DestructiveAction action, final boolean isBatch, final boolean allowDialog) {
// Order of events is critical! The Conversation View Fragment must be
// notified of the next conversation with showConversation(next) *before* the
// conversation list
@@ -1745,11 +1798,11 @@
final Runnable operation = new Runnable() {
@Override
public void run() {
- delete(actionId, target, action, isBatch);
+ delete(actionId, target, action, isBatch, false);
}
};
- if (!showNextConversation(target, operation)) {
+ if (!showNextConversation(target, operation, allowDialog)) {
// This method will be called again if the user selects an autoadvance option
return;
}
@@ -1811,6 +1864,7 @@
mSafeToModifyFragments = true;
attachEmptyFolderDialogFragmentListener();
+ attachRemovalActionDialogListener();
// Invalidating the options menu so that when we make changes in settings,
// the changes will always be updated in the action bar/options menu/
@@ -2685,7 +2739,7 @@
folderChange = getDeferredFolderChange(target, folderOps, isDestructive,
batch, showUndo, isMoveTo, actionFolder);
- delete(0, target, folderChange, batch);
+ delete(0, target, folderChange, batch, true /* allowDialog */);
} else {
folderChange = getFolderChange(target, folderOps, isDestructive,
batch, showUndo, false /* isMoveTo */, mFolder);
@@ -2964,7 +3018,7 @@
getFolderChange(conversations, dragDropOperations, isDestructive,
true /* isBatch */, true /* showUndo */, true /* isMoveTo */, folder);
if (isDestructive) {
- delete(0, conversations, action, true);
+ delete(0, conversations, action, true, true /* allowDialog */);
} else {
action.performAction();
}
@@ -3820,7 +3874,7 @@
mDialogListener = new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- delete(action, target, destructiveAction, isBatch);
+ delete(action, target, destructiveAction, isBatch, true /* allowDialog */);
// Afterwards, let's remove references to the listener and the action.
setListener(null, -1);
}
diff --git a/src/com/android/mail/ui/ConversationUpdater.java b/src/com/android/mail/ui/ConversationUpdater.java
index a63ffa8..4c363c0 100644
--- a/src/com/android/mail/ui/ConversationUpdater.java
+++ b/src/com/android/mail/ui/ConversationUpdater.java
@@ -82,10 +82,11 @@
* @param target the conversations to act upon.
* @param action to perform after the UI has been updated to remove the conversations
* @param isBatch true if this is a batch action, false otherwise.
+ * @param allowDialog <code>true</code> to allow dialogs to be displayed
*/
void delete(
int actionId, final Collection<Conversation> target, final DestructiveAction action,
- boolean isBatch);
+ boolean isBatch, boolean allowDialog);
/**
* Mark a number of conversations as read or unread.
diff --git a/src/com/android/mail/ui/RemovalActionPreferenceDialogFragment.java b/src/com/android/mail/ui/RemovalActionPreferenceDialogFragment.java
new file mode 100644
index 0000000..c39b5df
--- /dev/null
+++ b/src/com/android/mail/ui/RemovalActionPreferenceDialogFragment.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ * Licensed to 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.mail.ui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+
+import com.android.mail.R;
+import com.android.mail.preferences.MailPrefs;
+import com.android.mail.providers.Account;
+import com.android.mail.providers.UIProvider.AccountCapabilities;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This dialog is shown when a user first archives or deletes a message, and asks them what there
+ * preferred action is (archive, delete, or both).
+ */
+public class RemovalActionPreferenceDialogFragment extends DialogFragment {
+ public interface RemovalActionPreferenceDialogListener {
+ void onDismiss();
+ }
+
+ public static final String FRAGMENT_TAG = "ArchiveDeletePreferenceDialogFragment";
+
+ private static final String ARG_DEFAULT_VALUE = "defaultValue";
+
+ private String mDefaultValue;
+
+ private WeakReference<RemovalActionPreferenceDialogListener> mListener = null;
+
+ /**
+ * Create a new {@link ArchiveDeletePreferenceDialogFragment}.
+ *
+ * @param defaultValue the initial value to show as checked
+ */
+ public static RemovalActionPreferenceDialogFragment newInstance(final String defaultValue) {
+ final RemovalActionPreferenceDialogFragment fragment =
+ new RemovalActionPreferenceDialogFragment();
+
+ final Bundle args = new Bundle(1);
+ args.putString(ARG_DEFAULT_VALUE, defaultValue);
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ public void setListener(final RemovalActionPreferenceDialogListener listener) {
+ mListener = new WeakReference<RemovalActionPreferenceDialogListener>(listener);
+ }
+
+ @Override
+ public Dialog onCreateDialog(final Bundle savedInstanceState) {
+ final String[] entries = getResources().getStringArray(R.array.prefEntries_removal_action);
+ final String[] entryValues =
+ getResources().getStringArray(R.array.prefValues_removal_action);
+
+ final String defaultValue = getArguments().getString(ARG_DEFAULT_VALUE);
+
+ // Find the default value in the entryValues array
+ // If we can't find it, we end up on index 0, which is "archive"
+ int defaultItem;
+ for (defaultItem = entryValues.length - 1; defaultItem >= 0; defaultItem--) {
+ if (entryValues[defaultItem].equals(defaultValue)) {
+ break;
+ }
+ }
+
+ mDefaultValue = entryValues[defaultItem];
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.prefDialogTitle_removal_action_first_time)
+ .setSingleChoiceItems(entries, defaultItem, new OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ mDefaultValue = entryValues[which];
+ dismiss();
+ }
+ });
+
+ return builder.create();
+ }
+
+ @Override
+ public void onDismiss(final DialogInterface dialog) {
+ super.onDismiss(dialog);
+
+ // Use whatever was originally selected
+ saveRemovalAction(mDefaultValue);
+ }
+
+ /**
+ * Shows the dialog, if necessary.
+ *
+ * @param account The account this is being requested for
+ * @param mRemovalActionPreferenceDialogListener
+ * @param defaultValue The default removal action to show checked
+ * @return <code>true</code> if the dialog was shown, <code>false</code> otherwise
+ */
+ public static boolean showIfNecessary(final Context context, final Account account,
+ final FragmentManager fragmentManager,
+ final RemovalActionPreferenceDialogListener removalActionPreferenceDialogListener) {
+ final boolean supportsArchive = account.supportsCapability(AccountCapabilities.ARCHIVE);
+
+ if (shouldDisplayDialog(context, account, supportsArchive)) {
+ final String defaultValue = MailPrefs.get(context).getRemovalAction(supportsArchive);
+
+ final RemovalActionPreferenceDialogFragment fragment = newInstance(defaultValue);
+ fragment.setListener(removalActionPreferenceDialogListener);
+ fragment.show(fragmentManager, FRAGMENT_TAG);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether we should show the dialog. We show it if:
+ * <ol>
+ * <li>Archive is supported by the account</li>
+ * <li>We have not previously shown the dialog</li>
+ * </ol>
+ *
+ * @param account The account this is being requested for
+ * @param supportsArchive <code>true</code> if the current account supports
+ * archive, <code>false</code> otherwise
+ * @return <code>true</code> if the dialog needs to be displayed (because it
+ * hasn't been shown yet), <code>false</code> otherwise
+ */
+ private static boolean shouldDisplayDialog(final Context context, final Account account,
+ final boolean supportsArchive) {
+ return supportsArchive && !MailPrefs.get(context).hasRemovalActionDialogShown();
+ }
+
+ private void saveRemovalAction(final String removalAction) {
+ final MailPrefs mailPrefs = MailPrefs.get(getActivity());
+ mailPrefs.setRemovalAction(removalAction);
+ mailPrefs.setRemovalActionDialogShown();
+
+ if (mListener != null) {
+ final RemovalActionPreferenceDialogListener listener = mListener.get();
+ if (listener != null) {
+ listener.onDismiss();
+ }
+ }
+ }
+}