merge in klp-release history after reset to klp-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 82734b6..99602f6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -271,7 +271,8 @@
</activity>
<service android:name="com.android.incallui.CallHandlerService"
- android:process="com.android.incallui">
+ android:process="com.android.incallui"
+ android:permission="android.permission.BIND_CALL_SERVICE">
<intent-filter>
<action android:name="com.android.services.telephony.common.ICallHandlerService" />
</intent-filter>
diff --git a/res/drawable/bottom_border_background_pressed.xml b/res/drawable/bottom_border_background_pressed.xml
new file mode 100644
index 0000000..0924914
--- /dev/null
+++ b/res/drawable/bottom_border_background_pressed.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:drawable="@drawable/bottom_border_background" />
+ <item android:drawable="@*android:drawable/list_selector_background_transition_holo_light" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/contact_list_item_background.xml b/res/drawable/contact_list_item_background.xml
new file mode 100644
index 0000000..5637f4d
--- /dev/null
+++ b/res/drawable/contact_list_item_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/bottom_border_background_pressed" />
+ <item android:drawable="@drawable/bottom_border_background" />
+</selector>
diff --git a/res/layout/phone_favorites_fragment.xml b/res/layout/phone_favorites_fragment.xml
index 6023fc8..4d3abf4 100644
--- a/res/layout/phone_favorites_fragment.xml
+++ b/res/layout/phone_favorites_fragment.xml
@@ -45,5 +45,11 @@
android:layout_marginTop="@dimen/empty_message_top_margin"
android:textColor="?android:attr/textColorSecondary"
android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <ImageView
+ android:id="@+id/contact_tile_drag_shadow_overlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"/>
</FrameLayout>
</LinearLayout>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index f0b4d82..7d7071e 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -32,7 +32,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Gesprekkenoverzicht is leeg"</string>
<string name="clearCallLogConfirmation_title" msgid="6427524640461816332">"Oproeplog wissen?"</string>
<string name="clearCallLogConfirmation" msgid="5043563133171583152">"Al uw oproepgegevens worden verwijderd."</string>
- <string name="clearCallLogProgress_title" msgid="8365943000154295771">"Oproeplogboek wissen..."</string>
+ <string name="clearCallLogProgress_title" msgid="8365943000154295771">"Gesprekkenlijst wissen..."</string>
<plurals name="notification_voicemail_title">
<item quantity="one" msgid="1746619685488504230">"Voicemail"</item>
<item quantity="other" msgid="5513481419205061254">"<xliff:g id="COUNT">%1$d</xliff:g> voicemails"</item>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2717049..90ed648 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -24,12 +24,12 @@
<string name="menu_sendTextMessage" msgid="6937343460284499306">"发送短信"</string>
<string name="recentCalls_callNumber" msgid="1756372533999226126">"呼叫<xliff:g id="NAME">%s</xliff:g>"</string>
<string name="recentCalls_editNumberBeforeCall" msgid="7756171675833267857">"呼叫之前编辑号码"</string>
- <string name="recentCalls_addToContact" msgid="1429899535546487008">"添加到“联系人”"</string>
+ <string name="recentCalls_addToContact" msgid="1429899535546487008">"添加到通讯录"</string>
<string name="recentCalls_removeFromRecentList" msgid="401662244636511330">"从通话记录中删除"</string>
<string name="recentCalls_deleteAll" msgid="6352364392762163704">"清除通话记录"</string>
<string name="recentCalls_trashVoicemail" msgid="7604696960787435655">"删除语音邮件"</string>
<string name="recentCalls_shareVoicemail" msgid="1416112847592942840">"分享语音邮件"</string>
- <string name="recentCalls_empty" msgid="247053222448663107">"通话记录为空。"</string>
+ <string name="recentCalls_empty" msgid="247053222448663107">"没有通话记录。"</string>
<string name="clearCallLogConfirmation_title" msgid="6427524640461816332">"要清除通话记录吗?"</string>
<string name="clearCallLogConfirmation" msgid="5043563133171583152">"系统将删除您的所有通话记录。"</string>
<string name="clearCallLogProgress_title" msgid="8365943000154295771">"正在清除通话记录..."</string>
@@ -89,7 +89,7 @@
<string name="menu_show_missed_only" msgid="154473166059743996">"仅显示未接来电"</string>
<string name="menu_show_voicemails_only" msgid="1898421289561435703">"只显示语音邮件"</string>
<string name="menu_show_all_calls" msgid="7560347482073345885">"显示所有通话"</string>
- <string name="add_2sec_pause" msgid="9214012315201040129">"暂停时间延长 2 秒"</string>
+ <string name="add_2sec_pause" msgid="9214012315201040129">"暂停时间延长2秒"</string>
<string name="add_wait" msgid="3360818652790319634">"延长等待时间"</string>
<string name="call_settings" msgid="7666474782093693667">"设置"</string>
<string name="menu_newContact" msgid="1209922412763274638">"新建联系人"</string>
diff --git a/res/values/animation_constants.xml b/res/values/animation_constants.xml
index 77b7627..b41b316 100644
--- a/res/values/animation_constants.xml
+++ b/res/values/animation_constants.xml
@@ -15,7 +15,7 @@
~ limitations under the License
-->
<resources>
- <integer name="fade_duration">250</integer>
+ <integer name="fade_duration">300</integer>
<!-- Swipe constants -->
<integer name="swipe_escape_velocity">100</integer>
@@ -27,4 +27,4 @@
<dimen name="min_swipe">5dip</dimen>
<dimen name="min_vert">10dip</dimen>
<dimen name="min_lock">20dip</dimen>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 4680038..4c747c9 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -639,12 +639,15 @@
return;
}
- if (mDialpadFragment != null && (phoneIsInUse() || isDialIntent(intent))) {
- mDialpadFragment.setStartedFromNewIntent(true);
- if (!mDialpadFragment.isVisible()) {
- mInCallDialpadUp = true;
+ if (mDialpadFragment != null) {
+ final boolean phoneIsInUse = phoneIsInUse();
+ if (phoneIsInUse || isDialIntent(intent)) {
+ mDialpadFragment.setStartedFromNewIntent(true);
+ if (phoneIsInUse && !mDialpadFragment.isVisible()) {
+ mInCallDialpadUp = true;
+ }
+ showDialpadFragment(false);
}
- showDialpadFragment(false);
}
}
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index ce27ab7..c8e2613 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -432,8 +432,7 @@
updateOnTransition(true);
}
- // TODO krelease: Figure out if we still need this. If so, it should be probably be moved to
- // the call log activity instead, or done only in a single call log fragment.
+ // TODO: Move to CallLogActivity
private void updateOnTransition(boolean onEntry) {
// We don't want to update any call data when keyguard is on because the user has likely not
// seen the new calls yet.
@@ -441,6 +440,7 @@
if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()) {
// On either of the transitions we update the missed call and voicemail notifications.
// While exiting we additionally consume all missed calls (by marking them as read).
+ mCallLogQueryHandler.markNewCallsAsOld();
if (!onEntry) {
mCallLogQueryHandler.markMissedCallsAsRead();
}
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 987dedf..def3c97 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -138,22 +138,14 @@
* <p>
* It will asynchronously update the content of the list view when the fetch completes.
*/
- public void fetchCalls(int callType) {
+ public void fetchCalls(int callType, long newerThan) {
cancelFetch();
int requestId = newCallsRequest();
- fetchCalls(QUERY_CALLLOG_TOKEN, requestId, callType , false /* newOnly */);
+ fetchCalls(QUERY_CALLLOG_TOKEN, requestId, callType, false /* newOnly */, newerThan);
}
- /**
- * Fetches the list of calls from the call log for a given type.
- * This call fetches only the new (i.e. NEW = 1) ones.
- * <p>
- * It will asynchronously update the content of the list view when the fetch completes.
- */
- public void fetchNewCalls(int callType) {
- cancelFetch();
- int requestId = newCallsRequest();
- fetchCalls(QUERY_CALLLOG_TOKEN, requestId, callType , true /* newOnly */);
+ public void fetchCalls(int callType) {
+ fetchCalls(callType, 0);
}
public void fetchVoicemailStatus() {
@@ -162,7 +154,8 @@
}
/** Fetches the list of calls in the call log. */
- private void fetchCalls(int token, int requestId, int callType, boolean newOnly) {
+ private void fetchCalls(int token, int requestId, int callType, boolean newOnly,
+ long newerThan) {
// We need to check for NULL explicitly otherwise entries with where READ is NULL
// may not match either the query or its negation.
// We consider the calls that are not yet consumed (i.e. IS_READ = 0) as "new".
@@ -180,8 +173,18 @@
}
// Add a clause to fetch only items of type voicemail.
where.append(String.format("(%s = ?)", Calls.TYPE));
+ // Add a clause to fetch only items newer than the requested date
selectionArgs.add(Integer.toString(callType));
}
+
+ if (newerThan > 0) {
+ if (where.length() > 0) {
+ where.append(" AND ");
+ }
+ where.append(String.format("(%s > ?)", Calls.DATE));
+ selectionArgs.add(Long.toString(newerThan));
+ }
+
final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;
final String selection = where.length() > 0 ? where.toString() : null;
Uri uri = Calls.CONTENT_URI_WITH_VOICEMAIL.buildUpon()
diff --git a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java b/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java
deleted file mode 100644
index f0e97ac..0000000
--- a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.graphics.Rect;
-import android.util.Log;
-import android.view.DragEvent;
-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;
-
- /**
- * 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;
- }
-
- /**
- * @return The item index in {@link #mTileAdapter} for the given {@link DragEvent}.
- * Returns -1 if {@link #mTileAdapter} is not in dragging or index can not be found.
- */
- private int getDragItemIndex(DragEvent event) {
- int itemIndex = -1;
- if (mTileAdapter != null && mContactTileRow != null
- && !mTileAdapter.getInDragging()) {
- mX = event.getX();
- mY = event.getY();
- if (DEBUG) {
- Log.v(TAG, String.valueOf(mX) + "; " + String.valueOf(mY));
- }
-
- final int[] rowLocation = new int[2];
- mContactTileRow.getLocationOnScreen(rowLocation);
-
- final Rect locationRect = new Rect(rowLocation[0], rowLocation[1],
- rowLocation[0] + mContactTileRow.getWidth(),
- rowLocation[1] + mContactTileRow.getHeight());
-
- if (locationRect.contains((int) mX, (int) mY)) {
- // Finds out which item is being dragged.
- // Computes relative coordinates as we get absolute coordinates.
- itemIndex = mContactTileRow.getItemIndex(
- mX - rowLocation[0], mY - rowLocation[1]);
- if (DEBUG) {
- Log.v(TAG, "Start dragging " + String.valueOf(itemIndex));
- }
- }
- }
- return itemIndex;
- }
-
- @Override
- public boolean onDrag(View v, DragEvent event) {
- if (DEBUG) {
- Log.v(TAG, event.toString());
- }
- // Handles drag events.
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_STARTED:
- final int itemIndex = getDragItemIndex(event);
- if (itemIndex != -1) {
- // Indicates a drag has started.
- mTileAdapter.setInDragging(true);
-
- // Temporarily pops out the Contact entry.
- mTileAdapter.popContactEntry(itemIndex);
- }
- break;
- case DragEvent.ACTION_DRAG_ENTERED:
- break;
- case DragEvent.ACTION_DRAG_EXITED:
- break;
- case DragEvent.ACTION_DROP:
- // Indicates a drag has finished.
- if (mTileAdapter != null && mContactTileRow != null) {
- mTileAdapter.setInDragging(false);
- // The drop to position has been reported to the adapter
- // via {@link DragEvent#ACTION_DRAG_LOCATION} events in ListView.
- mTileAdapter.handleDrop();
- }
- break;
- case DragEvent.ACTION_DRAG_ENDED:
- if (mTileAdapter != null && mTileAdapter.getInDragging()) {
- // If the drag and drop ends when the drop happens outside of any rows,
- // we will end the drag here and put the item back to where it was dragged
- // from before.
- mTileAdapter.setInDragging(false);
- mTileAdapter.handleDrop();
- }
- break;
- case DragEvent.ACTION_DRAG_LOCATION:
- break;
- default:
- break;
- }
- return true;
- }
- }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index 270ed63..beeb320 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -18,9 +18,12 @@
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
+import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
+import android.content.SharedPreferences;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
@@ -33,6 +36,7 @@
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
@@ -41,7 +45,9 @@
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.list.ContactEntry;
import com.android.contacts.common.list.ContactTileView;
+import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
+import com.android.dialer.calllog.CallLogQuery;
import com.android.dialer.calllog.ContactInfoHelper;
import com.android.dialer.calllog.CallLogAdapter;
import com.android.dialer.calllog.CallLogQueryHandler;
@@ -66,13 +72,16 @@
private static final String TAG = PhoneFavoriteFragment.class.getSimpleName();
private static final boolean DEBUG = true;
- private static final int ANIMATION_DURATION = 300;
+ private int mAnimationDuration;
/**
* Used with LoaderManager.
*/
private static int LOADER_ID_CONTACT_TILE = 1;
+ private static final String KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE =
+ "key_last_dismissed_call_shortcut_date";
+
public interface OnShowAllContactsListener {
public void onShowAllContacts();
}
@@ -156,6 +165,17 @@
*/
private View mEmptyView;
+ /**
+ * Call shortcuts older than this date (persisted in shared preferences) will not show up in
+ * at the top of the screen
+ */
+ private long mLastCallShortcutDate = 0;
+
+ /**
+ * The date of the current call shortcut that is showing on screen.
+ */
+ private long mCurrentCallShortcutDate = 0;
+
private final ContactTileView.Listener mContactTileAdapterListener =
new ContactTileAdapterListener();
private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
@@ -182,6 +202,7 @@
if (DEBUG) Log.d(TAG, "onCreate()");
super.onCreate(savedState);
+ mAnimationDuration = getResources().getInteger(R.integer.fade_duration);
mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(),
this, 1);
final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
@@ -193,6 +214,11 @@
@Override
public void onResume() {
super.onResume();
+ final SharedPreferences prefs = getActivity().getSharedPreferences(
+ DialtactsActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+
+ mLastCallShortcutDate = prefs.getLong(KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE, 0);
+
fetchCalls();
mCallLogAdapter.setLoading(true);
getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad();
@@ -211,6 +237,11 @@
mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
mListView.setOnItemSwipeListener(mContactTileAdapter);
+ mListView.setOnDragDropListener(mContactTileAdapter);
+
+ final ImageView dragShadowOverlay =
+ (ImageView) listLayout.findViewById(R.id.contact_tile_drag_shadow_overlay);
+ mListView.setDragShadowOverlay(dragShadowOverlay);
mEmptyView = inflater.inflate(R.layout.phone_no_favorites, mListView, false);
@@ -224,7 +255,7 @@
});
mContactTileAdapter.setEmptyView(mEmptyView);
- mAdapter = new PhoneFavoriteMergedAdapter(getActivity(), mContactTileAdapter,
+ mAdapter = new PhoneFavoriteMergedAdapter(getActivity(), this, mContactTileAdapter,
mCallLogAdapter, mShowAllContactsButton);
mListView.setAdapter(mAdapter);
@@ -301,13 +332,20 @@
@Override
public void onCallsFetched(Cursor cursor) {
mCallLogAdapter.setLoading(false);
+
+ // Save the date of the most recent call log item
+ if (cursor != null && cursor.moveToFirst()) {
+ mCurrentCallShortcutDate = cursor.getLong(CallLogQuery.DATE);
+ }
+
mCallLogAdapter.changeCursor(cursor);
+
mAdapter.notifyDataSetChanged();
}
@Override
public void fetchCalls() {
- mCallLogQueryHandler.fetchNewCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+ mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL, mLastCallShortcutDate);
}
@Override
@@ -371,25 +409,13 @@
final ContactEntry entry = list.get(i);
final long itemId = mContactTileAdapter.getAdjustedItemId(entry.id);
- // Skip animation for this view if the caller specified that it should be
- // kept in place
- if (containsId(idsInPlace, itemId)) continue;
-
- Integer startLeft = mItemIdLeftMap.get(itemId);
- int left = child.getLeft();
- if (DEBUG) {
- Log.d(TAG, "Found itemId: " + itemId + " for tileview child " + i +
- " Left: " + left);
+ if (containsId(idsInPlace, itemId)) {
+ child.setAlpha(0.0f);
+ child.animate().alpha(1.0f)
+ .setDuration(mAnimationDuration)
+ .start();
+ break;
}
- if (startLeft != null) {
- if (startLeft != left) {
- int delta = startLeft - left;
- child.setTranslationX(delta);
- child.animate().setDuration(ANIMATION_DURATION).translationX(0);
- }
- }
- // No need to worry about horizontal offsets of new views that come into view since
- // there is no horizontal scrolling involved.
}
}
@@ -423,33 +449,12 @@
final long itemId = mAdapter.getItemId(position);
- // Skip animation for this view if the caller specified that it should be
- // kept in place
- if (containsId(idsInPlace, itemId)) continue;
-
- Integer startTop = mItemIdTopMap.get(itemId);
- final int top = child.getTop();
- if (DEBUG) {
- Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
- " Top: " + top);
- }
- int delta = 0;
- if (startTop != null) {
- if (startTop != top) {
- delta = startTop - top;
- }
- } else if (!mItemIdLeftMap.containsKey(itemId)) {
- // Animate new views along with the others. The catch is that they did not
- // exist in the start state, so we must calculate their starting position
- // based on neighboring views.
- int childHeight = child.getHeight() + mListView.getDividerHeight();
- startTop = top + (i > 0 ? childHeight : -childHeight);
- delta = startTop - top;
- }
-
- if (delta != 0) {
- child.setTranslationY(delta);
- child.animate().setDuration(ANIMATION_DURATION).translationY(0);
+ if (containsId(idsInPlace, itemId)) {
+ child.setAlpha(0.0f);
+ child.animate().alpha(1.0f)
+ .setDuration(mAnimationDuration)
+ .start();
+ break;
}
}
mItemIdTopMap.clear();
@@ -478,4 +483,13 @@
public void cacheOffsetsForDatasetChange() {
saveOffsets();
}
+
+ public void dismissShortcut() {
+ mLastCallShortcutDate = mCurrentCallShortcutDate;
+ final SharedPreferences prefs = getActivity().getSharedPreferences(
+ DialtactsActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
+ prefs.edit().putLong(KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE, mLastCallShortcutDate)
+ .apply();
+ fetchCalls();
+ }
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
index 00f645a..04bbe6b 100644
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteListView.java
@@ -17,8 +17,11 @@
package com.android.dialer.list;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Bitmap;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
@@ -26,6 +29,8 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.ListView;
import com.android.dialer.R;
@@ -47,6 +52,7 @@
private boolean mEnableSwipe = true;
private OnItemGestureListener mOnItemGestureListener;
+ private OnDragDropListener mOnDragDropListener;
private float mDensityScale;
private float mTouchSlop;
@@ -60,8 +66,25 @@
private final int DRAG_SCROLL_PX_UNIT = 10;
private boolean mIsDragScrollerRunning = false;
+ private int mTouchDownForDragStartX;
private int mTouchDownForDragStartY;
+ private Bitmap mDragShadowBitmap;
+ private ImageView mDragShadowOverlay;
+ private int mAnimationDuration;
+
+ // X and Y offsets inside the item from where the user grabbed to the
+ // child's left coordinate. This is used to aid in the drawing of the drag shadow.
+ private int mTouchOffsetToChildLeft;
+ private int mTouchOffsetToChildTop;
+
+ private int mDragShadowLeft;
+ private int mDragShadowTop;
+ private int mDragShadowWidth;
+ private int mDragShadowHeight;
+
+ private final float DRAG_SHADOW_ALPHA = 0.7f;
+
/**
* {@link #mTopScrollBound} and {@link mBottomScrollBound} will be
* offseted to the top / bottom by {@link #getHeight} * {@link #BOUND_GAP_RATIO} pixels.
@@ -80,6 +103,28 @@
}
};
+ private final AnimatorListenerAdapter mDragShadowOverAnimatorListener =
+ new AnimatorListenerAdapter() {
+ private void recycleDragShadow() {
+ if (mDragShadowBitmap != null) {
+ mDragShadowBitmap.recycle();
+ mDragShadowBitmap = null;
+ }
+ mDragShadowOverlay.setVisibility(GONE);
+ mDragShadowOverlay.setImageBitmap(null);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ recycleDragShadow();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ recycleDragShadow();
+ }
+ };
+
public PhoneFavoriteListView(Context context) {
this(context, null);
}
@@ -90,6 +135,7 @@
public PhoneFavoriteListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mAnimationDuration = context.getResources().getInteger(R.integer.fade_duration);
mDensityScale = getResources().getDisplayMetrics().density;
mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop();
mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this,
@@ -121,9 +167,14 @@
mOnItemGestureListener = listener;
}
+ public void setOnDragDropListener(OnDragDropListener listener) {
+ mOnDragDropListener = listener;
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchDownForDragStartX = (int) ev.getX();
mTouchDownForDragStartY = (int) ev.getY();
}
if (isSwipeEnabled()) {
@@ -147,24 +198,13 @@
@Override
public View getChildAtPosition(MotionEvent ev) {
- // find the view under the pointer, accounting for GONE views
- final int count = getChildCount();
- final int touchY = (int) ev.getY();
- View slidingChild;
- for (int childIdx = 0; childIdx < count; childIdx++) {
- slidingChild = getChildAt(childIdx);
- if (slidingChild.getVisibility() == GONE) {
- continue;
- }
- if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) {
- if (SwipeHelper.isSwipeable(slidingChild)) {
- // If this view is swipable in this listview, then return it. Otherwise
- // return a null view, which will simply be ignored by the swipe helper.
- return slidingChild;
- } else {
- return null;
- }
- }
+ final View view = getViewAtPosition((int) ev.getX(), (int) ev.getY());
+ if (view != null &&
+ SwipeHelper.isSwipeable(view) &&
+ view.getVisibility() != GONE) {
+ // If this view is swipable in this listview, then return it. Otherwise
+ // return a null view, which will simply be ignored by the swipe helper.
+ return view;
}
return null;
}
@@ -205,10 +245,18 @@
@Override
public boolean dispatchDragEvent(DragEvent event) {
- switch (event.getAction()) {
+ final int action = event.getAction();
+ final int eX = (int) event.getX();
+ final int eY = (int) event.getY();
+ switch (action) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ if (!handleDragStarted(mTouchDownForDragStartX, mTouchDownForDragStartY)) {
+ return false;
+ };
+ break;
case DragEvent.ACTION_DRAG_LOCATION:
- mLastDragY = (int) event.getY();
- handleDrag((int) event.getX(), mLastDragY);
+ mLastDragY = eY;
+ handleDragHovered(eX, eY);
// Kick off {@link #mScrollHandler} if it's not started yet.
if (!mIsDragScrollerRunning &&
// And if the distance traveled while dragging exceeds the touch slop
@@ -229,13 +277,36 @@
ensureScrollHandler();
mScrollHandler.removeCallbacks(mDragScroller);
mIsDragScrollerRunning = false;
+ // Either it's been a successful drop or it's ended with out drop.
+ if (action == DragEvent.ACTION_DROP ||
+ (action == DragEvent.ACTION_DRAG_ENDED && !event.getResult())) {
+ handleDragFinished(eX, eY);
+ }
break;
- case DragEvent.ACTION_DRAG_STARTED:
- // Not a receiver
default:
break;
}
- return super.dispatchDragEvent(event);
+ // This ListView will consumer the drag events on behalf of its children.
+ return true;
+ }
+
+ public void setDragShadowOverlay(ImageView overlay) {
+ mDragShadowOverlay = overlay;
+ }
+
+ /**
+ * Find the view under the pointer.
+ */
+ private View getViewAtPosition(int x, int y) {
+ final int count = getChildCount();
+ View child;
+ for (int childIdx = 0; childIdx < count; childIdx++) {
+ child = getChildAt(childIdx);
+ if (y >= child.getTop() && y <= child.getBottom()) {
+ return child;
+ }
+ }
+ return null;
}
private void ensureScrollHandler() {
@@ -244,29 +315,137 @@
}
}
- private void handleDrag(int x, int y) {
- // find the view under the pointer, accounting for GONE views
- final int count = getChildCount();
- View slidingChild;
- for (int childIdx = 0; childIdx < count; childIdx++) {
- slidingChild = getChildAt(childIdx);
- if (slidingChild.getVisibility() == GONE) {
- continue;
+ private FrameLayout.LayoutParams getDragShadowLayoutParams() {
+ final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ mDragShadowWidth, mDragShadowHeight);
+ lp.leftMargin = mDragShadowLeft;
+ lp.topMargin = mDragShadowTop;
+ return lp;
+ }
+
+ /**
+ * @return True if the drag is started.
+ */
+ private boolean handleDragStarted(int x, int y) {
+ final View child = getViewAtPosition(x, y);
+ if (!(child instanceof ContactTileRow)) {
+ // Bail early.
+ return false;
+ }
+
+ final ContactTileRow tile = (ContactTileRow) child;
+ final int itemIndex = tile.getItemIndex(x, y);
+ if (itemIndex != -1 && mOnDragDropListener != null) {
+ final PhoneFavoriteTileView tileView =
+ (PhoneFavoriteTileView) tile.getViewAtPosition(x, y);
+ if (mDragShadowOverlay == null) {
+ return false;
}
- if (y >= slidingChild.getTop() &&
- y <= slidingChild.getBottom() &&
- slidingChild instanceof ContactTileRow) {
- final ContactTileRow tile = (ContactTileRow) slidingChild;
- reportDragEnteredItemIndex(tile.getItemIndex(x, y));
+
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowBitmap = createDraggedChildBitmap(tileView);
+ if (mDragShadowBitmap == null) {
+ return false;
}
+
+ if (tileView instanceof PhoneFavoriteRegularRowView) {
+ mDragShadowLeft = tile.getLeft();
+ mDragShadowTop = tile.getTop();
+ } else {
+ // Square tile is relative to the contact tile,
+ // and contact tile is relative to this list view.
+ mDragShadowLeft = tileView.getLeft() + tileView.getParentRow().getLeft();
+ mDragShadowTop = tileView.getTop() + tileView.getParentRow().getTop();
+ }
+
+ mDragShadowWidth = tileView.getWidth();
+ mDragShadowHeight = tileView.getHeight();
+
+ mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
+ mDragShadowOverlay.setVisibility(VISIBLE);
+ mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
+
+ mDragShadowOverlay.setLayoutParams(getDragShadowLayoutParams());
+
+ // x and y passed in are the coordinates of where the user has touched down, calculate
+ // the offset to the top left coordinate of the dragged child. This will be used for
+ // drawing the drag shadow.
+ mTouchOffsetToChildLeft = x - mDragShadowLeft;
+ mTouchOffsetToChildTop = y - mDragShadowTop;
+
+ // invalidate to trigger a redraw of the drag shadow.
+ invalidate();
+
+ mOnDragDropListener.onDragStarted(itemIndex);
+ }
+
+ return true;
+ }
+
+ private void handleDragHovered(int x, int y) {
+ final View child = getViewAtPosition(x, y);
+ if (!(child instanceof ContactTileRow)) {
+ // Bail early.
+ return;
+ }
+
+ // Update the drag shadow location.
+ mDragShadowLeft = x - mTouchOffsetToChildLeft;
+ mDragShadowTop = y - mTouchOffsetToChildTop;
+
+ // Draw the drag shadow at its last known location if the drag shadow exists.
+ if (mDragShadowOverlay != null) {
+ mDragShadowOverlay.setLayoutParams(getDragShadowLayoutParams());
+ }
+
+ final ContactTileRow tile = (ContactTileRow) child;
+ final int itemIndex = tile.getItemIndex(x, y);
+ if (itemIndex != -1 && mOnDragDropListener != null) {
+ mOnDragDropListener.onDragHovered(itemIndex);
}
}
- private void reportDragEnteredItemIndex(int itemIndex) {
- final PhoneFavoriteMergedAdapter adapter =
- (PhoneFavoriteMergedAdapter) getAdapter();
- if (adapter != null) {
- adapter.reportDragEnteredItemIndex(itemIndex);
+ private void handleDragFinished(int x, int y) {
+ // Update the drag shadow location.
+ mDragShadowLeft = x - mTouchOffsetToChildLeft;
+ mDragShadowTop = y - mTouchOffsetToChildTop;
+
+ if (mDragShadowOverlay != null) {
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowOverlay.animate().alpha(0.0f)
+ .setDuration(mAnimationDuration)
+ .setListener(mDragShadowOverAnimatorListener)
+ .start();
}
+
+ if (mOnDragDropListener != null) {
+ mOnDragDropListener.onDragFinished();
+ }
+ }
+
+ private Bitmap createDraggedChildBitmap(View view) {
+ view.setDrawingCacheEnabled(true);
+ final Bitmap cache = view.getDrawingCache();
+
+ Bitmap bitmap = null;
+ if (cache != null) {
+ try {
+ bitmap = cache.copy(Bitmap.Config.ARGB_8888, false);
+ } catch (final OutOfMemoryError e) {
+ Log.w(LOG_TAG, "Failed to copy bitmap from Drawing cache", e);
+ bitmap = null;
+ }
+ }
+
+ view.destroyDrawingCache();
+ view.setDrawingCacheEnabled(false);
+
+ return bitmap;
+ }
+
+ public interface OnDragDropListener {
+ public void onDragStarted(int itemIndex);
+ public void onDragHovered(int itemIndex);
+ public void onDragFinished();
}
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
index 084a34e..cf2aeee 100644
--- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
@@ -53,6 +53,7 @@
private final PhoneFavoritesTileAdapter mContactTileAdapter;
private final CallLogAdapter mCallLogAdapter;
private final View mShowAllContactsButton;
+ private final PhoneFavoriteFragment mFragment;
private final int mCallLogPadding;
@@ -70,7 +71,7 @@
mCallLogQueryHandler.markNewVoicemailsAsOld();
CallLogNotificationsHelper.removeMissedCallNotifications();
CallLogNotificationsHelper.updateVoicemailNotifications(mContext);
- mCallLogQueryHandler.fetchNewCalls(CallLogQueryHandler.CALL_TYPE_ALL);
+ mFragment.dismissShortcut();
}
@Override
@@ -96,11 +97,13 @@
};
public PhoneFavoriteMergedAdapter(Context context,
+ PhoneFavoriteFragment fragment,
PhoneFavoritesTileAdapter contactTileAdapter,
CallLogAdapter callLogAdapter,
View showAllContactsButton) {
final Resources resources = context.getResources();
mContext = context;
+ mFragment = fragment;
mCallLogPadding = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_padding);
mContactTileAdapter = contactTileAdapter;
mCallLogAdapter = callLogAdapter;
@@ -346,10 +349,4 @@
mOnItemSwipeListener = listener;
}
}
-
- public void reportDragEnteredItemIndex(int itemIndex) {
- if (mContactTileAdapter != null) {
- mContactTileAdapter.reportDragEnteredItemIndex(itemIndex);
- }
- }
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
index 374f733..b4ad784 100644
--- a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
@@ -23,7 +23,6 @@
import com.android.contacts.common.util.ViewUtil;
import com.android.dialer.R;
-import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteDragListener;
import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
@@ -57,7 +56,7 @@
rowPaddingBottom = resources.getDimensionPixelSize(
R.dimen.favorites_row_bottom_padding);
- favoriteContactCard.setBackgroundResource(R.drawable.bottom_border_background);
+ favoriteContactCard.setBackgroundResource(R.drawable.contact_list_item_background);
favoriteContactCard.setPaddingRelative(rowPaddingStart, rowPaddingTop, rowPaddingEnd,
rowPaddingBottom);
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
index 76a0e35..57d258f 100644
--- a/src/com/android/dialer/list/PhoneFavoriteTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java
@@ -20,7 +20,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.content.ClipData;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -32,7 +31,7 @@
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.PhoneFavoriteDragAndDropListeners.PhoneFavoriteDragListener;
+import com.android.dialer.R;
import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
import com.android.dialer.list.PhoneFavoritesTileAdapter.ViewTypes;
@@ -49,7 +48,7 @@
private static final boolean DEBUG = false;
/** Length of all animations in miniseconds. */
- private static final int ANIMATION_LENGTH = 300;
+ private int mAnimationDuration;
/** The view that holds the front layer of the favorite contact card. */
private View mFavoriteContactCard;
@@ -68,6 +67,7 @@
public PhoneFavoriteTileView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mAnimationDuration = context.getResources().getInteger(R.integer.fade_duration);
}
public ContactTileRow getParentRow() {
@@ -94,20 +94,17 @@
public boolean onLongClick(View v) {
setPressed(false);
final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v;
- final ClipData data = ClipData.newPlainText("", "");
+ // NOTE The drag shadow is handled in the ListView.
if (view instanceof PhoneFavoriteRegularRowView) {
- // If the view is regular row, start drag the row view.
- final View.DragShadowBuilder shadowBuilder =
- new View.DragShadowBuilder(view.getParentRow());
final ContactTileRow parent = (ContactTileRow) view.getParentRow();
+ // If the view is regular row, start drag the row view.
// Drag is not available for the item exceeds the PIN_LIMIT.
if (parent.getRegularRowItemIndex() < PhoneFavoritesTileAdapter.PIN_LIMIT) {
- view.getParentRow().startDrag(data, shadowBuilder, null, 0);
+ parent.startDrag(null, new View.DragShadowBuilder(), null, 0);
}
} else {
// If the view is a tile view, start drag the tile.
- final View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
- view.startDrag(data, shadowBuilder, null, 0);
+ view.startDrag(null, new View.DragShadowBuilder(), null, 0);
}
return true;
}
@@ -136,7 +133,7 @@
mRemovalDialogue.setVisibility(VISIBLE);
mRemovalDialogue.setAlpha(0f);
final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(mRemovalDialogue, "alpha",
- 1.f).setDuration(ANIMATION_LENGTH);
+ 1.f).setDuration(mAnimationDuration);
fadeIn.addListener(new AnimatorListenerAdapter() {
@Override
@@ -162,9 +159,9 @@
// Animates back the favorite contact card.
final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(mFavoriteContactCard, "alpha", 1.f).
- setDuration(ANIMATION_LENGTH);
+ setDuration(mAnimationDuration);
final ObjectAnimator moveBack = ObjectAnimator.ofFloat(mFavoriteContactCard, "translationX",
- 0.f).setDuration(ANIMATION_LENGTH);
+ 0.f).setDuration(mAnimationDuration);
final AnimatorSet animSet = new AnimatorSet();
@@ -205,8 +202,6 @@
@Override
protected void onAttachedToWindow() {
mParentRow = (ContactTileRow) getParent();
- mParentRow.setOnDragListener(new PhoneFavoriteDragListener(mParentRow,
- mParentRow.getTileAdapter()));
}
@Override
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index 4eaeaf5..73d3c36 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -62,7 +62,7 @@
*
*/
public class PhoneFavoritesTileAdapter extends BaseAdapter implements
- SwipeHelper.OnItemGestureListener {
+ SwipeHelper.OnItemGestureListener, PhoneFavoriteListView.OnDragDropListener {
private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -609,7 +609,8 @@
public void handleDrop() {
boolean changed = false;
if (mDraggedEntry != null) {
- if (isIndexInBound(mDragEnteredEntryIndex)) {
+ if (isIndexInBound(mDragEnteredEntryIndex) &&
+ mDragEnteredEntryIndex != mDraggedEntryIndex) {
// Don't add the ContactEntry here (to prevent a double animation from occuring).
// When we receive a new cursor the list of contact entries will automatically be
// populated with the dragged ContactEntry at the correct spot.
@@ -970,27 +971,32 @@
return mPosition;
}
- @Override
- public View getChildAtPosition(MotionEvent ev) {
+ /**
+ * Find the view under the pointer.
+ */
+ public View getViewAtPosition(int x, int y) {
// find the view under the pointer, accounting for GONE views
final int count = getChildCount();
- final int touchX = (int) ev.getX();
- View slidingChild;
+ View view;
for (int childIdx = 0; childIdx < count; childIdx++) {
- slidingChild = getChildAt(childIdx);
- if (slidingChild.getVisibility() == GONE) {
- continue;
+ view = getChildAt(childIdx);
+ if (x >= view.getLeft() && x <= view.getRight()) {
+ return view;
}
- if (touchX >= slidingChild.getLeft() && touchX <= slidingChild.getRight()) {
- if (SwipeHelper.isSwipeable(slidingChild)) {
- // If this view is swipable, then return it. If not, because the removal
- // dialog is currently showing, then return a null view, which will simply
- // be ignored by the swipe helper.
- return slidingChild;
- } else {
- return null;
- }
- }
+ }
+ return null;
+ }
+
+ @Override
+ public View getChildAtPosition(MotionEvent ev) {
+ final View view = getViewAtPosition((int) ev.getX(), (int) ev.getY());
+ if (view != null &&
+ SwipeHelper.isSwipeable(view) &&
+ view.getVisibility() != GONE) {
+ // If this view is swipable, then return it. If not, because the removal
+ // dialog is currently showing, then return a null view, which will simply
+ // be ignored by the swipe helper.
+ return view;
}
return null;
}
@@ -1192,11 +1198,14 @@
return !mAwaitingRemove;
}
- public void setEmptyView(View emptyView) {
- mEmptyView = emptyView;
+ @Override
+ public void onDragStarted(int itemIndex) {
+ setInDragging(true);
+ popContactEntry(itemIndex);
}
- public void reportDragEnteredItemIndex(int itemIndex) {
+ @Override
+ public void onDragHovered(int itemIndex) {
if (mInDragging &&
mDragEnteredEntryIndex != itemIndex &&
isIndexInBound(itemIndex) &&
@@ -1204,4 +1213,14 @@
markDropArea(itemIndex);
}
}
+
+ @Override
+ public void onDragFinished() {
+ setInDragging(false);
+ handleDrop();
+ }
+
+ public void setEmptyView(View emptyView) {
+ mEmptyView = emptyView;
+ }
}