blob: daba39e6dd65dd6cf67aae69e7ef96d1a96ad187 [file] [log] [blame]
/*
* Copyright (C) 2011 Google Inc.
* Licensed to 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.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}
* and {@link com.android.dialer.calllog.CallLogAdapter} into a single list.
*/
public class PhoneFavoriteMergedAdapter extends BaseAdapter {
private class CustomDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
notifyDataSetChanged();
}
}
private static final String TAG = PhoneFavoriteMergedAdapter.class.getSimpleName();
private static final int TILE_INTERACTION_TEASER_VIEW_POSITION = 2;
private static final int TILE_INTERACTION_TEASER_VIEW_ID = -2;
private static final int ALL_CONTACTS_BUTTON_ITEM_ID = -1;
private final PhoneFavoritesTileAdapter mContactTileAdapter;
private final CallLogAdapter mCallLogAdapter;
private final View mShowAllContactsButton;
private final PhoneFavoriteFragment mFragment;
private final TileInteractionTeaserView mTileInteractionTeaserView;
private final int mCallLogPadding;
private final Context mContext;
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);
mFragment.dismissShortcut(((View) view.getParent()).getHeight());
}
@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,
PhoneFavoriteFragment fragment,
PhoneFavoritesTileAdapter contactTileAdapter,
CallLogAdapter callLogAdapter,
View showAllContactsButton,
TileInteractionTeaserView tileInteractionTeaserView) {
final Resources resources = context.getResources();
mContext = context;
mFragment = fragment;
mCallLogPadding = resources.getDimensionPixelSize(R.dimen.recent_call_log_item_padding);
mContactTileAdapter = contactTileAdapter;
mCallLogAdapter = callLogAdapter;
mObserver = new CustomDataSetObserver();
mCallLogAdapter.registerDataSetObserver(mObserver);
mContactTileAdapter.registerDataSetObserver(mObserver);
mShowAllContactsButton = showAllContactsButton;
mTileInteractionTeaserView = tileInteractionTeaserView;
mCallLogQueryHandler = new CallLogQueryHandler(mContext.getContentResolver(),
mCallLogQueryHandlerListener);
}
@Override
public int getCount() {
if (mContactTileAdapter.getCount() > 0) {
return mContactTileAdapter.getCount() + mCallLogAdapter.getCount() + 1 +
getTeaserViewCount();
} else {
return mCallLogAdapter.getCount();
}
}
@Override
public Object getItem(int position) {
final int callLogAdapterCount = mCallLogAdapter.getCount();
if (callLogAdapterCount > 0) {
if (position < callLogAdapterCount) {
return mCallLogAdapter.getItem(position);
}
}
// Set position to the position of the actual favorite contact in the favorites adapter
position = getAdjustedFavoritePosition(position, callLogAdapterCount);
return mContactTileAdapter.getItem(position);
}
/**
* In order to ensure that items have stable ids (for animation purposes), we need to
* guarantee that every single item has a unique ID, even across data set changes.
*
* These are the ranges of IDs reserved for each item type.
*
* -(N + 1) to -3: CallLogAdapterItems, where N is equal to the number of call log items
* -2: Teaser
* -1: All contacts button
* 0 to (N -1): Rows of tiled contacts, where N is equal to the max rows of tiled contacts
* N to infinity: Rows of regular contacts. Their item id is calculated by N + contact_id,
* where contact_id is guaranteed to never be negative.
*/
@Override
public long getItemId(int position) {
final int callLogAdapterCount = mCallLogAdapter.getCount();
if (position < callLogAdapterCount) {
// Call log items are not animated, so reusing their position for IDs is fine.
return ALL_CONTACTS_BUTTON_ITEM_ID - 2 - position;
} else if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount &&
mTileInteractionTeaserView.getShouldDisplayInList()){
return TILE_INTERACTION_TEASER_VIEW_ID;
} else if (position < (callLogAdapterCount + mContactTileAdapter.getCount() +
getTeaserViewCount())) {
return mContactTileAdapter.getItemId(
getAdjustedFavoritePosition(position, callLogAdapterCount));
} else {
// All contacts button
return ALL_CONTACTS_BUTTON_ITEM_ID;
}
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getViewTypeCount() {
return (mContactTileAdapter.getViewTypeCount() + /* Favorite and frequent */
mCallLogAdapter.getViewTypeCount() + /* Recent call log */
getTeaserViewCount() + /* Teaser */
1); /* Show all contacts button. */
}
@Override
public int getItemViewType(int position) {
final int callLogAdapterCount = mCallLogAdapter.getCount();
if (position < callLogAdapterCount) {
// View type of the call log adapter is the last view type of the contact tile adapter
// + 1
return mContactTileAdapter.getViewTypeCount();
} else if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount &&
mTileInteractionTeaserView.getShouldDisplayInList()) {
// View type of the teaser row is the last view type of the contact tile adapter + 3
return mContactTileAdapter.getViewTypeCount() + 2;
} else if (position < getCount() - 1) {
return mContactTileAdapter.getItemViewType(
getAdjustedFavoritePosition(position, callLogAdapterCount));
} else {
// View type of the show all contact button is the last view type of the contact tile
// adapter + 2
return mContactTileAdapter.getViewTypeCount() + 1;
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int callLogAdapterCount = mCallLogAdapter.getCount();
if ((position == getCount() - 1) && (mContactTileAdapter.getCount() > 0)) {
return mShowAllContactsButton;
}
if (mTileInteractionTeaserView.getShouldDisplayInList()) {
if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount) {
return mTileInteractionTeaserView;
}
}
if (callLogAdapterCount > 0) {
if (position == 0) {
final SwipeableCallLogRow wrapper;
if (convertView == null) {
wrapper = new SwipeableCallLogRow(mContext);
wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener);
} else {
wrapper = (SwipeableCallLogRow) convertView;
}
// Special case wrapper view for the most recent call log item. This allows
// us to create a card-like effect for the more recent call log item in
// the PhoneFavoriteMergedAdapter, but keep the original look of the item in
// the CallLogAdapter.
final View view = mCallLogAdapter.getView(position, convertView == null ?
null : wrapper.getChildAt(0), parent);
wrapper.removeAllViews();
final View callLogItem = view.findViewById(R.id.call_log_list_item);
// Reset the internal call log item view if it is being recycled
callLogItem.setTranslationX(0);
callLogItem.setAlpha(1);
wrapper.addView(view);
return wrapper;
}
}
// Set position to the position of the actual favorite contact in the
// favorites adapter
position = getAdjustedFavoritePosition(position, callLogAdapterCount);
// Favorites section
final View view = mContactTileAdapter.getView(position, convertView, parent);
if (position >= mContactTileAdapter.getMaxTiledRows()) {
final FrameLayout frameLayout = (FrameLayout) view;
final View child = frameLayout.getChildAt(0);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
child.setLayoutParams(params);
}
return view;
}
@Override
public boolean areAllItemsEnabled() {
return mCallLogAdapter.areAllItemsEnabled() && mContactTileAdapter.areAllItemsEnabled();
}
@Override
public boolean isEnabled(int position) {
final int callLogAdapterCount = mCallLogAdapter.getCount();
if (position < callLogAdapterCount) {
return mCallLogAdapter.isEnabled(position);
} else { // For favorites section
return mContactTileAdapter.isEnabled(
getAdjustedFavoritePosition(position, callLogAdapterCount));
}
}
private int getAdjustedFavoritePosition(int position, int callLogAdapterCount) {
if (position - callLogAdapterCount > TILE_INTERACTION_TEASER_VIEW_POSITION &&
mTileInteractionTeaserView.getShouldDisplayInList()) {
return position - callLogAdapterCount - 1;
} else {
return position - callLogAdapterCount;
}
}
private int getTeaserViewCount() {
return (mContactTileAdapter.getCount() > TILE_INTERACTION_TEASER_VIEW_POSITION &&
mTileInteractionTeaserView.getShouldDisplayInList() ? 1 : 0);
}
/**
* 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.MATCH_PARENT,
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) {
// We do this so the underlying ScrollView knows that it won't get
// the chance to intercept events anymore
requestDisallowInterceptTouchEvent(true);
}
@Override
public void onChildDismissed(View v) {
if (v != null && mOnItemSwipeListener != null) {
mOnItemSwipeListener.onSwipe(v);
}
}
@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;
}
}
}