/* * 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); } }