blob: 620e511bc27b776e0e38101ed3f64bbdf39f822c [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.messaging.ui;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
/**
* A general purpose adapter that composes one or more other adapters. It
* appends them in the order they are added.
*/
public class CompositeAdapter extends BaseAdapter {
private static final int INITIAL_CAPACITY = 2;
public static class Partition {
boolean mShowIfEmpty;
boolean mHasHeader;
BaseAdapter mAdapter;
public Partition(final boolean showIfEmpty, final boolean hasHeader,
final BaseAdapter adapter) {
this.mShowIfEmpty = showIfEmpty;
this.mHasHeader = hasHeader;
this.mAdapter = adapter;
}
/**
* True if the directory should be shown even if no contacts are found.
*/
public boolean showIfEmpty() {
return mShowIfEmpty;
}
public boolean hasHeader() {
return mHasHeader;
}
public int getCount() {
int count = mAdapter.getCount();
if (mHasHeader && (count != 0 || mShowIfEmpty)) {
count++;
}
return count;
}
public BaseAdapter getAdapter() {
return mAdapter;
}
public View getHeaderView(final View convertView, final ViewGroup parentView) {
return null;
}
public void close() {
// do nothing in base class.
}
}
private class Observer extends DataSetObserver {
@Override
public void onChanged() {
CompositeAdapter.this.notifyDataSetChanged();
}
@Override
public void onInvalidated() {
CompositeAdapter.this.notifyDataSetInvalidated();
}
};
protected final Context mContext;
private Partition[] mPartitions;
private int mSize = 0;
private int mCount = 0;
private boolean mCacheValid = true;
private final Observer mObserver;
public CompositeAdapter(final Context context) {
mContext = context;
mObserver = new Observer();
mPartitions = new Partition[INITIAL_CAPACITY];
}
public Context getContext() {
return mContext;
}
public void addPartition(final Partition partition) {
if (mSize >= mPartitions.length) {
final int newCapacity = mSize + 2;
final Partition[] newAdapters = new Partition[newCapacity];
System.arraycopy(mPartitions, 0, newAdapters, 0, mSize);
mPartitions = newAdapters;
}
mPartitions[mSize++] = partition;
partition.getAdapter().registerDataSetObserver(mObserver);
invalidate();
notifyDataSetChanged();
}
public void removePartition(final int index) {
final Partition partition = mPartitions[index];
partition.close();
System.arraycopy(mPartitions, index + 1, mPartitions, index,
mSize - index - 1);
mSize--;
partition.getAdapter().unregisterDataSetObserver(mObserver);
invalidate();
notifyDataSetChanged();
}
public void clearPartitions() {
for (int i = 0; i < mSize; i++) {
final Partition partition = mPartitions[i];
partition.close();
partition.getAdapter().unregisterDataSetObserver(mObserver);
}
invalidate();
notifyDataSetChanged();
}
public Partition getPartition(final int index) {
return mPartitions[index];
}
public int getPartitionAtPosition(final int position) {
ensureCacheValid();
int start = 0;
for (int i = 0; i < mSize; i++) {
final int end = start + mPartitions[i].getCount();
if (position >= start && position < end) {
int offset = position - start;
if (mPartitions[i].hasHeader() &&
(mPartitions[i].getCount() > 0 || mPartitions[i].showIfEmpty())) {
offset--;
}
if (offset == -1) {
return -1;
}
return i;
}
start = end;
}
return mSize - 1;
}
public int getPartitionCount() {
return mSize;
}
public void invalidate() {
mCacheValid = false;
}
private void ensureCacheValid() {
if (mCacheValid) {
return;
}
mCount = 0;
for (int i = 0; i < mSize; i++) {
mCount += mPartitions[i].getCount();
}
}
@Override
public int getCount() {
ensureCacheValid();
return mCount;
}
public int getCount(final int index) {
ensureCacheValid();
return mPartitions[index].getCount();
}
@Override
public Object getItem(final int position) {
ensureCacheValid();
int start = 0;
for (int i = 0; i < mSize; i++) {
final int end = start + mPartitions[i].getCount();
if (position >= start && position < end) {
final int offset = position - start;
final Partition partition = mPartitions[i];
if (partition.hasHeader() && offset == 0 &&
(partition.getCount() > 0 || partition.showIfEmpty())) {
// This is the header
return null;
}
return mPartitions[i].getAdapter().getItem(offset);
}
start = end;
}
return null;
}
@Override
public long getItemId(final int position) {
ensureCacheValid();
int start = 0;
for (int i = 0; i < mSize; i++) {
final int end = start + mPartitions[i].getCount();
if (position >= start && position < end) {
final int offset = position - start;
final Partition partition = mPartitions[i];
if (partition.hasHeader() && offset == 0 &&
(partition.getCount() > 0 || partition.showIfEmpty())) {
// Header
return 0;
}
return mPartitions[i].getAdapter().getItemId(offset);
}
start = end;
}
return 0;
}
@Override
public boolean isEnabled(int position) {
ensureCacheValid();
int start = 0;
for (int i = 0; i < mSize; i++) {
final int end = start + mPartitions[i].getCount();
if (position >= start && position < end) {
final int offset = position - start;
final Partition partition = mPartitions[i];
if (partition.hasHeader() && offset == 0 &&
(partition.getCount() > 0 || partition.showIfEmpty())) {
// This is the header
return false;
}
return true;
}
start = end;
}
return true;
}
@Override
public View getView(final int position, final View convertView, final ViewGroup parentView) {
ensureCacheValid();
int start = 0;
for (int i = 0; i < mSize; i++) {
final Partition partition = mPartitions[i];
final int end = start + partition.getCount();
if (position >= start && position < end) {
int offset = position - start;
View view;
if (partition.hasHeader() &&
(partition.getCount() > 0 || partition.showIfEmpty())) {
offset = offset - 1;
}
if (offset == -1) {
view = partition.getHeaderView(convertView, parentView);
} else {
view = partition.getAdapter().getView(offset, convertView, parentView);
}
if (view == null) {
throw new NullPointerException("View should not be null, partition: " + i
+ " position: " + offset);
}
return view;
}
start = end;
}
throw new ArrayIndexOutOfBoundsException(position);
}
}