From 3011bc13a78b918df9c295e5c55ebe2ca09dab13 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Fri, 6 Sep 2013 13:29:18 -0700 Subject: Make the call log shortcut swipeable Bug: 10365541 Change-Id: I0301644d3fbb9a0284bbe9b2c468a46474e8c6f8 --- .../android/dialer/calllog/CallLogFragment.java | 28 +--- .../dialer/calllog/CallLogNotificationsHelper.java | 55 ++++++++ .../dialer/calllog/CallLogQueryHandler.java | 33 ++++- .../android/dialer/list/PhoneFavoriteFragment.java | 2 +- .../dialer/list/PhoneFavoriteMergedAdapter.java | 148 +++++++++++++++++++-- 5 files changed, 220 insertions(+), 46 deletions(-) create mode 100644 src/com/android/dialer/calllog/CallLogNotificationsHelper.java (limited to 'src') diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java index 7168667e7..b68503064 100644 --- a/src/com/android/dialer/calllog/CallLogFragment.java +++ b/src/com/android/dialer/calllog/CallLogFragment.java @@ -26,8 +26,6 @@ import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.RemoteException; -import android.os.ServiceManager; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract; @@ -429,22 +427,6 @@ public class CallLogFragment extends ListFragment } } - /** Removes the missed call notifications. */ - private void removeMissedCallNotifications() { - try { - ITelephony telephony = - ITelephony.Stub.asInterface(ServiceManager.getService("phone")); - if (telephony != null) { - telephony.cancelMissedCallsNotification(); - } else { - Log.w(TAG, "Telephony service is null, can't call " + - "cancelMissedCallsNotification"); - } - } catch (RemoteException e) { - Log.e(TAG, "Failed to clear missed calls notification due to remote exception"); - } - } - /** Updates call data and notification state while leaving the call log tab. */ private void updateOnExit() { updateOnTransition(false); @@ -469,14 +451,8 @@ public class CallLogFragment extends ListFragment if (!onEntry) { mCallLogQueryHandler.markMissedCallsAsRead(); } - removeMissedCallNotifications(); - updateVoicemailNotifications(); + CallLogNotificationsHelper.removeMissedCallNotifications(); + CallLogNotificationsHelper.updateVoicemailNotifications(getActivity()); } } - - private void updateVoicemailNotifications() { - Intent serviceIntent = new Intent(getActivity(), CallLogNotificationsService.class); - serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS); - getActivity().startService(serviceIntent); - } } diff --git a/src/com/android/dialer/calllog/CallLogNotificationsHelper.java b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java new file mode 100644 index 000000000..6ce66f08f --- /dev/null +++ b/src/com/android/dialer/calllog/CallLogNotificationsHelper.java @@ -0,0 +1,55 @@ +/* + * 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.calllog; + +import android.content.Context; +import android.content.Intent; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import com.android.internal.telephony.ITelephony; + +/** + * Helper class operating on call log notifications. + */ +public class CallLogNotificationsHelper { + private static final String TAG = "CallLogNotificationsHelper"; + + /** Removes the missed call notifications. */ + public static void removeMissedCallNotifications() { + try { + ITelephony telephony = + ITelephony.Stub.asInterface(ServiceManager.getService("phone")); + if (telephony != null) { + telephony.cancelMissedCallsNotification(); + } else { + Log.w(TAG, "Telephony service is null, can't call " + + "cancelMissedCallsNotification"); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to clear missed calls notification due to remote exception"); + } + } + + /** Update the voice mail notifications. */ + public static void updateVoicemailNotifications(Context context) { + Intent serviceIntent = new Intent(context, CallLogNotificationsService.class); + serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS); + context.startService(serviceIntent); + } +} diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java index 91a2e5d2b..987dedf66 100644 --- a/src/com/android/dialer/calllog/CallLogQueryHandler.java +++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java @@ -132,16 +132,28 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler { mLogLimit = limit; } - /** * Fetches the list of calls from the call log for a given type. + * This call ignores the new or old state. *

* It will asynchronously update the content of the list view when the fetch completes. */ public void fetchCalls(int callType) { cancelFetch(); int requestId = newCallsRequest(); - fetchCalls(QUERY_CALLLOG_TOKEN, requestId, callType); + fetchCalls(QUERY_CALLLOG_TOKEN, requestId, callType , false /* newOnly */); + } + + /** + * Fetches the list of calls from the call log for a given type. + * This call fetches only the new (i.e. NEW = 1) ones. + *

+ * 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 fetchVoicemailStatus() { @@ -149,20 +161,29 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler { VoicemailStatusHelperImpl.PROJECTION, null, null, null); } - /** Fetches the list of calls in the call log, either the new one or the old ones. */ - private void fetchCalls(int token, int requestId, int callType) { + /** Fetches the list of calls in the call log. */ + private void fetchCalls(int token, int requestId, int callType, boolean newOnly) { // 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". - String selection = null; + StringBuilder where = new StringBuilder(); List selectionArgs = Lists.newArrayList(); + if (newOnly) { + where.append(Calls.NEW); + where.append(" = 1"); + } + if (callType > CALL_TYPE_ALL) { + if (where.length() > 0) { + where.append(" AND "); + } // Add a clause to fetch only items of type voicemail. - selection = String.format("(%s = ?)", Calls.TYPE); + where.append(String.format("(%s = ?)", Calls.TYPE)); selectionArgs.add(Integer.toString(callType)); } 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() .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit)) .build(); diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java index 11889bf8f..4ffb0303b 100644 --- a/src/com/android/dialer/list/PhoneFavoriteFragment.java +++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java @@ -199,7 +199,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen @Override public void onResume() { super.onResume(); - mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL); + mCallLogQueryHandler.fetchNewCalls(CallLogQueryHandler.CALL_TYPE_ALL); mCallLogAdapter.setLoading(true); getLoaderManager().getLoader(LOADER_ID_CONTACT_TILE).forceLoad(); } diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java index f8e1e716d..fba55a689 100644 --- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java +++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java @@ -18,14 +18,21 @@ package com.android.dialer.list; import android.content.Context; import android.content.res.Resources; +import android.database.Cursor; import android.database.DataSetObserver; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.FrameLayout; import com.android.dialer.R; import com.android.dialer.calllog.CallLogAdapter; +import com.android.dialer.calllog.CallLogNotificationsHelper; +import com.android.dialer.calllog.CallLogQueryHandler; +import com.android.dialer.list.SwipeHelper.OnItemGestureListener; +import com.android.dialer.list.SwipeHelper.SwipeHelperCallback; /** * An adapter that combines items from {@link com.android.contacts.common.list.ContactTileAdapter} @@ -54,6 +61,41 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter { private final DataSetObserver mObserver; + private final CallLogQueryHandler mCallLogQueryHandler; + + private final OnItemGestureListener mCallLogOnItemSwipeListener = + new OnItemGestureListener() { + @Override + public void onSwipe(View view) { + mCallLogQueryHandler.markNewCallsAsOld(); + mCallLogQueryHandler.markNewVoicemailsAsOld(); + CallLogNotificationsHelper.removeMissedCallNotifications(); + CallLogNotificationsHelper.updateVoicemailNotifications(mContext); + mCallLogQueryHandler.fetchNewCalls(CallLogQueryHandler.CALL_TYPE_ALL); + } + + @Override + public void onTouch() {} + + @Override + public boolean isSwipeEnabled() { + return true; + } + }; + + private final CallLogQueryHandler.Listener mCallLogQueryHandlerListener = + new CallLogQueryHandler.Listener() { + @Override + public void onVoicemailStatusFetched(Cursor statusCursor) {} + + @Override + public void onCallsFetched(Cursor combinedCursor) { + mCallLogAdapter.invalidateCache(); + mCallLogAdapter.changeCursor(combinedCursor); + mCallLogAdapter.notifyDataSetChanged(); + } + }; + public PhoneFavoriteMergedAdapter(Context context, PhoneFavoritesTileAdapter contactTileAdapter, CallLogAdapter callLogAdapter, @@ -69,6 +111,8 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter { mContactTileAdapter.registerDataSetObserver(mObserver); mLoadingView = loadingView; mShowAllContactsButton = showAllContactsButton; + mCallLogQueryHandler = new CallLogQueryHandler(mContext.getContentResolver(), + mCallLogQueryHandlerListener); } @Override @@ -160,11 +204,12 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter { if (callLogAdapterCount > 0) { if (position == 0) { - final FrameLayout wrapper; + final SwipeableCallLogRow wrapper; if (convertView == null) { - wrapper = new FrameLayout(mContext); + wrapper = new SwipeableCallLogRow(mContext); + wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener); } else { - wrapper = (FrameLayout) convertView; + wrapper = (SwipeableCallLogRow) convertView; } // Special case wrapper view for the most recent call log item. This allows @@ -174,17 +219,7 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter { final View view = mCallLogAdapter.getView(position, convertView == null ? null : wrapper.getChildAt(0), parent); wrapper.removeAllViews(); - view.setBackgroundResource(R.drawable.dialer_recent_card_bg); - - final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - - params.setMarginsRelative(mCallLogPadding, mCallLogPadding, mCallLogPadding, - mCallLogPadding); - view.setLayoutParams(params); wrapper.addView(view); - return wrapper; } // Set position to the position of the actual favorite contact in the @@ -227,4 +262,91 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter { private int getAdjustedFavoritePosition(int position, int callLogAdapterCount) { return position - callLogAdapterCount; } + + /** + * The swipeable call log row. + * See also {@link PhoneFavoritesTileAdapter.ContactTileRow}. + */ + private class SwipeableCallLogRow extends FrameLayout implements SwipeHelperCallback { + private SwipeHelper mSwipeHelper; + private OnItemGestureListener mOnItemSwipeListener; + + public SwipeableCallLogRow(Context context) { + super(context); + final float densityScale = getResources().getDisplayMetrics().density; + final float pagingTouchSlop = ViewConfiguration.get(context) + .getScaledPagingTouchSlop(); + mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, + densityScale, pagingTouchSlop); + } + + @Override + public void addView(View view) { + view.setBackgroundResource(R.drawable.dialer_recent_card_bg); + + final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT); + params.setMarginsRelative(mCallLogPadding, mCallLogPadding, mCallLogPadding, + mCallLogPadding); + view.setLayoutParams(params); + + super.addView(view); + } + + @Override + public View getChildAtPosition(MotionEvent ev) { + return getChildCount() > 0 ? getChildAt(0) : null; + } + + @Override + public View getChildContentView(View v) { + return v.findViewById(R.id.call_log_list_item); + } + + @Override + public void onScroll() {} + + @Override + public boolean canChildBeDismissed(View v) { + return true; + } + + @Override + public void onBeginDrag(View v) { + } + + @Override + public void onChildDismissed(View v) { + if (v != null && mOnItemSwipeListener != null) { + mOnItemSwipeListener.onSwipe(v); + } + removeAllViews(); + } + + @Override + public void onDragCancelled(View v) {} + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (mSwipeHelper != null) { + return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); + } else { + return super.onInterceptTouchEvent(ev); + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (mSwipeHelper != null) { + return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev); + } else { + return super.onTouchEvent(ev); + } + } + + public void setOnItemSwipeListener(OnItemGestureListener listener) { + mOnItemSwipeListener = listener; + } + } } -- cgit v1.2.3