Home | History | Annotate | Download | only in list
      1 /*
      2  * Copyright (C) 2011 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 package com.android.dialer.list;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.database.DataSetObserver;
     22 import android.view.View;
     23 import android.view.ViewGroup;
     24 import android.widget.BaseAdapter;
     25 import android.widget.FrameLayout;
     26 import android.widget.SectionIndexer;
     27 
     28 import com.android.contacts.common.list.ContactEntryListAdapter;
     29 import com.android.contacts.common.list.ContactListItemView;
     30 import com.android.contacts.common.list.ContactTileAdapter;
     31 import com.android.dialer.R;
     32 
     33 /**
     34  * An adapter that combines items from {@link com.android.contacts.common.list.ContactTileAdapter} and
     35  * {@link com.android.contacts.common.list.ContactEntryListAdapter} into a single list. In between those two results,
     36  * an account filter header will be inserted.
     37  */
     38 public class PhoneFavoriteMergedAdapter extends BaseAdapter implements SectionIndexer {
     39 
     40     private class CustomDataSetObserver extends DataSetObserver {
     41         @Override
     42         public void onChanged() {
     43             notifyDataSetChanged();
     44         }
     45     }
     46 
     47     private final ContactTileAdapter mContactTileAdapter;
     48     private final ContactEntryListAdapter mContactEntryListAdapter;
     49     private final View mAccountFilterHeaderContainer;
     50     private final View mLoadingView;
     51 
     52     private final int mItemPaddingLeft;
     53     private final int mItemPaddingRight;
     54 
     55     // Make frequent header consistent with account filter header.
     56     private final int mFrequentHeaderPaddingTop;
     57 
     58     private final DataSetObserver mObserver;
     59 
     60     public PhoneFavoriteMergedAdapter(Context context,
     61             ContactTileAdapter contactTileAdapter,
     62             View accountFilterHeaderContainer,
     63             ContactEntryListAdapter contactEntryListAdapter,
     64             View loadingView) {
     65         Resources resources = context.getResources();
     66         mItemPaddingLeft = resources.getDimensionPixelSize(R.dimen.detail_item_side_margin);
     67         mItemPaddingRight = resources.getDimensionPixelSize(R.dimen.list_visible_scrollbar_padding);
     68         mFrequentHeaderPaddingTop = resources.getDimensionPixelSize(
     69                 R.dimen.contact_browser_list_top_margin);
     70         mContactTileAdapter = contactTileAdapter;
     71         mContactEntryListAdapter = contactEntryListAdapter;
     72 
     73         mAccountFilterHeaderContainer = accountFilterHeaderContainer;
     74 
     75         mObserver = new CustomDataSetObserver();
     76         mContactTileAdapter.registerDataSetObserver(mObserver);
     77         mContactEntryListAdapter.registerDataSetObserver(mObserver);
     78 
     79         mLoadingView = loadingView;
     80     }
     81 
     82     @Override
     83     public boolean isEmpty() {
     84         // Cannot use the super's method here because we add extra rows in getCount() to account
     85         // for headers
     86         return mContactTileAdapter.getCount() + mContactEntryListAdapter.getCount() == 0;
     87     }
     88 
     89     @Override
     90     public int getCount() {
     91         final int contactTileAdapterCount = mContactTileAdapter.getCount();
     92         final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
     93         if (mContactEntryListAdapter.isLoading()) {
     94             // Hide "all" contacts during its being loaded. Instead show "loading" view.
     95             //
     96             // "+2" for mAccountFilterHeaderContainer and mLoadingView
     97             return contactTileAdapterCount + 2;
     98         } else {
     99             // "+1" for mAccountFilterHeaderContainer
    100             return contactTileAdapterCount + contactEntryListAdapterCount + 1;
    101         }
    102     }
    103 
    104     @Override
    105     public Object getItem(int position) {
    106         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    107         final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
    108         if (position < contactTileAdapterCount) {  // For "tile" and "frequent" sections
    109             return mContactTileAdapter.getItem(position);
    110         } else if (position == contactTileAdapterCount) {  // For "all" section's account header
    111             return mAccountFilterHeaderContainer;
    112         } else {  // For "all" section
    113             if (mContactEntryListAdapter.isLoading()) {  // "All" section is being loaded.
    114                 return mLoadingView;
    115             } else {
    116                 // "-1" for mAccountFilterHeaderContainer
    117                 final int localPosition = position - contactTileAdapterCount - 1;
    118                 return mContactTileAdapter.getItem(localPosition);
    119             }
    120         }
    121     }
    122 
    123     @Override
    124     public long getItemId(int position) {
    125         return position;
    126     }
    127 
    128     @Override
    129     public int getViewTypeCount() {
    130         // "+2" for mAccountFilterHeaderContainer and mLoadingView
    131         return (mContactTileAdapter.getViewTypeCount()
    132                 + mContactEntryListAdapter.getViewTypeCount()
    133                 + 2);
    134     }
    135 
    136     @Override
    137     public int getItemViewType(int position) {
    138         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    139         final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
    140         // There should be four kinds of types that are usually used, and one more exceptional
    141         // type (IGNORE_ITEM_VIEW_TYPE), which sometimes comes from mContactTileAdapter.
    142         //
    143         // The four ordinary view types have the index equal to or more than 0, and less than
    144         // mContactTileAdapter.getViewTypeCount()+ mContactEntryListAdapter.getViewTypeCount() + 2.
    145         // (See also this class's getViewTypeCount())
    146         //
    147         // We have those values for:
    148         // - The view types mContactTileAdapter originally has
    149         // - The view types mContactEntryListAdapter originally has
    150         // - mAccountFilterHeaderContainer ("all" section's account header), and
    151         // - mLoadingView
    152         //
    153         // Those types should not be mixed, so we have a different range for each kinds of types:
    154         // - Types for mContactTileAdapter ("tile" and "frequent" sections)
    155         //   They should have the index, >=0 and <mContactTileAdapter.getViewTypeCount()
    156         //
    157         // - Types for mContactEntryListAdapter ("all" sections)
    158         //   They should have the index, >=mContactTileAdapter.getViewTypeCount() and
    159         //   <(mContactTileAdapter.getViewTypeCount() + mContactEntryListAdapter.getViewTypeCount())
    160         //
    161         // - Type for "all" section's account header
    162         //   It should have the exact index
    163         //   mContactTileAdapter.getViewTypeCount()+ mContactEntryListAdapter.getViewTypeCount()
    164         //
    165         // - Type for "loading" view used during "all" section is being loaded.
    166         //   It should have the exact index
    167         //   mContactTileAdapter.getViewTypeCount()+ mContactEntryListAdapter.getViewTypeCount() + 1
    168         //
    169         // As an exception, IGNORE_ITEM_VIEW_TYPE (-1) will be remained as is, which will be used
    170         // by framework's Adapter implementation and thus should be left as is.
    171         if (position < contactTileAdapterCount) {  // For "tile" and "frequent" sections
    172             return mContactTileAdapter.getItemViewType(position);
    173         } else if (position == contactTileAdapterCount) {  // For "all" section's account header
    174             return mContactTileAdapter.getViewTypeCount()
    175                     + mContactEntryListAdapter.getViewTypeCount();
    176         } else {  // For "all" section
    177             if (mContactEntryListAdapter.isLoading()) {  // "All" section is being loaded.
    178                 return mContactTileAdapter.getViewTypeCount()
    179                         + mContactEntryListAdapter.getViewTypeCount() + 1;
    180             } else {
    181                 // "-1" for mAccountFilterHeaderContainer
    182                 final int localPosition = position - contactTileAdapterCount - 1;
    183                 final int type = mContactEntryListAdapter.getItemViewType(localPosition);
    184                 // IGNORE_ITEM_VIEW_TYPE must be handled differently.
    185                 return (type < 0) ? type : type + mContactTileAdapter.getViewTypeCount();
    186             }
    187         }
    188     }
    189 
    190     @Override
    191     public View getView(int position, View convertView, ViewGroup parent) {
    192         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    193         final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
    194 
    195         // Obtain a View relevant for that position, and adjust its horizontal padding. Each
    196         // View has different implementation, so we use different way to control those padding.
    197         if (position < contactTileAdapterCount) {  // For "tile" and "frequent" sections
    198             final View view = mContactTileAdapter.getView(position, convertView, parent);
    199             final int frequentHeaderPosition = mContactTileAdapter.getFrequentHeaderPosition();
    200             if (position < frequentHeaderPosition) {  // "starred" contacts
    201                 // No padding adjustment.
    202             } else if (position == frequentHeaderPosition) {
    203                 view.setPadding(mItemPaddingLeft, mFrequentHeaderPaddingTop,
    204                         mItemPaddingRight, view.getPaddingBottom());
    205             } else {
    206                 // Views for "frequent" contacts use FrameLayout's margins instead of padding.
    207                 final FrameLayout frameLayout = (FrameLayout) view;
    208                 final View child = frameLayout.getChildAt(0);
    209                 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
    210                         FrameLayout.LayoutParams.WRAP_CONTENT,
    211                         FrameLayout.LayoutParams.WRAP_CONTENT);
    212                 params.setMargins(mItemPaddingLeft, 0, mItemPaddingRight, 0);
    213                 child.setLayoutParams(params);
    214             }
    215             return view;
    216         } else if (position == contactTileAdapterCount) {  // For "all" section's account header
    217             mAccountFilterHeaderContainer.setPadding(mItemPaddingLeft,
    218                     mAccountFilterHeaderContainer.getPaddingTop(),
    219                     mItemPaddingRight,
    220                     mAccountFilterHeaderContainer.getPaddingBottom());
    221 
    222             // Show a single "No Contacts" label under the "all" section account header
    223             // if no contacts are displayed.
    224             mAccountFilterHeaderContainer.findViewById(
    225                     R.id.contact_list_all_empty).setVisibility(
    226                     contactEntryListAdapterCount == 0 ? View.VISIBLE : View.GONE);
    227             return mAccountFilterHeaderContainer;
    228         } else {  // For "all" section
    229             if (mContactEntryListAdapter.isLoading()) {  // "All" section is being loaded.
    230                 mLoadingView.setPadding(mItemPaddingLeft,
    231                         mLoadingView.getPaddingTop(),
    232                         mItemPaddingRight,
    233                         mLoadingView.getPaddingBottom());
    234                 return mLoadingView;
    235             } else {
    236                 // "-1" for mAccountFilterHeaderContainer
    237                 final int localPosition = position - contactTileAdapterCount - 1;
    238                 final ContactListItemView itemView = (ContactListItemView)
    239                         mContactEntryListAdapter.getView(localPosition, convertView, null);
    240                 itemView.setPadding(mItemPaddingLeft, itemView.getPaddingTop(),
    241                         mItemPaddingRight, itemView.getPaddingBottom());
    242                 itemView.setSelectionBoundsHorizontalMargin(mItemPaddingLeft, mItemPaddingRight);
    243                 return itemView;
    244             }
    245         }
    246     }
    247 
    248     @Override
    249     public boolean areAllItemsEnabled() {
    250         // If "all" section is being loaded we'll show mLoadingView, which is not enabled.
    251         // Otherwise check the all the other components in the ListView and return appropriate
    252         // result.
    253         return !mContactEntryListAdapter.isLoading()
    254                 && (mContactTileAdapter.areAllItemsEnabled()
    255                 && mAccountFilterHeaderContainer.isEnabled()
    256                 && mContactEntryListAdapter.areAllItemsEnabled());
    257     }
    258 
    259     @Override
    260     public boolean isEnabled(int position) {
    261         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    262         final int contactEntryListAdapterCount = mContactEntryListAdapter.getCount();
    263         if (position < contactTileAdapterCount) {  // For "tile" and "frequent" sections
    264             return mContactTileAdapter.isEnabled(position);
    265         } else if (position == contactTileAdapterCount) {  // For "all" section's account header
    266             // This will be handled by View's onClick event instead of ListView's onItemClick event.
    267             return false;
    268         } else {  // For "all" section
    269             if (mContactEntryListAdapter.isLoading()) {  // "All" section is being loaded.
    270                 return false;
    271             } else {
    272                 // "-1" for mAccountFilterHeaderContainer
    273                 final int localPosition = position - contactTileAdapterCount - 1;
    274                 return mContactEntryListAdapter.isEnabled(localPosition);
    275             }
    276         }
    277     }
    278 
    279     @Override
    280     public int getPositionForSection(int sectionIndex) {
    281         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    282         final int localPosition = mContactEntryListAdapter.getPositionForSection(sectionIndex);
    283         return contactTileAdapterCount + 1 + localPosition;
    284     }
    285 
    286     @Override
    287     public int getSectionForPosition(int position) {
    288         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    289         if (position <= contactTileAdapterCount) {
    290             return 0;
    291         } else {
    292             // "-1" for mAccountFilterHeaderContainer
    293             final int localPosition = position - contactTileAdapterCount - 1;
    294             return mContactEntryListAdapter.getSectionForPosition(localPosition);
    295         }
    296     }
    297 
    298     @Override
    299     public Object[] getSections() {
    300         return mContactEntryListAdapter.getSections();
    301     }
    302 
    303     public boolean shouldShowFirstScroller(int firstVisibleItem) {
    304         final int contactTileAdapterCount = mContactTileAdapter.getCount();
    305         return firstVisibleItem > contactTileAdapterCount;
    306     }
    307 }
    308