• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ConversationPhotoTeaserView(final Context context)47     public ConversationPhotoTeaserView(final Context context) {
48         this(context, null);
49     }
50 
ConversationPhotoTeaserView(final Context context, final AttributeSet attrs)51     public ConversationPhotoTeaserView(final Context context, final AttributeSet attrs) {
52         this(context, attrs, -1);
53     }
54 
ConversationPhotoTeaserView( final Context context, final AttributeSet attrs, final int defStyle)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
onFinishInflate()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
onLayout(boolean changed, int left, int top, int right, int bottom)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
onUpdate(Folder folder, ConversationCursor cursor)122     public void onUpdate(Folder folder, ConversationCursor cursor) {
123         // Do nothing
124     }
125 
126     @Override
onGetView()127     public void onGetView() {
128         // Do nothing
129     }
130 
131     @Override
getShouldDisplayInList()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
getPosition()140     public int getPosition() {
141         return 0;
142     }
143 
144     @Override
setAdapter(AnimatedAdapter adapter)145     public void setAdapter(AnimatedAdapter adapter) {
146         mAdapter = adapter;
147     }
148 
149     @Override
bindFragment(final LoaderManager loaderManager, final Bundle savedInstanceState)150     public void bindFragment(final LoaderManager loaderManager, final Bundle savedInstanceState) {
151     }
152 
153     @Override
cleanup()154     public void cleanup() {
155     }
156 
157     @Override
onConversationSelected()158     public void onConversationSelected() {
159         // DO NOTHING
160     }
161 
162     @Override
onCabModeEntered()163     public void onCabModeEntered() {
164         if (mShown) {
165             dismiss();
166         }
167     }
168 
169     @Override
onCabModeExited()170     public void onCabModeExited() {
171         // Do nothing
172     }
173 
174     @Override
onConversationListVisibilityChanged(final boolean visible)175     public void onConversationListVisibilityChanged(final boolean visible) {
176         // Do nothing
177     }
178 
179     @Override
saveInstanceState(final Bundle outState)180     public void saveInstanceState(final Bundle outState) {
181         // Do nothing
182     }
183 
184     @Override
acceptsUserTaps()185     public boolean acceptsUserTaps() {
186         // No, we don't allow user taps.
187         return false;
188     }
189 
190     @Override
dismiss()191     public void dismiss() {
192         setDismissed();
193         startDestroyAnimation();
194     }
195 
setDismissed()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 
shouldShowSenderImage()204     protected boolean shouldShowSenderImage() {
205         return mMailPrefs.getShowSenderImages();
206     }
207 
208     @Override
getSwipeableView()209     public SwipeableView getSwipeableView() {
210         return SwipeableView.from(mSwipeableContent);
211     }
212 
213     @Override
canChildBeDismissed()214     public boolean canChildBeDismissed() {
215         return true;
216     }
217 
218     @Override
getMinAllowScrollDistance()219     public float getMinAllowScrollDistance() {
220         return sScrollSlop;
221     }
222 
startDestroyAnimation()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      */
setAnimatedHeight(final int height)249     public void setAnimatedHeight(final int height) {
250         mAnimatedHeight = height;
251         requestLayout();
252     }
253 
254     @Override
onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)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