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