Tip for long press to enter CAB
Bug: 9859881, 9572851
Change-Id: I1603651084fb9ded479af69e17bd54cdab693c2e
diff --git a/proguard.flags b/proguard.flags
index 6bcae61..cb8d15f 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -70,3 +70,7 @@
-keepclasseswithmembers class com.android.mail.ui.ConversationPhotoTeaserView {
*** setAnimatedHeight(...);
}
+
+-keepclasseswithmembers class com.android.mail.ui.ConversationLongPressTipView {
+ *** setAnimatedHeight(...);
+}
diff --git a/res/layout/conversation_long_press_to_select_tip_view.xml b/res/layout/conversation_long_press_to_select_tip_view.xml
new file mode 100644
index 0000000..448ecee
--- /dev/null
+++ b/res/layout/conversation_long_press_to_select_tip_view.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2013 Google Inc. -->
+<com.android.mail.ui.ConversationLongPressTipView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/swiped_bg_color" >
+
+ <LinearLayout
+ android:id="@+id/swipeable_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/conversation_read_selector"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="12dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_weight="1"
+ android:duplicateParentState="true"
+ android:fontFamily="sans-serif-light"
+ android:text="@string/long_press_to_select_tip"
+ android:textColor="@color/teaser_main_text"
+ android:textSize="16sp" />
+
+ <View
+ android:id="@+id/dismiss_separator"
+ android:layout_width="1dip"
+ android:layout_height="match_parent"
+ android:background="@color/teaser_main_text"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp" />
+
+ <ImageButton
+ android:id="@+id/dismiss_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground"
+ android:clickable="true"
+ android:paddingLeft="20dip"
+ android:paddingStart="20dip"
+ android:paddingRight="28dip"
+ android:paddingEnd="28dip"
+ android:scaleType="center"
+ android:contentDescription="@string/dismiss_tip_hover_text"
+ android:src="@drawable/ic_cancel_holo_light" />
+ </LinearLayout>
+
+</com.android.mail.ui.ConversationLongPressTipView>
diff --git a/res/layout/conversation_photo_teaser_view.xml b/res/layout/conversation_photo_teaser_view.xml
index 46318f9..86eac00 100644
--- a/res/layout/conversation_photo_teaser_view.xml
+++ b/res/layout/conversation_photo_teaser_view.xml
@@ -54,9 +54,12 @@
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:paddingLeft="20dip"
+ android:paddingStart="20dip"
android:paddingRight="28dip"
+ android:paddingEnd="28dip"
android:scaleType="center"
+ android:contentDescription="@string/dismiss_tip_hover_text"
android:src="@drawable/ic_cancel_holo_light" />
</LinearLayout>
-</com.android.mail.ui.ConversationPhotoTeaserView>
\ No newline at end of file
+</com.android.mail.ui.ConversationPhotoTeaserView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 919eb0a..4b104e7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -941,6 +941,8 @@
<string name="conversation_photo_welcome_text">Touch a sender image to select that conversation.</string>
+ <string name="long_press_to_select_tip">Touch & hold to select one conversation, then touch to select more.</string>
+
<!-- Content description for the folder icon for nested folders. -->
<string name="folder_icon_desc">Folder icon</string>
@@ -965,4 +967,7 @@
<!-- Shown in the message ad header and in the list item for a message ad. [CHAR LIMIT=15]-->
<string name="ads">Ads</string>
+ <!-- Content description for the "X" image icon for dismissing a tip. This is used for spoken description of the icon when touch explore is enabled. [CHAR LIMIT=50] -->
+ <string name="dismiss_tip_hover_text">Dismiss tip</string>
+
</resources>
diff --git a/src/com/android/mail/preferences/MailPrefs.java b/src/com/android/mail/preferences/MailPrefs.java
index 269c7b4..59bf8b7 100644
--- a/src/com/android/mail/preferences/MailPrefs.java
+++ b/src/com/android/mail/preferences/MailPrefs.java
@@ -82,6 +82,9 @@
public static final String SHOW_SENDER_IMAGES = "conversation-list-sender-image";
+ public static final String
+ LONG_PRESS_TO_SELECT_TIP_SHOWN = "long-press-to-select-tip-shown";
+
public static final ImmutableSet<String> BACKUP_KEYS =
new ImmutableSet.Builder<String>()
.add(DEFAULT_REPLY_ALL)
@@ -90,6 +93,7 @@
.add(DISPLAY_IMAGES)
.add(DISPLAY_IMAGES_PATTERNS)
.add(SHOW_SENDER_IMAGES)
+ .add(LONG_PRESS_TO_SELECT_TIP_SHOWN)
.build();
}
@@ -248,7 +252,7 @@
}
/**
- * Returns whether the teaser has bee shown before
+ * Returns whether the teaser has been shown before
*/
public boolean isConversationPhotoTeaserAlreadyShown() {
return getSharedPreferences()
@@ -263,10 +267,18 @@
}
/**
- * Reset the flag so that next time, the teaser will be shown again
+ * Returns whether the tip has been shown before
*/
- public void resetConversationPhotoTeaserAlreadyShown() {
- getEditor().putBoolean(PreferenceKeys.CONVERSATION_PHOTO_TEASER_SHOWN, false).apply();
+ public boolean isLongPressToSelectTipAlreadyShown() {
+ // Using an int instead of boolean here in case we need to reshow the tip (don't have
+ // to use a new preference name).
+ return getSharedPreferences()
+ .getInt(PreferenceKeys.LONG_PRESS_TO_SELECT_TIP_SHOWN, 0) > 1;
+ }
+
+ public void setLongPressToSelectTipAlreadyShown() {
+ getEditor().putInt(PreferenceKeys.LONG_PRESS_TO_SELECT_TIP_SHOWN, 1).apply();
+ notifyBackupPreferenceChanged();
}
void setSenderWhitelist(Set<String> addresses) {
diff --git a/src/com/android/mail/ui/ConversationListFragment.java b/src/com/android/mail/ui/ConversationListFragment.java
index 7b6ec49..3ed4ee5 100644
--- a/src/com/android/mail/ui/ConversationListFragment.java
+++ b/src/com/android/mail/ui/ConversationListFragment.java
@@ -334,6 +334,9 @@
manager.initLoader(ChildFolderLoads.LOADER_CHIDREN, args, mChildCallback);
}
+ // TODO: These special views are always created, doesn't matter whether they will
+ // be shown or not, as we add more views this will get more expensive. Given these are
+ // tips that are only shown once to the user, we should consider creating these on demand.
final ConversationListHelper helper = mActivity.getConversationListHelper();
final List<ConversationSpecialItemView> specialItemViews = helper != null ?
ImmutableList.copyOf(helper.makeConversationListSpecialViews(
diff --git a/src/com/android/mail/ui/ConversationListHelper.java b/src/com/android/mail/ui/ConversationListHelper.java
index dec28a3..707f5f0 100644
--- a/src/com/android/mail/ui/ConversationListHelper.java
+++ b/src/com/android/mail/ui/ConversationListHelper.java
@@ -39,8 +39,14 @@
.inflate(R.layout.conversation_photo_teaser_view, null);
+ // Long press to select tip
+ final ConversationLongPressTipView conversationLongPressTipView =
+ (ConversationLongPressTipView) LayoutInflater.from(context)
+ .inflate(R.layout.conversation_long_press_to_select_tip_view, null);
+
final ArrayList<ConversationSpecialItemView> itemViews = Lists.newArrayList();
itemViews.add(conversationPhotoTeaser);
+ itemViews.add(conversationLongPressTipView);
return itemViews;
}
}
diff --git a/src/com/android/mail/ui/ConversationLongPressTipView.java b/src/com/android/mail/ui/ConversationLongPressTipView.java
new file mode 100644
index 0000000..2335f8c
--- /dev/null
+++ b/src/com/android/mail/ui/ConversationLongPressTipView.java
@@ -0,0 +1,190 @@
+package com.android.mail.ui;
+
+import com.android.mail.R;
+import com.android.mail.browse.ConversationCursor;
+import com.android.mail.preferences.MailPrefs;
+import com.android.mail.providers.Folder;
+
+import android.animation.ObjectAnimator;
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+
+/**
+ * A tip to educate users about long press to enter CAB mode. Appears on top of conversation list.
+ */
+// TODO: this class was shamelessly copied from ConversationPhotoTeaserView. Look into
+// extracting a common base class.
+public class ConversationLongPressTipView extends FrameLayout
+ implements ConversationSpecialItemView, SwipeableItemView {
+
+ private static int sScrollSlop = 0;
+ private static int sShrinkAnimationDuration;
+
+ private final MailPrefs mMailPrefs;
+ private AnimatedAdapter mAdapter;
+
+ private View mSwipeableContent;
+
+ private boolean mShow;
+ private int mAnimatedHeight = -1;
+
+ public ConversationLongPressTipView(final Context context) {
+ this(context, null);
+ }
+
+ public ConversationLongPressTipView(final Context context, final AttributeSet attrs) {
+ this(context, attrs, -1);
+ }
+
+ public ConversationLongPressTipView(
+ final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context, attrs, defStyle);
+
+ final Resources resources = context.getResources();
+
+ if (sScrollSlop == 0) {
+ sScrollSlop = resources.getInteger(R.integer.swipeScrollSlop);
+ sShrinkAnimationDuration = resources.getInteger(
+ R.integer.shrink_animation_duration);
+ }
+
+ mMailPrefs = MailPrefs.get(context);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mSwipeableContent = findViewById(R.id.swipeable_content);
+
+ findViewById(R.id.dismiss_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ }
+
+ @Override
+ public void onUpdate(String account, Folder folder, ConversationCursor cursor) {
+ // It's possible user has enabled/disabled sender images in settings, which affects
+ // whether we want to show this tip or not.
+ mShow = checkWhetherToShow();
+ }
+
+ @Override
+ public boolean getShouldDisplayInList() {
+ mShow = checkWhetherToShow();
+ return mShow;
+ }
+
+ private boolean checkWhetherToShow() {
+ // show if 1) sender images are disabled 2) there are items
+ return !shouldShowSenderImage() && !mAdapter.isEmpty()
+ && !mMailPrefs.isLongPressToSelectTipAlreadyShown();
+ }
+
+ @Override
+ public int getPosition() {
+ // We want this teaser to go before the first real conversation
+ // If another teaser wants position 0, we will want position 1
+ return mAdapter.getPositionOffset(0);
+ }
+
+ @Override
+ public void setAdapter(AnimatedAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ @Override
+ public void bindLoaderManager(LoaderManager loaderManager) {
+ }
+
+ @Override
+ public void cleanup() {
+ }
+
+ @Override
+ public void onConversationSelected() {
+ // DO NOTHING
+ }
+
+ @Override
+ public void onCabModeEntered() {
+ dismiss();
+ }
+
+
+ @Override
+ public boolean acceptsUserTaps() {
+ // No, we don't allow user taps.
+ return false;
+ }
+
+ @Override
+ public void dismiss() {
+ setDismissed();
+ startDestroyAnimation();
+ }
+
+ private void setDismissed() {
+ if (mShow) {
+ mMailPrefs.setLongPressToSelectTipAlreadyShown();
+ mShow = false;
+ }
+ }
+
+ protected boolean shouldShowSenderImage() {
+ return mMailPrefs.getShowSenderImages();
+ }
+
+ @Override
+ public SwipeableView getSwipeableView() {
+ return SwipeableView.from(mSwipeableContent);
+ }
+
+ @Override
+ public boolean canChildBeDismissed() {
+ return true;
+ }
+
+ @Override
+ public float getMinAllowScrollDistance() {
+ return sScrollSlop;
+ }
+
+ private void startDestroyAnimation() {
+ final int start = getHeight();
+ final int end = 0;
+ mAnimatedHeight = start;
+ final ObjectAnimator heightAnimator =
+ ObjectAnimator.ofInt(this, "animatedHeight", start, end);
+ heightAnimator.setInterpolator(new DecelerateInterpolator(2.0f));
+ heightAnimator.setDuration(sShrinkAnimationDuration);
+ heightAnimator.start();
+ }
+
+ /**
+ * This method is used by the animator. It is explicitly kept in proguard.flags to prevent it
+ * from being removed, inlined, or obfuscated.
+ * Edit ./packages/apps/UnifiedEmail/proguard.flags
+ * In the future, we want to use @Keep
+ */
+ public void setAnimatedHeight(final int height) {
+ mAnimatedHeight = height;
+ requestLayout();
+ }
+
+ @Override
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+ if (mAnimatedHeight == -1) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mAnimatedHeight);
+ }
+ }
+
+}