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 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             return view;
     87         } else {
     88             return null;
     89         }
     90     }
     91 
     92     @Override
     93     public void configurePinnedHeaders(PinnedHeaderListView listView) {
     94         if (!mPinnedPartitionHeadersEnabled) {
     95             return;
     96         }
     97 
     98         int size = getPartitionCount();
     99 
    100         // Cache visibility bits, because we will need them several times later on
    101         if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
    102             mHeaderVisibility = new boolean[size];
    103         }
    104         for (int i = 0; i < size; i++) {
    105             boolean visible = isPinnedPartitionHeaderVisible(i);
    106             mHeaderVisibility[i] = visible;
    107             if (!visible) {
    108                 listView.setHeaderInvisible(i, true);
    109             }
    110         }
    111 
    112         int headerViewsCount = listView.getHeaderViewsCount();
    113 
    114         // Starting at the top, find and pin headers for partitions preceding the visible one(s)
    115         int maxTopHeader = -1;
    116         int topHeaderHeight = 0;
    117         for (int i = 0; i < size; i++) {
    118             if (mHeaderVisibility[i]) {
    119                 int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
    120                 int partition = getPartitionForPosition(position);
    121                 if (i > partition) {
    122                     break;
    123                 }
    124 
    125                 listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
    126                 topHeaderHeight += listView.getPinnedHeaderHeight(i);
    127                 maxTopHeader = i;
    128             }
    129         }
    130 
    131         // Starting at the bottom, find and pin headers for partitions following the visible one(s)
    132         int maxBottomHeader = size;
    133         int bottomHeaderHeight = 0;
    134         int listHeight = listView.getHeight();
    135         for (int i = size; --i > maxTopHeader;) {
    136             if (mHeaderVisibility[i]) {
    137                 int position = listView.getPositionAt(listHeight - bottomHeaderHeight)
    138                         - headerViewsCount;
    139                 if (position < 0) {
    140                     break;
    141                 }
    142 
    143                 int partition = getPartitionForPosition(position - 1);
    144                 if (partition == -1 || i <= partition) {
    145                     break;
    146                 }
    147 
    148                 int height = listView.getPinnedHeaderHeight(i);
    149                 bottomHeaderHeight += height;
    150                 // Animate the header only if the partition is completely invisible below
    151                 // the bottom of the view
    152                 int firstPositionForPartition = getPositionForPartition(i);
    153                 boolean animate = position < firstPositionForPartition;
    154                 listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, animate);
    155                 maxBottomHeader = i;
    156             }
    157         }
    158 
    159         // Headers in between the top-pinned and bottom-pinned should be hidden
    160         for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) {
    161             if (mHeaderVisibility[i]) {
    162                 listView.setHeaderInvisible(i, isPartitionEmpty(i));
    163             }
    164         }
    165     }
    166 
    167     @Override
    168     public int getScrollPositionForHeader(int viewIndex) {
    169         return getPositionForPartition(viewIndex);
    170     }
    171 }
    172