diff options
author | Christine Chen <christinech@google.com> | 2013-07-15 18:31:22 -0700 |
---|---|---|
committer | Christine Chen <christinech@google.com> | 2013-07-31 10:37:09 -0700 |
commit | 316b4713b2f8f26f393ecc4bb4760512a4a9f096 (patch) | |
tree | bd8e71ad2813c0861415cacdc062611d0fe556d2 /src/com/android/dialer/list/PhoneFavoriteTileView.java | |
parent | 6091473941d277ed3746143c1ca9bffdfbe2bd94 (diff) |
Adds Drag and Drop UI to the Dialer main view.
- Adds drag and drop listner.
- Changes the FavoritesTileAdapter to use an array stored in cache to
populate the view.
- Adds animation for drag and drop.
- Adds swipe to delete an entry.
Change-Id: I0717fb3d256b2ab2353f86a998de07edb24e9b4c
Diffstat (limited to 'src/com/android/dialer/list/PhoneFavoriteTileView.java')
-rw-r--r-- | src/com/android/dialer/list/PhoneFavoriteTileView.java | 233 |
1 files changed, 211 insertions, 22 deletions
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java index d87e2a837..9a1577a27 100644 --- a/src/com/android/dialer/list/PhoneFavoriteTileView.java +++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,51 +16,239 @@ */ package com.android.dialer.list; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Context; -import android.content.Intent; +import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; import android.view.View; -import android.widget.ImageButton; -import com.android.contacts.common.R; +import com.android.contacts.common.MoreContactUtils; +import com.android.contacts.common.list.ContactEntry; import com.android.contacts.common.list.ContactTileView; +import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow; /** - * Displays the contact's picture overlayed with their name - * in a perfect square. It also has an additional touch target for a secondary action. + * A light version of the {@link com.android.contacts.common.list.ContactTileView} that is used in + * Dialtacts for frequently called contacts. Slightly different behavior from superclass when you + * tap it, you want to call the frequently-called number for the contact, even if that is not the + * default number for that contact. This abstract class is the super class to both the row and tile + * view. */ -public class PhoneFavoriteTileView extends ContactTileView { - private ImageButton mSecondaryButton; +public abstract class PhoneFavoriteTileView extends ContactTileView { + + private static final String TAG = PhoneFavoriteTileView.class.getSimpleName(); + private static final boolean DEBUG = false; + + /** Length of all animations in miniseconds. */ + private static final int ANIMATION_LENGTH = 300; + + /** The view that holds the front layer of the favorite contact card. */ + protected View mFavoriteContactCard; + /** The view that holds the background layer of the removal dialogue. */ + protected View mRemovalDialogue; + /** Undo button for undoing favorite removal. */ + protected View mUndoRemovalButton; + /** The view that holds the list view row. */ + protected ContactTileRow mParentRow; + + /** Users' most frequent phone number. */ + private String mPhoneNumberString; + + /** Custom gesture detector.*/ + protected GestureDetector mGestureDetector; + /** Indicator of whether a scroll has started. */ + private boolean mInScroll; public PhoneFavoriteTileView(Context context, AttributeSet attrs) { super(context, attrs); } + public ContactTileRow getParentRow() { + return mParentRow; + } + @Override - protected void onFinishInflate() { - super.onFinishInflate(); + public void loadFromContact(ContactEntry entry) { + super.loadFromContact(entry); + mPhoneNumberString = null; // ... in case we're reusing the view + if (entry != null) { + // Grab the phone-number to call directly... see {@link onClick()} + mPhoneNumberString = entry.phoneNumber; + } + } + + /** + * Gets the latest scroll gesture offset. + */ + public void setScrollOffset(float offset) { + // Sets the mInScroll variable to indicate a scroll is in progress. + if (!mInScroll) { + mInScroll = true; + } + + // Changes the view to follow user's scroll position. + shiftViewWithScroll(offset); + } + + /** + * Shifts the view to follow user's scroll position. + */ + private void shiftViewWithScroll(float offset) { + if (mInScroll) { + // Shifts the foreground card to follow users' scroll gesture. + mFavoriteContactCard.setTranslationX(offset); + + // Changes transparency of the foreground and background color + final float alpha = 1.f - Math.abs((offset)) / getWidth(); + final float cappedAlpha = Math.min(Math.max(alpha, 0.f), 1.f); + mFavoriteContactCard.setAlpha(cappedAlpha); + } + } - mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button); - mSecondaryButton.setOnClickListener(new OnClickListener() { + /** + * Sets the scroll has finished. + * + * @param isUnfinishedFling True if it is triggered from the onFling method, but the fling was + * too short or too slow, or from the scroll that does not trigger fling. + */ + public void setScrollEnd(boolean isUnfinishedFling) { + mInScroll = false; + + if (isUnfinishedFling) { + // If the fling is too short or too slow, or it is from a scroll, bring back the + // favorite contact card. + final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(mFavoriteContactCard, "alpha", + 1.f).setDuration(ANIMATION_LENGTH); + final ObjectAnimator moveBack = ObjectAnimator.ofFloat(mFavoriteContactCard, + "translationX", 0.f).setDuration(ANIMATION_LENGTH); + final ObjectAnimator backgroundFadeOut = ObjectAnimator.ofInt( + mParentRow.getBackground(), "alpha", 255).setDuration(ANIMATION_LENGTH); + final AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(fadeIn, moveBack, backgroundFadeOut); + animSet.start(); + } else { + // If the fling is fast and far enough, move away the favorite contact card, bring the + // favorite removal view to the foreground to ask user to confirm removal. + int animationLength = (int) ((1 - Math.abs(mFavoriteContactCard.getTranslationX()) / + getWidth()) * ANIMATION_LENGTH); + final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(mFavoriteContactCard, "alpha", + 0.f).setDuration(animationLength); + final ObjectAnimator moveAway = ObjectAnimator.ofFloat(mFavoriteContactCard, + "translationX", getWidth()).setDuration(animationLength); + final ObjectAnimator backgroundFadeIn = ObjectAnimator.ofInt( + mParentRow.getBackground(), "alpha", 0).setDuration(animationLength); + if (mFavoriteContactCard.getTranslationX() < 0) { + moveAway.setFloatValues(-getWidth()); + } + final AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(fadeOut, moveAway, backgroundFadeIn); + animSet.start(); + } + } + + /** + * Signals the user wants to undo removing the favorite contact. + */ + public void undoRemove() { + // Makes the removal dialogue invisible. + mRemovalDialogue.setAlpha(0.0f); + mRemovalDialogue.setVisibility(GONE); + + // Animates back the favorite contact card. + final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(mFavoriteContactCard, "alpha", 1.f). + setDuration(ANIMATION_LENGTH); + final ObjectAnimator moveBack = ObjectAnimator.ofFloat(mFavoriteContactCard, "translationX", + 0.f).setDuration(ANIMATION_LENGTH); + final ObjectAnimator backgroundFadeOut = ObjectAnimator.ofInt(mParentRow.getBackground(), + "alpha", 255).setDuration(ANIMATION_LENGTH); + final AnimatorSet animSet = new AnimatorSet(); + animSet.playTogether(fadeIn, moveBack, backgroundFadeOut); + animSet.start(); + + // Signals the PhoneFavoritesTileAdapter to undo the potential delete. + mParentRow.getTileAdapter().undoPotentialRemoveEntryIndex(); + } + + /** + * Sets up the removal dialogue. + */ + public void setupRemoveDialogue() { + mRemovalDialogue.setVisibility(VISIBLE); + mRemovalDialogue.setAlpha(1.0f); + + mUndoRemovalButton.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_VIEW, getLookupUri()); - // Secondary target will be visible only from phone's favorite screen, then - // we want to launch it as a separate People task. - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - getContext().startActivity(intent); + public void onClick(View view) { + undoRemove(); } }); } + /** + * Sets up the favorite contact card. + */ + public void setupFavoriteContactCard() { + if (mRemovalDialogue != null) { + mRemovalDialogue.setVisibility(GONE); + mRemovalDialogue.setAlpha(0.f); + } + mFavoriteContactCard.setAlpha(1.0f); + mFavoriteContactCard.setTranslationX(0.f); + } + @Override - protected boolean isDarkTheme() { - return false; + protected OnClickListener createClickListener() { + return new OnClickListener() { + @Override + public void onClick(View v) { + if (mListener == null) return; + if (TextUtils.isEmpty(mPhoneNumberString)) { + // Copy "superclass" implementation + mListener.onContactSelected(getLookupUri(), MoreContactUtils + .getTargetRectFromView( + mContext, PhoneFavoriteTileView.this)); + } else { + // When you tap a frequently-called contact, you want to + // call them at the number that you usually talk to them + // at (i.e. the one displayed in the UI), regardless of + // whether that's their default number. + mListener.onCallNumberDirectly(mPhoneNumberString); + } + } + }; } @Override - protected int getApproximateImageSize() { - // The picture is the full size of the tile (minus some padding, but we can be generous) - return mListener.getApproximateTileWidth(); + public boolean onTouchEvent(MotionEvent event) { + if (DEBUG) { + Log.v(TAG, event.toString()); + } + switch (event.getAction()) { + // If the scroll has finished without triggering a fling, handles it here. + case MotionEvent.ACTION_UP: + setPressed(false); + if (mInScroll) { + if (!mGestureDetector.onTouchEvent(event)) { + setScrollEnd(true); + } + return true; + } + break; + // When user starts a new gesture, clean up the pending removals. + case MotionEvent.ACTION_DOWN: + mParentRow.getTileAdapter().removeContactEntry(); + break; + // When user continues with a new gesture, cleans up all the temp variables. + case MotionEvent.ACTION_CANCEL: + mParentRow.getTileAdapter().cleanTempVariables(); + break; + default: + break; + } + return mGestureDetector.onTouchEvent(event); } } |