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