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