Home | History | Annotate | Download | only in ui
      1 package com.android.mail.ui;
      2 
      3 import android.animation.ObjectAnimator;
      4 import android.app.LoaderManager;
      5 import android.content.Context;
      6 import android.content.res.Resources;
      7 import android.os.Bundle;
      8 import android.util.AttributeSet;
      9 import android.view.View;
     10 import android.view.animation.DecelerateInterpolator;
     11 import android.widget.FrameLayout;
     12 import android.widget.ImageView;
     13 import android.widget.LinearLayout;
     14 import android.widget.TextView;
     15 
     16 import com.android.mail.R;
     17 import com.android.mail.analytics.Analytics;
     18 import com.android.mail.browse.ConversationCursor;
     19 import com.android.mail.preferences.MailPrefs;
     20 import com.android.mail.providers.Folder;
     21 import com.android.mail.utils.Utils;
     22 
     23 /**
     24  * A teaser to introduce people to the contact photo check boxes
     25  */
     26 public class ConversationPhotoTeaserView extends FrameLayout
     27         implements ConversationSpecialItemView, SwipeableItemView {
     28     private static int sScrollSlop = 0;
     29     private static int sShrinkAnimationDuration;
     30 
     31     private final MailPrefs mMailPrefs;
     32     private AnimatedAdapter mAdapter;
     33 
     34     private View mSwipeableContent;
     35 
     36     private boolean mShown;
     37     private int mAnimatedHeight = -1;
     38     private boolean mNeedLayout;
     39     private int mTextTop;
     40 
     41     private View mTeaserRightEdge;
     42     /** Whether we are on a tablet device or not */
     43     private final boolean mTabletDevice;
     44     /** When in conversation mode, true if the list is hidden */
     45     private final boolean mListCollapsible;
     46 
     47     public ConversationPhotoTeaserView(final Context context) {
     48         this(context, null);
     49     }
     50 
     51     public ConversationPhotoTeaserView(final Context context, final AttributeSet attrs) {
     52         this(context, attrs, -1);
     53     }
     54 
     55     public ConversationPhotoTeaserView(
     56             final Context context, final AttributeSet attrs, final int defStyle) {
     57         super(context, attrs, defStyle);
     58 
     59         final Resources resources = context.getResources();
     60 
     61         synchronized (ConversationPhotoTeaserView.class) {
     62             if (sScrollSlop == 0) {
     63                 sScrollSlop = resources.getInteger(R.integer.swipeScrollSlop);
     64                 sShrinkAnimationDuration = resources.getInteger(
     65                         R.integer.shrink_animation_duration);
     66             }
     67         }
     68 
     69         mMailPrefs = MailPrefs.get(context);
     70 
     71         mNeedLayout = true;
     72 
     73         mTabletDevice = Utils.useTabletUI(resources);
     74         mListCollapsible = resources.getBoolean(R.bool.list_collapsible);
     75     }
     76 
     77     @Override
     78     protected void onFinishInflate() {
     79         mSwipeableContent = findViewById(R.id.swipeable_content);
     80 
     81         findViewById(R.id.dismiss_button).setOnClickListener(new OnClickListener() {
     82             @Override
     83             public void onClick(View v) {
     84                 dismiss();
     85             }
     86         });
     87 
     88         mTeaserRightEdge = findViewById(R.id.teaser_right_edge);
     89     }
     90 
     91     @Override
     92     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     93         super.onLayout(changed, left, top, right, bottom);
     94 
     95         final TextView text = (TextView) findViewById(R.id.text);
     96         final ImageView arrow = (ImageView) findViewById(R.id.arrow);
     97 
     98         // We post to avoid calling layout within layout
     99         arrow.post(new Runnable() {
    100             @Override
    101             public void run() {
    102 
    103                 // The text top is changed when we move the arrow, so we need to
    104                 // do multiple passes
    105                 int textTop = text.getTop();
    106                 if (mNeedLayout || textTop != mTextTop) {
    107                     mNeedLayout = false;
    108                     mTextTop = textTop;
    109 
    110                     final int lineHeight = text.getLineHeight();
    111                     final LinearLayout.LayoutParams arrowParams = (LinearLayout.LayoutParams) arrow
    112                             .getLayoutParams();
    113                     arrowParams.topMargin = mTextTop + lineHeight / 2;
    114                     arrow.setLayoutParams(arrowParams);
    115                 }
    116                 arrow.setVisibility(View.VISIBLE);
    117             }
    118         });
    119     }
    120 
    121     @Override
    122     public void onUpdate(Folder folder, ConversationCursor cursor) {
    123         // Do nothing
    124     }
    125 
    126     @Override
    127     public void onGetView() {
    128         // Do nothing
    129     }
    130 
    131     @Override
    132     public boolean getShouldDisplayInList() {
    133         // show if 1) sender images are enabled 2) there are items
    134         mShown = shouldShowSenderImage() && !mAdapter.isEmpty()
    135                 && !mMailPrefs.isConversationPhotoTeaserAlreadyShown();
    136         return mShown;
    137     }
    138 
    139     @Override
    140     public int getPosition() {
    141         return 0;
    142     }
    143 
    144     @Override
    145     public void setAdapter(AnimatedAdapter adapter) {
    146         mAdapter = adapter;
    147     }
    148 
    149     @Override
    150     public void bindFragment(final LoaderManager loaderManager, final Bundle savedInstanceState) {
    151     }
    152 
    153     @Override
    154     public void cleanup() {
    155     }
    156 
    157     @Override
    158     public void onConversationSelected() {
    159         // DO NOTHING
    160     }
    161 
    162     @Override
    163     public void onCabModeEntered() {
    164         if (mShown) {
    165             dismiss();
    166         }
    167     }
    168 
    169     @Override
    170     public void onCabModeExited() {
    171         // Do nothing
    172     }
    173 
    174     @Override
    175     public void onConversationListVisibilityChanged(final boolean visible) {
    176         // Do nothing
    177     }
    178 
    179     @Override
    180     public void saveInstanceState(final Bundle outState) {
    181         // Do nothing
    182     }
    183 
    184     @Override
    185     public boolean acceptsUserTaps() {
    186         // No, we don't allow user taps.
    187         return false;
    188     }
    189 
    190     @Override
    191     public void dismiss() {
    192         setDismissed();
    193         startDestroyAnimation();
    194     }
    195 
    196     private void setDismissed() {
    197         if (mShown) {
    198             mMailPrefs.setConversationPhotoTeaserAlreadyShown();
    199             mShown = false;
    200             Analytics.getInstance().sendEvent("list_swipe", "photo_teaser", null, 0);
    201         }
    202     }
    203 
    204     protected boolean shouldShowSenderImage() {
    205         return mMailPrefs.getShowSenderImages();
    206     }
    207 
    208     @Override
    209     public SwipeableView getSwipeableView() {
    210         return SwipeableView.from(mSwipeableContent);
    211     }
    212 
    213     @Override
    214     public boolean canChildBeDismissed() {
    215         return true;
    216     }
    217 
    218     @Override
    219     public float getMinAllowScrollDistance() {
    220         return sScrollSlop;
    221     }
    222 
    223     private void startDestroyAnimation() {
    224         final int start = getHeight();
    225         final int end = 0;
    226         mAnimatedHeight = start;
    227         final ObjectAnimator heightAnimator =
    228                 ObjectAnimator.ofInt(this, "animatedHeight", start, end);
    229         heightAnimator.setInterpolator(new DecelerateInterpolator(2.0f));
    230         heightAnimator.setDuration(sShrinkAnimationDuration);
    231         heightAnimator.start();
    232 
    233         /*
    234          * Ideally, we would like to call mAdapter.notifyDataSetChanged() in a listener's
    235          * onAnimationEnd(), but we are in the middle of a touch event, and this will cause all the
    236          * views to get recycled, which will cause problems.
    237          *
    238          * Instead, we'll just leave the item in the list with a height of 0, and the next
    239          * notifyDatasetChanged() will remove it from the adapter.
    240          */
    241     }
    242 
    243     /**
    244      * This method is used by the animator.  It is explicitly kept in proguard.flags to prevent it
    245      * from being removed, inlined, or obfuscated.
    246      * Edit ./packages/apps/UnifiedEmail/proguard.flags
    247      * In the future, we want to use @Keep
    248      */
    249     public void setAnimatedHeight(final int height) {
    250         mAnimatedHeight = height;
    251         requestLayout();
    252     }
    253 
    254     @Override
    255     protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
    256         if (Utils.getDisplayListRightEdgeEffect(mTabletDevice, mListCollapsible,
    257                 mAdapter.getViewMode())) {
    258             mTeaserRightEdge.setVisibility(VISIBLE);
    259         } else {
    260             mTeaserRightEdge.setVisibility(GONE);
    261         }
    262 
    263         if (mAnimatedHeight == -1) {
    264             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    265         } else {
    266             setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mAnimatedHeight);
    267         }
    268     }
    269 }
    270