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