ConversationPhotoTeaserView.java revision f45e0e7f65eb421fc5c2acf7bb76a714e12dc68b
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() { 129 // Do nothing 130 } 131 132 @Override 133 public boolean getShouldDisplayInList() { 134 // show if 1) sender images are enabled 2) there are items 135 mShown = shouldShowSenderImage() && !mAdapter.isEmpty() 136 && !mMailPrefs.isConversationPhotoTeaserAlreadyShown(); 137 return mShown; 138 } 139 140 @Override 141 public int getPosition() { 142 return 0; 143 } 144 145 @Override 146 public void setAdapter(AnimatedAdapter adapter) { 147 mAdapter = adapter; 148 } 149 150 @Override 151 public void bindLoaderManager(LoaderManager loaderManager) { 152 } 153 154 @Override 155 public void cleanup() { 156 } 157 158 @Override 159 public void onConversationSelected() { 160 // DO NOTHING 161 } 162 163 @Override 164 public void onCabModeEntered() { 165 dismiss(); 166 } 167 168 @Override 169 public void onCabModeExited() { 170 // Do nothing 171 } 172 173 @Override 174 public boolean acceptsUserTaps() { 175 // No, we don't allow user taps. 176 return false; 177 } 178 179 @Override 180 public void dismiss() { 181 setDismissed(); 182 startDestroyAnimation(); 183 } 184 185 private void setDismissed() { 186 if (mShown) { 187 mMailPrefs.setConversationPhotoTeaserAlreadyShown(); 188 mShown = false; 189 Analytics.getInstance().sendEvent("list_swipe", "photo_teaser", null, 0); 190 } 191 } 192 193 protected boolean shouldShowSenderImage() { 194 return mMailPrefs.getShowSenderImages(); 195 } 196 197 @Override 198 public SwipeableView getSwipeableView() { 199 return SwipeableView.from(mSwipeableContent); 200 } 201 202 @Override 203 public boolean canChildBeDismissed() { 204 return true; 205 } 206 207 @Override 208 public float getMinAllowScrollDistance() { 209 return sScrollSlop; 210 } 211 212 private void startDestroyAnimation() { 213 final int start = getHeight(); 214 final int end = 0; 215 mAnimatedHeight = start; 216 final ObjectAnimator heightAnimator = 217 ObjectAnimator.ofInt(this, "animatedHeight", start, end); 218 heightAnimator.setInterpolator(new DecelerateInterpolator(2.0f)); 219 heightAnimator.setDuration(sShrinkAnimationDuration); 220 heightAnimator.addListener(new AnimatorListener() { 221 @Override 222 public void onAnimationStart(final Animator animation) { 223 // Do nothing 224 } 225 226 @Override 227 public void onAnimationRepeat(final Animator animation) { 228 // Do nothing 229 } 230 231 @Override 232 public void onAnimationEnd(final Animator animation) { 233 // We should no longer exist, so notify the adapter 234 mAdapter.notifyDataSetChanged(); 235 } 236 237 @Override 238 public void onAnimationCancel(final Animator animation) { 239 // Do nothing 240 } 241 }); 242 heightAnimator.start(); 243 } 244 245 /** 246 * This method is used by the animator. It is explicitly kept in proguard.flags to prevent it 247 * from being removed, inlined, or obfuscated. 248 * Edit ./packages/apps/UnifiedEmail/proguard.flags 249 * In the future, we want to use @Keep 250 */ 251 public void setAnimatedHeight(final int height) { 252 mAnimatedHeight = height; 253 requestLayout(); 254 } 255 256 @Override 257 protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { 258 if (Utils.getDisplayListRightEdgeEffect(mTabletDevice, mListCollapsible, 259 mAdapter.getViewMode())) { 260 mTeaserRightEdge.setVisibility(VISIBLE); 261 } else { 262 mTeaserRightEdge.setVisibility(GONE); 263 } 264 265 if (mAnimatedHeight == -1) { 266 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 267 } else { 268 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mAnimatedHeight); 269 } 270 } 271} 272