summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristine Chen <christinech@google.com>2013-07-15 18:31:22 -0700
committerChristine Chen <christinech@google.com>2013-07-31 10:37:09 -0700
commit316b4713b2f8f26f393ecc4bb4760512a4a9f096 (patch)
treebd8e71ad2813c0861415cacdc062611d0fe556d2 /src
parent6091473941d277ed3746143c1ca9bffdfbe2bd94 (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')
-rw-r--r--src/com/android/dialer/list/NewPhoneFavoriteFragment.java1
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java196
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteRegularRowView.java70
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteSquareTileView.java82
-rw-r--r--src/com/android/dialer/list/PhoneFavoriteTileView.java233
-rw-r--r--src/com/android/dialer/list/PhoneFavoritesTileAdapter.java395
6 files changed, 854 insertions, 123 deletions
diff --git a/src/com/android/dialer/list/NewPhoneFavoriteFragment.java b/src/com/android/dialer/list/NewPhoneFavoriteFragment.java
index ba438cff3..eba931021 100644
--- a/src/com/android/dialer/list/NewPhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/NewPhoneFavoriteFragment.java
@@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
diff --git a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java b/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java
new file mode 100644
index 000000000..846b2a74f
--- /dev/null
+++ b/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dialer.list;
+
+import android.content.ClipData;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnDragListener;
+
+import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
+
+/**
+ * Implements the OnLongClickListener and OnDragListener for phone's favorite tiles and rows.
+ */
+public class PhoneFavoriteDragAndDropListeners {
+
+ private static final String TAG = PhoneFavoriteDragAndDropListeners.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final float FLING_HEIGHT_PORTION = 1.f / 4.f;
+ private static final float FLING_WIDTH_PORTION = 1.f / 6.f;
+
+ public static class PhoneFavoriteGestureListener extends SimpleOnGestureListener {
+ private static final float FLING_VELOCITY_MINIMUM = 5.0f;
+ private float mFlingHorizontalThreshold;
+ private float mFlingVerticalThreshold;
+ private final PhoneFavoriteTileView mView;
+
+ public PhoneFavoriteGestureListener(View view) {
+ super();
+ mView = (PhoneFavoriteTileView) view;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent event) {
+ final ClipData data = ClipData.newPlainText("", "");
+ final View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(mView);
+ mView.setPressed(false);
+ if (mView instanceof PhoneFavoriteRegularRowView) {
+ // If the view is regular row, start drag the row view.
+ // TODO: move the padding so we can start drag the original view.
+ mView.getParentRow().startDrag(data, shadowBuilder, null, 0);
+ } else {
+ // If the view is a tile view, start drag the tile.
+ mView.startDrag(data, shadowBuilder, null, 0);
+ }
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ final float x1 = e1.getX();
+ final float x2 = e2.getX();
+ // Temporarily disables parents from getting this event so the listview does not scroll.
+ mView.getParent().requestDisallowInterceptTouchEvent(true);
+ mView.setScrollOffset(x2 - x1);
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ // Sets fling trigger threshold.
+ mFlingVerticalThreshold = (float) mView.getHeight() * FLING_HEIGHT_PORTION;
+ mFlingHorizontalThreshold = (float) mView.getWidth() * FLING_WIDTH_PORTION;
+ final float x1 = e1.getX();
+ final float x2 = e2.getX();
+ final float y1 = e1.getY();
+ final float y2 = e2.getY();
+
+ mView.setPressed(false);
+
+ if (Math.abs(y1 - y2) < mFlingVerticalThreshold &&
+ Math.abs(x2 - x1) > mFlingHorizontalThreshold &&
+ Math.abs(velocityX) > FLING_VELOCITY_MINIMUM) {
+ // If fling is triggered successfully, end the scroll and setup removal dialogue.
+ final int removeIndex = mView.getParentRow().getItemIndex(x1, y1);
+ mView.setScrollEnd(false);
+ mView.setupRemoveDialogue();
+ mView.getParentRow().getTileAdapter().setPotentialRemoveEntryIndex(removeIndex);
+
+ return true;
+ } else {
+ mView.setScrollEnd(true);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ mView.setPressed(true);
+ // Signals that the view will accept further events.
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ mView.performClick();
+ return true;
+ }
+ }
+
+ /**
+ * Implements the OnDragListener to handle drag events.
+ */
+ public static class PhoneFavoriteDragListener implements OnDragListener {
+ /** Location of the drag event. */
+ private float mX = 0;
+ private float mY = 0;
+ private final ContactTileRow mContactTileRow;
+ private final PhoneFavoritesTileAdapter mTileAdapter;
+
+ public PhoneFavoriteDragListener(ContactTileRow contactTileRow,
+ PhoneFavoritesTileAdapter tileAdapter) {
+ super();
+ mContactTileRow = contactTileRow;
+ mTileAdapter = tileAdapter;
+ }
+
+ @Override
+ public boolean onDrag(View v, DragEvent event) {
+ // Handles drag events.
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ break;
+ case DragEvent.ACTION_DRAG_ENTERED:
+ break;
+ case DragEvent.ACTION_DRAG_EXITED:
+ break;
+ case DragEvent.ACTION_DROP:
+ // Gets the location of the drag with respect to the whole Dialer view.
+ mX = event.getX() + v.getLeft();
+ mY = event.getY() + v.getTop();
+
+ // Indicates a drag has finished.
+ if (mTileAdapter != null && mContactTileRow != null) {
+ mTileAdapter.setInDragging(false);
+
+ // Finds out at which position of the list the Contact is being dropped.
+ final int dropIndex = mContactTileRow.getItemIndex(mX, mY);
+ if (DEBUG) {
+ Log.v(TAG, "Stop dragging " + String.valueOf(dropIndex));
+ }
+
+ // Adds the dragged contact to the drop position.
+ mTileAdapter.dropContactEntry(dropIndex);
+ }
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ // Gets the current drag location with respect to the whole Dialer view.
+ mX = event.getX() + v.getLeft();
+ mY = event.getY() + v.getTop();
+ if (DEBUG) {
+ Log.v(TAG, String.valueOf(mX) + "; " + String.valueOf(mY));
+ }
+
+ if (mTileAdapter != null && mContactTileRow != null) {
+ // If there is no drag in process, initializes the drag.
+ if (!mTileAdapter.getInDragging()) {
+ // Finds out which item is being dragged.
+ final int dragIndex = mContactTileRow.getItemIndex(mX, mY);
+ if (DEBUG) {
+ Log.v(TAG, "Start dragging " + String.valueOf(dragIndex));
+ }
+
+ // Indicates a drag has started.
+ mTileAdapter.setInDragging(true);
+
+ // Temporarily pops out the Contact entry.
+ mTileAdapter.popContactEntry(dragIndex);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
index 2f5921eaf..6d9fdcbbb 100644
--- a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
@@ -16,67 +16,51 @@
package com.android.dialer.list;
import android.content.Context;
-import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.View;
+import android.view.GestureDetector;
-import com.android.contacts.common.MoreContactUtils;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileView;
import com.android.contacts.common.util.ViewUtil;
+import com.android.dialer.R;
+import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteDragListener;
+import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteGestureListener;
+
+import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
-/**
- * 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.
- */
-public class PhoneFavoriteRegularRowView extends ContactTileView {
- private String mPhoneNumberString;
+
+public class PhoneFavoriteRegularRowView extends PhoneFavoriteTileView {
+ private static final String TAG = PhoneFavoriteRegularRowView.class.getSimpleName();
+ private static final boolean DEBUG = false;
public PhoneFavoriteRegularRowView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
- protected boolean isDarkTheme() {
- return false;
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mFavoriteContactCard = findViewById(R.id.contact_favorite_card);
+ mRemovalDialogue = findViewById(R.id.favorite_remove_dialogue);
+ mUndoRemovalButton = findViewById(R.id.favorite_remove_undo_button);
+
+ mGestureDetector = new GestureDetector(getContext(),
+ new PhoneFavoriteGestureListener(this));
}
@Override
- protected int getApproximateImageSize() {
- return ViewUtil.getConstantPreLayoutWidth(getQuickContact());
+ protected void onAttachedToWindow() {
+ mParentRow = (ContactTileRow) getParent();
+ mParentRow.setOnDragListener(new PhoneFavoriteDragListener(mParentRow,
+ mParentRow.getTileAdapter()));
}
@Override
- 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;
- }
+ protected boolean isDarkTheme() {
+ return false;
}
@Override
- 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, PhoneFavoriteRegularRowView.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);
- }
- }
- };
+ protected int getApproximateImageSize() {
+ return ViewUtil.getConstantPreLayoutWidth(getQuickContact());
}
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
new file mode 100644
index 000000000..fe07d188e
--- /dev/null
+++ b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
@@ -0,0 +1,82 @@
+/*
+
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dialer.list;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.View;
+import android.widget.ImageButton;
+
+import com.android.contacts.common.R;
+import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteDragListener;
+import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteGestureListener;
+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.
+ */
+public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView {
+ private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
+ private ImageButton mSecondaryButton;
+
+ public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mFavoriteContactCard = findViewById(com.android.dialer.R.id.contact_tile_favorite_card);
+ mRemovalDialogue = findViewById(com.android.dialer.R.id.favorite_tile_remove_dialogue);
+ mUndoRemovalButton = findViewById(com.android.dialer.R.id.favorite_tile_remove_undo_button);
+ mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
+ mSecondaryButton.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);
+ }
+ });
+
+ mGestureDetector = new GestureDetector(getContext(),
+ new PhoneFavoriteGestureListener(this));
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ mParentRow = (ContactTileRow) getParent();
+ setOnDragListener(new PhoneFavoriteDragListener(mParentRow, mParentRow.getTileAdapter()));
+ }
+
+ @Override
+ protected boolean isDarkTheme() {
+ return false;
+ }
+
+ @Override
+ protected int getApproximateImageSize() {
+ // The picture is the full size of the tile (minus some padding, but we can be generous)
+ return mListener.getApproximateTileWidth();
+ }
+}
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);
}
}
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index 6a4476df5..0a08f2caf 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -15,11 +15,13 @@
*/
package com.android.dialer.list;
+import android.animation.ObjectAnimator;
import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
-import android.graphics.drawable.Drawable;
+import android.graphics.Color;
+import android.graphics.Rect;
import android.net.Uri;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
@@ -33,7 +35,6 @@ import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactTileLoaderFactory;
import com.android.contacts.common.R;
import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileAdapter;
import com.android.contacts.common.list.ContactTileView;
import java.util.ArrayList;
@@ -45,14 +46,33 @@ import java.util.ArrayList;
*
*/
public class PhoneFavoritesTileAdapter extends BaseAdapter {
- private static final String TAG = ContactTileAdapter.class.getSimpleName();
+ private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
+ private static final boolean DEBUG = false;
public static final int ROW_LIMIT_DEFAULT = 1;
+ /** Time period for an animation. */
+ private static final int ANIMATION_LENGTH = 300;
+
+ private final ObjectAnimator mTranslateHorizontalAnimation;
+ private final ObjectAnimator mTranslateVerticalAnimation;
+ private final ObjectAnimator mAlphaAnimation;
+
private ContactTileView.Listener mListener;
private Context mContext;
private Resources mResources;
- protected Cursor mContactCursor = null;
+
+ /** Contact data stored in cache. This is used to populate the associated view. */
+ protected ArrayList<ContactEntry> mContactEntries = null;
+ /** Back up of the temporarily removed Contact during dragging. */
+ private ContactEntry mDraggedEntry = null;
+ /** Position of the temporarily removed contact in the cache. */
+ private int mDraggedEntryIndex = -1;
+ /** New position of the temporarily removed contact in the cache. */
+ private int mDropEntryIndex = -1;
+ /** Position of the contact pending removal. */
+ private int mPotentialRemoveEntryIndex = -1;
+
private ContactPhotoManager mPhotoManager;
protected int mNumFrequents;
protected int mNumStarred;
@@ -78,22 +98,36 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
private boolean mIsQuickContactEnabled = false;
private final int mPaddingInPixels;
- public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener, int numCols) {
+ /** Indicates whether a drag is in process. */
+ private boolean mInDragging = false;
+
+ public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener,
+ int numCols) {
this(context, listener, numCols, ROW_LIMIT_DEFAULT);
}
- public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener, int numCols,
- int maxTiledRows) {
+ public PhoneFavoritesTileAdapter(Context context, ContactTileView.Listener listener,
+ int numCols, int maxTiledRows) {
mListener = listener;
mContext = context;
mResources = context.getResources();
mColumnCount = numCols;
mNumFrequents = 0;
mMaxTiledRows = maxTiledRows;
-
+ mContactEntries = new ArrayList<ContactEntry>();
// Converting padding in dips to padding in pixels
mPaddingInPixels = mContext.getResources()
.getDimensionPixelSize(R.dimen.contact_tile_divider_padding);
+
+ // Initiates all animations.
+ mAlphaAnimation = ObjectAnimator.ofFloat(null, "alpha", 1.f).setDuration(ANIMATION_LENGTH);
+
+ mTranslateHorizontalAnimation = ObjectAnimator.ofFloat(null, "translationX", 0.f).
+ setDuration(ANIMATION_LENGTH);
+
+ mTranslateVerticalAnimation = ObjectAnimator.ofFloat(null, "translationY", 0.f).setDuration(
+ ANIMATION_LENGTH);
+
bindColumnIndices();
}
@@ -114,6 +148,20 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
}
/**
+ * Indicates whether a drag is in process.
+ *
+ * @param inDragging Boolean variable indicating whether there is a drag in process.
+ */
+ public void setInDragging(boolean inDragging) {
+ mInDragging = inDragging;
+ }
+
+ /** Gets whether the drag is in process. */
+ public boolean getInDragging() {
+ return mInDragging;
+ }
+
+ /**
* Sets the column indices for expected {@link Cursor}
* based on {@link DisplayType}.
*/
@@ -148,13 +196,49 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
* Else use {@link ContactTileLoaderFactory}
*/
public void setContactCursor(Cursor cursor) {
- mContactCursor = cursor;
- mNumStarred = getNumStarredContacts(cursor);
+ if (cursor != null && !cursor.isClosed()) {
+ mNumStarred = getNumStarredContacts(cursor);
+ saveNumFrequentsFromCursor(cursor);
+ saveCursorToCache(cursor);
- saveNumFrequentsFromCursor(cursor);
+ // cause a refresh of any views that rely on this data
+ notifyDataSetChanged();
+ }
+ }
- // cause a refresh of any views that rely on this data
- notifyDataSetChanged();
+ /**
+ * Saves the cursor data to the cache, to speed up UI changes.
+ *
+ * @param cursor Returned cursor with data to populate the view.
+ */
+ private void saveCursorToCache(Cursor cursor) {
+ mContactEntries.clear();
+ try {
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ final long id = cursor.getLong(mIdIndex);
+ final String photoUri = cursor.getString(mPhotoUriIndex);
+ final String lookupKey = cursor.getString(mLookupIndex);
+
+ final ContactEntry contact = new ContactEntry();
+ final String name = cursor.getString(mNameIndex);
+ contact.name = (name != null) ? name : mResources.getString(R.string.missing_name);
+ contact.status = cursor.getString(mStatusIndex);
+ contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
+ contact.lookupKey = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
+
+ // Set phone number and label
+ final int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
+ final String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
+ contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
+ phoneNumberCustomLabel);
+ contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
+ mContactEntries.add(contact);
+ }
+ } finally {
+ cursor.close();
+ }
}
/**
@@ -164,10 +248,6 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
* Returns 0 if {@link DisplayType#FREQUENT_ONLY}
*/
protected int getNumStarredContacts(Cursor cursor) {
- if (cursor == null || cursor.isClosed()) {
- throw new IllegalStateException("Unable to access cursor");
- }
-
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
if (cursor.getInt(mStarredIndex) == 0) {
@@ -180,32 +260,15 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
return cursor.getCount();
}
- protected ContactEntry createContactEntryFromCursor(Cursor cursor, int position) {
- // If the loader was canceled we will be given a null cursor.
- // In that case, show an empty list of contacts.
- if (cursor == null || cursor.isClosed() || cursor.getCount() <= position) return null;
-
- cursor.moveToPosition(position);
- long id = cursor.getLong(mIdIndex);
- String photoUri = cursor.getString(mPhotoUriIndex);
- String lookupKey = cursor.getString(mLookupIndex);
-
- ContactEntry contact = new ContactEntry();
- String name = cursor.getString(mNameIndex);
- contact.name = (name != null) ? name : mResources.getString(R.string.missing_name);
- contact.status = cursor.getString(mStatusIndex);
- contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
- contact.lookupKey = ContentUris.withAppendedId(
- Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
-
- // Set phone number and label
- int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
- String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
- contact.phoneLabel = (String) Phone.getTypeLabel(mResources, phoneNumberType,
- phoneNumberCustomLabel);
- contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
-
- return contact;
+ /**
+ * Loads a contact from the cached list.
+ *
+ * @param position Position of the Contact.
+ * @return Contact at the requested position.
+ */
+ protected ContactEntry getContactEntryFromCache(int position) {
+ if (mContactEntries.size() <= position) return null;
+ return mContactEntries.get(position);
}
/**
@@ -217,7 +280,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
@Override
public int getCount() {
- if (mContactCursor == null || mContactCursor.isClosed()) {
+ if (mContactEntries == null) {
return 0;
}
@@ -244,6 +307,14 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
return mColumnCount * mMaxTiledRows;
}
+ protected int getRowIndex(int entryIndex) {
+ if (entryIndex < mMaxTiledRows * mColumnCount) {
+ return entryIndex / mColumnCount;
+ } else {
+ return entryIndex - mMaxTiledRows * mColumnCount + mMaxTiledRows;
+ }
+ }
+
public int getColumnCount() {
return mColumnCount;
}
@@ -261,7 +332,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
// Contacts that appear as tiles
for (int columnCounter = 0; columnCounter < mColumnCount &&
contactIndex != maxContactsInTiles; columnCounter++) {
- resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ resultList.add(getContactEntryFromCache(contactIndex));
contactIndex++;
}
} else {
@@ -269,7 +340,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
// The actual position of the contact in the cursor is simply total the number of
// tiled contacts + the given position
contactIndex = maxContactsInTiles + position - 1;
- resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
+ resultList.add(getContactEntryFromCache(contactIndex));
}
return resultList;
@@ -295,7 +366,74 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
}
@Override
+ public void notifyDataSetChanged() {
+ if (DEBUG) {
+ Log.v(TAG, "nofigyDataSetChanged");
+ }
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * Configures the animation for each view.
+ *
+ * @param contactTileRowView The row to be animated.
+ * @param position The position of the row.
+ * @param itemViewType The type of the row.
+ */
+ private void configureAnimationToView(ContactTileRow contactTileRowView, int position,
+ int itemViewType) {
+ if (mInDragging) {
+ // If the one item above the row is being dragged, animates all following items to
+ // move up. If the item is a favorite tile, animate it to appear from right.
+ if (position >= getRowIndex(mDraggedEntryIndex)) {
+ if (itemViewType == ViewTypes.FREQUENT) {
+ mTranslateVerticalAnimation.setTarget(contactTileRowView);
+ mTranslateVerticalAnimation.setFloatValues(contactTileRowView.getHeight(), 0);
+ mTranslateVerticalAnimation.clone().start();
+ } else {
+ contactTileRowView.animateTilesAppearLeft(mDraggedEntryIndex -
+ position * mColumnCount);
+ }
+ }
+ } else if (mDropEntryIndex != -1) {
+ // If one item is dropped in front the row, animate all following rows to shift down.
+ // If the item is a favorite tile, animate it to appear from left.
+ if (position >= getRowIndex(mDropEntryIndex)) {
+ if (itemViewType == ViewTypes.FREQUENT) {
+ if (position == getRowIndex(mDropEntryIndex) || position == mMaxTiledRows) {
+ contactTileRowView.setVisibility(View.VISIBLE);
+ mAlphaAnimation.setTarget(contactTileRowView);
+ mAlphaAnimation.clone().start();
+ } else {
+ mTranslateVerticalAnimation.setTarget(contactTileRowView);
+ mTranslateVerticalAnimation.setFloatValues(-contactTileRowView.getHeight(),
+ 0);
+ mTranslateVerticalAnimation.clone().start();
+ }
+ } else {
+ contactTileRowView.animateTilesAppearRight(mDropEntryIndex + 1 -
+ position * mColumnCount);
+ }
+ }
+ } else if (mPotentialRemoveEntryIndex != -1) {
+ // If one item is to be removed above this row, animate the row to shift up. If it is
+ // a favorite contact tile, animate it to appear from right.
+ if (position >= getRowIndex(mPotentialRemoveEntryIndex)) {
+ if (itemViewType == ViewTypes.FREQUENT) {
+ mTranslateVerticalAnimation.setTarget(contactTileRowView);
+ mTranslateVerticalAnimation.setFloatValues(contactTileRowView.getHeight(), 0);
+ mTranslateVerticalAnimation.clone().start();
+ } else {
+ contactTileRowView.animateTilesAppearLeft(
+ mPotentialRemoveEntryIndex - position * mColumnCount);
+ }
+ }
+ }
+ }
+
+ @Override
public View getView(int position, View convertView, ViewGroup parent) {
+ Log.v(TAG, "get view for " + String.valueOf(position));
int itemViewType = getItemViewType(position);
ContactTileRow contactTileRowView = (ContactTileRow) convertView;
@@ -304,10 +442,13 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
if (contactTileRowView == null) {
// Creating new row if needed
- contactTileRowView = new ContactTileRow(mContext, itemViewType);
+ contactTileRowView = new ContactTileRow(mContext, itemViewType, position);
}
- contactTileRowView.configureRow(contactList, position == getCount() - 1);
+ contactTileRowView.configureRow(contactList, position, position == getCount() - 1);
+
+ configureAnimationToView(contactTileRowView, position, itemViewType);
+
return contactTileRowView;
}
@@ -347,22 +488,100 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
}
/**
+ * Temporarily removes a contact from the list for UI refresh. Stores data for this contact
+ * in the back-up variable.
+ *
+ * @param index Position of the contact to be removed.
+ */
+ public void popContactEntry(int index) {
+ if (index >= 0 && index < mContactEntries.size()) {
+ mDraggedEntry = mContactEntries.get(index);
+ mDraggedEntryIndex = index;
+ mContactEntries.remove(index);
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Drops the temporarily removed contact to the desired location in the list.
+ *
+ * @param index Location where the contact will be dropped.
+ */
+ public void dropContactEntry(int index) {
+ if (mDraggedEntry != null) {
+ if (index >= 0 && index <= mContactEntries.size()) {
+ mContactEntries.add(index, mDraggedEntry);
+ mDropEntryIndex = index;
+ } else if (mDraggedEntryIndex >= 0 && mDraggedEntryIndex <= mContactEntries.size()) {
+ /** If the index is invalid, falls back to the original position of the contact. */
+ mContactEntries.add(mDraggedEntryIndex, mDraggedEntry);
+ mDropEntryIndex = mDraggedEntryIndex;
+ }
+ mDraggedEntry = null;
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Sets an item to for pending removal. If the user does not click the undo button, the item
+ * will be removed at the next interaction.
+ *
+ * @param index Index of the item to be removed.
+ */
+ public void setPotentialRemoveEntryIndex(int index) {
+ mPotentialRemoveEntryIndex = index;
+ }
+
+ /**
+ * Removes a contact entry from the cache.
+ *
+ * @return True is an item is removed. False is there is no item to be removed.
+ */
+ public boolean removeContactEntry() {
+ if (mPotentialRemoveEntryIndex >= 0 && mPotentialRemoveEntryIndex < mContactEntries.size()) {
+ mContactEntries.remove(mPotentialRemoveEntryIndex);
+ notifyDataSetChanged();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Resets the item for pending removal.
+ */
+ public void undoPotentialRemoveEntryIndex() {
+ mPotentialRemoveEntryIndex = -1;
+ }
+
+ /**
+ * Clears all temporary variables at a new interaction.
+ */
+ public void cleanTempVariables() {
+ mDraggedEntryIndex = -1;
+ mDropEntryIndex = -1;
+ mDraggedEntry = null;
+ mPotentialRemoveEntryIndex = -1;
+ }
+
+ /**
* Acts as a row item composed of {@link ContactTileView}
*
* TODO: FREQUENT doesn't really need it. Just let {@link #getView} return
*/
- private class ContactTileRow extends FrameLayout {
+ public class ContactTileRow extends FrameLayout {
private int mItemViewType;
private int mLayoutResId;
private final int mRowPaddingStart;
private final int mRowPaddingEnd;
private final int mRowPaddingTop;
private final int mRowPaddingBottom;
+ private int mPosition;
- public ContactTileRow(Context context, int itemViewType) {
+ public ContactTileRow(Context context, int itemViewType, int position) {
super(context);
mItemViewType = itemViewType;
mLayoutResId = getLayoutResourceId(mItemViewType);
+ mPosition = position;
final Resources resources = mContext.getResources();
mRowPaddingStart = resources.getDimensionPixelSize(
@@ -386,8 +605,9 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
/**
* Configures the row to add {@link ContactEntry}s information to the views
*/
- public void configureRow(ArrayList<ContactEntry> list, boolean isLastRow) {
+ public void configureRow(ArrayList<ContactEntry> list, int position, boolean isLastRow) {
int columnCount = mItemViewType == ViewTypes.FREQUENT ? 1 : mColumnCount;
+ mPosition = position;
// Adding tiles to row and filling in contact information
for (int columnCounter = 0; columnCounter < columnCount; columnCounter++) {
@@ -398,11 +618,11 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
}
private void addTileFromEntry(ContactEntry entry, int childIndex, boolean isLastRow) {
- final ContactTileView contactTile;
+ final PhoneFavoriteTileView contactTile;
if (getChildCount() <= childIndex) {
- contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
+ contactTile = (PhoneFavoriteTileView) inflate(mContext, mLayoutResId, null);
// Note: the layoutparam set here is only actually used for FREQUENT.
// We override onMeasure() for STARRED and we don't care the layout param there.
final Resources resources = mContext.getResources();
@@ -411,18 +631,17 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(
- resources.getDimensionPixelSize(R.dimen.detail_item_side_margin),
- 0,
- resources.getDimensionPixelSize(R.dimen.detail_item_side_margin),
- 0);
+ resources.getDimensionPixelSize(R.dimen.detail_item_side_margin), 0,
+ resources.getDimensionPixelSize(R.dimen.detail_item_side_margin), 0);
contactTile.setLayoutParams(params);
contactTile.setPhotoManager(mPhotoManager);
contactTile.setListener(mListener);
addView(contactTile);
} else {
- contactTile = (ContactTileView) getChildAt(childIndex);
+ contactTile = (PhoneFavoriteTileView) getChildAt(childIndex);
}
contactTile.loadFromContact(entry);
+ contactTile.setId(childIndex);
switch (mItemViewType) {
case ViewTypes.TOP:
// Setting divider visibilities
@@ -436,6 +655,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
default:
break;
}
+ contactTile.setupFavoriteContactCard();
}
@Override
@@ -518,6 +738,65 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter {
}
setMeasuredDimension(width, imageSize + getPaddingTop() + getPaddingBottom());
}
+
+ /**
+ * Gets the index of the item at the specified coordinates.
+ *
+ * @param itemX X-coordinate of the selected item.
+ * @param itemY Y-coordinate of the selected item.
+ * @return Index of the selected item in the cached array.
+ */
+ public int getItemIndex(float itemX, float itemY) {
+ if (mPosition < mMaxTiledRows) {
+ final Rect childRect = new Rect();
+ if (DEBUG) {
+ Log.v(TAG, String.valueOf(itemX) + " " + String.valueOf(itemY));
+ }
+ for (int i = 0; i < getChildCount(); ++i) {
+ /** If the row contains multiple tiles, checks each tile to see if the point
+ * is contained in the tile. */
+ getChildAt(i).getHitRect(childRect);
+ if (DEBUG) {
+ Log.v(TAG, childRect.toString());
+ }
+ if (childRect.contains((int)itemX, (int)itemY)) {
+ /** If the point is contained in the rectangle, computes the index of the
+ * item in the cached array. */
+ return i + (mPosition) * mColumnCount;
+ }
+ }
+ } else {
+ /** If the selected item is one of the rows, compute the index. */
+ return (mPosition - mMaxTiledRows) + mColumnCount * mMaxTiledRows;
+ }
+ return -1;
+ }
+
+ public PhoneFavoritesTileAdapter getTileAdapter() {
+ return PhoneFavoritesTileAdapter.this;
+ }
+
+ public void animateTilesAppearLeft(int index) {
+ for (int i = index; i < getChildCount(); ++i) {
+ View childView = getChildAt(i);
+ mTranslateHorizontalAnimation.setTarget(childView);
+ mTranslateHorizontalAnimation.setFloatValues(childView.getWidth(), 0);
+ mTranslateHorizontalAnimation.clone().start();
+ }
+ }
+
+ public void animateTilesAppearRight(int index) {
+ for (int i = index; i < getChildCount(); ++i) {
+ View childView = getChildAt(i);
+ mTranslateHorizontalAnimation.setTarget(childView);
+ mTranslateHorizontalAnimation.setFloatValues(-childView.getWidth(), 0);
+ mTranslateHorizontalAnimation.clone().start();
+ }
+ }
+
+ public int getPosition() {
+ return mPosition;
+ }
}
protected static class ViewTypes {