blob: ba291a00f6e899250e8c1a862b9974181e9576bb [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.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.SectionIndexer;
import com.android.contacts.common.list.ContactEntryListAdapter;
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.ContactTileAdapter;
import com.android.dialer.R;
/**
* An adapter that combines items from {@link com.android.contacts.common.list.ContactTileAdapter} and
* {@link com.android.contacts.common.list.ContactEntryListAdapter} into a single list. In between those two results,
* an account filter header will be inserted.
*/
public class PhoneFavoriteMergedAdapter extends BaseAdapter implements SectionIndexer {
private class CustomDataSetObserver extends DataSetObserver {
@Override
public void onChanged() {
notifyDataSetChanged();
}
}
private final ContactTileAdapter mContactTileAdapter;
private final ContactEntryListAdapter mContactEntryListAdapter;
private final View mAccountFilterHeaderContainer;
private final View mLoadingView;
private final int mItemPaddingLeft;
private final int mItemPaddingRight;
// Make frequent header consistent with account filter header.
private final int mFrequentHeaderPaddingTop;
private final DataSetObserver mObserver;
public PhoneFavoriteMergedAdapter(Context context,
ContactTileAdapter contactTileAdapter,
View accountFilterHeaderContainer,
ContactEntryListAdapter contactEntryListAdapter,
View loadingView) {
Resources resources = context.getResources();
mItemPaddingLeft = resources.getDimensionPixelSize(R.dimen.detail_item_side_margin);
mItemPaddingRight = resources.getDimensionPixelSize(R.dimen.list_visible_scrollbar_padding);
mFrequentHeaderPaddingTop = resources.getDimensionPixelSize(
R.dimen.contact_browser_list_top_margin);
mContactTileAdapter = contactTileAdapter;
mContactEntryListAdapter = contactEntryListAdapter;
mAccountFilterHeaderContainer = accountFilterHeaderContainer;
mObserver = new CustomDataSetObserver();
mContactTileAdapter.registerDataSetObserver(mObserver);
mContactEntryListAdapter.registerDataSetObserver(mObserver);
mLoadingView = loadingView;
}
@Override
public boolean isEmpty() {
// Cannot use the super's method here because we add extra rows in getCount() to account
// for headers
return mContactTileAdapter.getCount() + mContactEntryListAdapter.getCount() == 0;
}
@Override
public int getCount() {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
if (mContactEntryListAdapter.isLoading()) {
// Hide "all" contacts during its being loaded. Instead show "loading" view.
//
// "+2" for mAccountFilterHeaderContainer and mLoadingView
return contactTileAdapterCount + 2;
} else {
// "+1" for mAccountFilterHeaderContainer
return contactTileAdapterCount + contactEntryListAdapterCount + 1;
}
}
@Override
public Object getItem(int position) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
if (position < contactTileAdapterCount) { // For "tile" and "frequent" sections
return mContactTileAdapter.getItem(position);
} else if (position == contactTileAdapterCount) { // For "all" section's account header
return mAccountFilterHeaderContainer;
} else { // For "all" section
if (mContactEntryListAdapter.isLoading()) { // "All" section is being loaded.
return mLoadingView;
} else {
// "-1" for mAccountFilterHeaderContainer
final int localPosition = position - contactTileAdapterCount - 1;
return mContactTileAdapter.getItem(localPosition);
}
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getViewTypeCount() {
// "+2" for mAccountFilterHeaderContainer and mLoadingView
return (mContactTileAdapter.getViewTypeCount()
+ mContactEntryListAdapter.getViewTypeCount()
+ 2);
}
@Override
public int getItemViewType(int position) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
// There should be four kinds of types that are usually used, and one more exceptional
// type (IGNORE_ITEM_VIEW_TYPE), which sometimes comes from mContactTileAdapter.
//
// The four ordinary view types have the index equal to or more than 0, and less than
// mContactTileAdapter.getViewTypeCount()+ mContactEntryListAdapter.getViewTypeCount() + 2.
// (See also this class's getViewTypeCount())
//
// We have those values for:
// - The view types mContactTileAdapter originally has
// - The view types mContactEntryListAdapter originally has
// - mAccountFilterHeaderContainer ("all" section's account header), and
// - mLoadingView
//
// Those types should not be mixed, so we have a different range for each kinds of types:
// - Types for mContactTileAdapter ("tile" and "frequent" sections)
// They should have the index, >=0 and <mContactTileAdapter.getViewTypeCount()
//
// - Types for mContactEntryListAdapter ("all" sections)
// They should have the index, >=mContactTileAdapter.getViewTypeCount() and
// <(mContactTileAdapter.getViewTypeCount() + mContactEntryListAdapter.getViewTypeCount())
//
// - Type for "all" section's account header
// It should have the exact index
// mContactTileAdapter.getViewTypeCount()+ mContactEntryListAdapter.getViewTypeCount()
//
// - Type for "loading" view used during "all" section is being loaded.
// It should have the exact index
// mContactTileAdapter.getViewTypeCount()+ mContactEntryListAdapter.getViewTypeCount() + 1
//
// As an exception, IGNORE_ITEM_VIEW_TYPE (-1) will be remained as is, which will be used
// by framework's Adapter implementation and thus should be left as is.
if (position < contactTileAdapterCount) { // For "tile" and "frequent" sections
return mContactTileAdapter.getItemViewType(position);
} else if (position == contactTileAdapterCount) { // For "all" section's account header
return mContactTileAdapter.getViewTypeCount()
+ mContactEntryListAdapter.getViewTypeCount();
} else { // For "all" section
if (mContactEntryListAdapter.isLoading()) { // "All" section is being loaded.
return mContactTileAdapter.getViewTypeCount()
+ mContactEntryListAdapter.getViewTypeCount() + 1;
} else {
// "-1" for mAccountFilterHeaderContainer
final int localPosition = position - contactTileAdapterCount - 1;
final int type = mContactEntryListAdapter.getItemViewType(localPosition);
// IGNORE_ITEM_VIEW_TYPE must be handled differently.
return (type < 0) ? type : type + mContactTileAdapter.getViewTypeCount();
}
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
// Obtain a View relevant for that position, and adjust its horizontal padding. Each
// View has different implementation, so we use different way to control those padding.
if (position < contactTileAdapterCount) { // For "tile" and "frequent" sections
final View view = mContactTileAdapter.getView(position, convertView, parent);
final int frequentHeaderPosition = mContactTileAdapter.getFrequentHeaderPosition();
if (position < frequentHeaderPosition) { // "starred" contacts
// No padding adjustment.
} else if (position == frequentHeaderPosition) {
view.setPadding(mItemPaddingLeft, mFrequentHeaderPaddingTop,
mItemPaddingRight, view.getPaddingBottom());
} else {
// Views for "frequent" contacts use FrameLayout's margins instead of padding.
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);
params.setMargins(mItemPaddingLeft, 0, mItemPaddingRight, 0);
child.setLayoutParams(params);
}
return view;
} else if (position == contactTileAdapterCount) { // For "all" section's account header
mAccountFilterHeaderContainer.setPadding(mItemPaddingLeft,
mAccountFilterHeaderContainer.getPaddingTop(),
mItemPaddingRight,
mAccountFilterHeaderContainer.getPaddingBottom());
// Show a single "No Contacts" label under the "all" section account header
// if no contacts are displayed.
mAccountFilterHeaderContainer.findViewById(
R.id.contact_list_all_empty).setVisibility(
contactEntryListAdapterCount == 0 ? View.VISIBLE : View.GONE);
return mAccountFilterHeaderContainer;
} else { // For "all" section
if (mContactEntryListAdapter.isLoading()) { // "All" section is being loaded.
mLoadingView.setPadding(mItemPaddingLeft,
mLoadingView.getPaddingTop(),
mItemPaddingRight,
mLoadingView.getPaddingBottom());
return mLoadingView;
} else {
// "-1" for mAccountFilterHeaderContainer
final int localPosition = position - contactTileAdapterCount - 1;
final ContactListItemView itemView = (ContactListItemView)
mContactEntryListAdapter.getView(localPosition, convertView, null);
itemView.setPadding(mItemPaddingLeft, itemView.getPaddingTop(),
mItemPaddingRight, itemView.getPaddingBottom());
itemView.setSelectionBoundsHorizontalMargin(mItemPaddingLeft, mItemPaddingRight);
return itemView;
}
}
}
@Override
public boolean areAllItemsEnabled() {
// If "all" section is being loaded we'll show mLoadingView, which is not enabled.
// Otherwise check the all the other components in the ListView and return appropriate
// result.
return !mContactEntryListAdapter.isLoading()
&& (mContactTileAdapter.areAllItemsEnabled()
&& mAccountFilterHeaderContainer.isEnabled()
&& mContactEntryListAdapter.areAllItemsEnabled());
}
@Override
public boolean isEnabled(int position) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
if (position < contactTileAdapterCount) { // For "tile" and "frequent" sections
return mContactTileAdapter.isEnabled(position);
} else if (position == contactTileAdapterCount) { // For "all" section's account header
// This will be handled by View's onClick event instead of ListView's onItemClick event.
return false;
} else { // For "all" section
if (mContactEntryListAdapter.isLoading()) { // "All" section is being loaded.
return false;
} else {
// "-1" for mAccountFilterHeaderContainer
final int localPosition = position - contactTileAdapterCount - 1;
return mContactEntryListAdapter.isEnabled(localPosition);
}
}
}
@Override
public int getPositionForSection(int sectionIndex) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
final int localPosition = mContactEntryListAdapter.getPositionForSection(sectionIndex);
return contactTileAdapterCount + 1 + localPosition;
}
@Override
public int getSectionForPosition(int position) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
if (position <= contactTileAdapterCount) {
return 0;
} else {
// "-1" for mAccountFilterHeaderContainer
final int localPosition = position - contactTileAdapterCount - 1;
return mContactEntryListAdapter.getSectionForPosition(localPosition);
}
}
@Override
public Object[] getSections() {
return mContactEntryListAdapter.getSections();
}
public boolean shouldShowFirstScroller(int firstVisibleItem) {
final int contactTileAdapterCount = mContactTileAdapter.getCount();
return firstVisibleItem > contactTileAdapterCount;
}
}