Home | History | Annotate | Download | only in widget
      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.widget;
     17 
     18 import com.android.common.widget.CompositeCursorAdapter;
     19 
     20 import android.content.Context;
     21 import android.view.View;
     22 import android.view.ViewGroup;
     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     public int getPinnedHeaderCount() {
     52         if (mPinnedPartitionHeadersEnabled) {
     53             return getPartitionCount();
     54         } else {
     55             return 0;
     56         }
     57     }
     58 
     59     protected boolean isPinnedPartitionHeaderVisible(int partition) {
     60         return mPinnedPartitionHeadersEnabled && hasHeader(partition)
     61                 && !isPartitionEmpty(partition);
     62     }
     63 
     64     /**
     65      * The default implementation creates the same type of view as a normal
     66      * partition header.
     67      */
     68     public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) {
     69         if (hasHeader(partition)) {
     70             View view = null;
     71             if (convertView != null) {
     72                 Integer headerType = (Integer)convertView.getTag();
     73                 if (headerType != null && headerType == PARTITION_HEADER_TYPE) {
     74                     view = convertView;
     75                 }
     76             }
     77             if (view == null) {
     78                 view = newHeaderView(getContext(), partition, null, parent);
     79                 view.setTag(PARTITION_HEADER_TYPE);
     80                 view.setFocusable(false);
     81                 view.setEnabled(false);
     82             }
     83             bindHeaderView(view, partition, getCursor(partition));
     84             return view;
     85         } else {
     86             return null;
     87         }
     88     }
     89 
     90     public void configurePinnedHeaders(PinnedHeaderListView listView) {
     91         if (!mPinnedPartitionHeadersEnabled) {
     92             return;
     93         }
     94 
     95         int size = getPartitionCount();
     96 
     97         // Cache visibility bits, because we will need them several times later on
     98         if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
     99             mHeaderVisibility = new boolean[size];
    100         }
    101         for (int i = 0; i < size; i++) {
    102             boolean visible = isPinnedPartitionHeaderVisible(i);
    103             mHeaderVisibility[i] = visible;
    104             if (!visible) {
    105                 listView.setHeaderInvisible(i, true);
    106             }
    107         }
    108 
    109         int headerViewsCount = listView.getHeaderViewsCount();
    110 
    111         // Starting at the top, find and pin headers for partitions preceding the visible one(s)
    112         int maxTopHeader = -1;
    113         int topHeaderHeight = 0;
    114         for (int i = 0; i < size; i++) {
    115             if (mHeaderVisibility[i]) {
    116                 int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
    117                 int partition = getPartitionForPosition(position);
    118                 if (i > partition) {
    119                     break;
    120                 }
    121 
    122                 listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
    123                 topHeaderHeight += listView.getPinnedHeaderHeight(i);
    124                 maxTopHeader = i;
    125             }
    126         }
    127 
    128         // Starting at the bottom, find and pin headers for partitions following the visible one(s)
    129         int maxBottomHeader = size;
    130         int bottomHeaderHeight = 0;
    131         int listHeight = listView.getHeight();
    132         for (int i = size; --i > maxTopHeader;) {
    133             if (mHeaderVisibility[i]) {
    134                 int position = listView.getPositionAt(listHeight - bottomHeaderHeight)
    135                         - headerViewsCount;
    136                 if (position < 0) {
    137                     break;
    138                 }
    139 
    140                 int partition = getPartitionForPosition(position - 1);
    141                 if (partition == -1 || i <= partition) {
    142                     break;
    143                 }
    144 
    145                 int height = listView.getPinnedHeaderHeight(i);
    146                 bottomHeaderHeight += height;
    147                 // Animate the header only if the partition is completely invisible below
    148                 // the bottom of the view
    149                 int firstPositionForPartition = getPositionForPartition(i);
    150                 boolean animate = position < firstPositionForPartition;
    151                 listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, animate);
    152                 maxBottomHeader = i;
    153             }
    154         }
    155 
    156         // Headers in between the top-pinned and bottom-pinned should be hidden
    157         for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) {
    158             if (mHeaderVisibility[i]) {
    159                 listView.setHeaderInvisible(i, isPartitionEmpty(i));
    160             }
    161         }
    162     }
    163 
    164     public int getScrollPositionForHeader(int viewIndex) {
    165         return getPositionForPartition(viewIndex);
    166     }
    167 }
    168