Home | History | Annotate | Download | only in list
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.contacts.common.list;
     17 
     18 import android.content.Context;
     19 import android.view.View;
     20 import android.view.ViewGroup;
     21 
     22 import com.android.common.widget.CompositeCursorAdapter;
     23 
     24 /**
     25  * A subclass of {@link CompositeCursorAdapter} that manages pinned partition headers.
     26  */
     27 public abstract class PinnedHeaderListAdapter extends CompositeCursorAdapter
     28         implements PinnedHeaderListView.PinnedHeaderAdapter {
     29 
     30     public static final int PARTITION_HEADER_TYPE = 0;
     31 
     32     private boolean mPinnedPartitionHeadersEnabled;
     33     private boolean mHeaderVisibility[];
     34 
     35     public PinnedHeaderListAdapter(Context context) {
     36         super(context);
     37     }
     38 
     39     public PinnedHeaderListAdapter(Context context, int initialCapacity) {
     40         super(context, initialCapacity);
     41     }
     42 
     43     public boolean getPinnedPartitionHeadersEnabled() {
     44         return mPinnedPartitionHeadersEnabled;
     45     }
     46 
     47     public void setPinnedPartitionHeadersEnabled(boolean flag) {
     48         this.mPinnedPartitionHeadersEnabled = flag;
     49     }
     50 
     51     @Override
     52     public int getPinnedHeaderCount() {
     53         if (mPinnedPartitionHeadersEnabled) {
     54             return getPartitionCount();
     55         } else {
     56             return 0;
     57         }
     58     }
     59 
     60     protected boolean isPinnedPartitionHeaderVisible(int partition) {
     61         return mPinnedPartitionHeadersEnabled && hasHeader(partition)
     62                 && !isPartitionEmpty(partition);
     63     }
     64 
     65     /**
     66      * The default implementation creates the same type of view as a normal
     67      * partition header.
     68      */
     69     @Override
     70     public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) {
     71         if (hasHeader(partition)) {
     72             View view = null;
     73             if (convertView != null) {
     74                 Integer headerType = (Integer)convertView.getTag();
     75                 if (headerType != null && headerType == PARTITION_HEADER_TYPE) {
     76                     view = convertView;
     77                 }
     78             }
     79             if (view == null) {
     80                 view = newHeaderView(getContext(), partition, null, parent);
     81                 view.setTag(PARTITION_HEADER_TYPE);
     82                 view.setFocusable(false);
     83                 view.setEnabled(false);
     84             }
     85             bindHeaderView(view, partition, getCursor(partition));
     86             view.setLayoutDirection(parent.getLayoutDirection());
     87             return view;
     88         } else {
     89             return null;
     90         }
     91     }
     92 
     93     @Override
     94     public void configurePinnedHeaders(PinnedHeaderListView listView) {
     95         if (!mPinnedPartitionHeadersEnabled) {
     96             return;
     97         }
     98 
     99         int size = getPartitionCount();
    100 
    101         // Cache visibility bits, because we will need them several times later on
    102         if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
    103             mHeaderVisibility = new boolean[size];
    104         }
    105         for (int i = 0; i < size; i++) {
    106             boolean visible = isPinnedPartitionHeaderVisible(i);
    107             mHeaderVisibility[i] = visible;
    108             if (!visible) {
    109                 listView.setHeaderInvisible(i, true);
    110             }
    111         }
    112 
    113         int headerViewsCount = listView.getHeaderViewsCount();
    114 
    115         // Starting at the top, find and pin headers for partitions preceding the visible one(s)
    116         int maxTopHeader = -1;
    117         int topHeaderHeight = 0;
    118         for (int i = 0; i < size; i++) {
    119             if (mHeaderVisibility[i]) {
    120                 int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
    121                 int partition = getPartitionForPosition(position);
    122                 if (i > partition) {
    123                     break;
    124                 }
    125 
    126                 listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
    127                 topHeaderHeight += listView.getPinnedHeaderHeight(i);
    128                 maxTopHeader = i;
    129             }
    130         }
    131 
    132         // Starting at the bottom, find and pin headers for partitions following the visible one(s)
    133         int maxBottomHeader = size;
    134         int bottomHeaderHeight = 0;
    135         int listHeight = listView.getHeight();
    136         for (int i = size; --i > maxTopHeader;) {
    137             if (mHeaderVisibility[i]) {
    138                 int position = listView.getPositionAt(listHeight - bottomHeaderHeight)
    139                         - headerViewsCount;
    140                 if (position < 0) {
    141                     break;
    142                 }
    143 
    144                 int partition = getPartitionForPosition(position - 1);
    145                 if (partition == -1 || i <= partition) {
    146                     break;
    147                 }
    148 
    149                 int height = listView.getPinnedHeaderHeight(i);
    150                 bottomHeaderHeight += height;
    151                 // Animate the header only if the partition is completely invisible below
    152                 // the bottom of the view
    153                 int firstPositionForPartition = getPositionForPartition(i);
    154                 boolean animate = position < firstPositionForPartition;
    155                 listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, animate);
    156                 maxBottomHeader = i;
    157             }
    158         }
    159 
    160         // Headers in between the top-pinned and bottom-pinned should be hidden
    161         for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) {
    162             if (mHeaderVisibility[i]) {
    163                 listView.setHeaderInvisible(i, isPartitionEmpty(i));
    164             }
    165         }
    166     }
    167 
    168     @Override
    169     public int getScrollPositionForHeader(int viewIndex) {
    170         return getPositionForPartition(viewIndex);
    171     }
    172 }
    173