Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2015 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.messaging.ui;
     17 
     18 import android.content.Context;
     19 import android.database.DataSetObserver;
     20 import android.view.View;
     21 import android.view.ViewGroup;
     22 import android.widget.BaseAdapter;
     23 
     24 /**
     25  * A general purpose adapter that composes one or more other adapters.  It
     26  * appends them in the order they are added.
     27  */
     28 public class CompositeAdapter extends BaseAdapter {
     29 
     30     private static final int INITIAL_CAPACITY = 2;
     31 
     32     public static class Partition {
     33         boolean mShowIfEmpty;
     34         boolean mHasHeader;
     35         BaseAdapter mAdapter;
     36 
     37         public Partition(final boolean showIfEmpty, final boolean hasHeader,
     38                 final BaseAdapter adapter) {
     39             this.mShowIfEmpty = showIfEmpty;
     40             this.mHasHeader = hasHeader;
     41             this.mAdapter = adapter;
     42         }
     43 
     44         /**
     45          * True if the directory should be shown even if no contacts are found.
     46          */
     47         public boolean showIfEmpty() {
     48             return mShowIfEmpty;
     49         }
     50 
     51         public boolean hasHeader() {
     52             return mHasHeader;
     53         }
     54 
     55         public int getCount() {
     56             int count = mAdapter.getCount();
     57             if (mHasHeader && (count != 0 || mShowIfEmpty)) {
     58                 count++;
     59             }
     60             return count;
     61         }
     62 
     63         public BaseAdapter getAdapter() {
     64             return mAdapter;
     65         }
     66 
     67         public View getHeaderView(final View convertView, final ViewGroup parentView) {
     68             return null;
     69         }
     70 
     71         public void close() {
     72             // do nothing in base class.
     73         }
     74     }
     75 
     76     private class Observer extends DataSetObserver {
     77         @Override
     78         public void onChanged() {
     79             CompositeAdapter.this.notifyDataSetChanged();
     80         }
     81 
     82         @Override
     83         public void onInvalidated() {
     84             CompositeAdapter.this.notifyDataSetInvalidated();
     85         }
     86     };
     87 
     88     protected final Context mContext;
     89     private Partition[] mPartitions;
     90     private int mSize = 0;
     91     private int mCount = 0;
     92     private boolean mCacheValid = true;
     93     private final Observer mObserver;
     94 
     95     public CompositeAdapter(final Context context) {
     96         mContext = context;
     97         mObserver = new Observer();
     98         mPartitions = new Partition[INITIAL_CAPACITY];
     99     }
    100 
    101     public Context getContext() {
    102         return mContext;
    103     }
    104 
    105     public void addPartition(final Partition partition) {
    106         if (mSize >= mPartitions.length) {
    107             final int newCapacity = mSize + 2;
    108             final Partition[] newAdapters = new Partition[newCapacity];
    109             System.arraycopy(mPartitions, 0, newAdapters, 0, mSize);
    110             mPartitions = newAdapters;
    111         }
    112         mPartitions[mSize++] = partition;
    113         partition.getAdapter().registerDataSetObserver(mObserver);
    114         invalidate();
    115         notifyDataSetChanged();
    116     }
    117 
    118     public void removePartition(final int index) {
    119         final Partition partition = mPartitions[index];
    120         partition.close();
    121         System.arraycopy(mPartitions, index + 1, mPartitions, index,
    122                 mSize - index - 1);
    123         mSize--;
    124         partition.getAdapter().unregisterDataSetObserver(mObserver);
    125         invalidate();
    126         notifyDataSetChanged();
    127     }
    128 
    129     public void clearPartitions() {
    130         for (int i = 0; i < mSize; i++) {
    131             final Partition partition = mPartitions[i];
    132             partition.close();
    133             partition.getAdapter().unregisterDataSetObserver(mObserver);
    134         }
    135         invalidate();
    136         notifyDataSetChanged();
    137     }
    138 
    139     public Partition getPartition(final int index) {
    140         return mPartitions[index];
    141     }
    142 
    143     public int getPartitionAtPosition(final int position) {
    144         ensureCacheValid();
    145         int start = 0;
    146         for (int i = 0; i < mSize; i++) {
    147             final int end = start + mPartitions[i].getCount();
    148             if (position >= start && position < end) {
    149                 int offset = position - start;
    150                 if (mPartitions[i].hasHeader() &&
    151                         (mPartitions[i].getCount() > 0 || mPartitions[i].showIfEmpty())) {
    152                     offset--;
    153                 }
    154                 if (offset == -1) {
    155                     return -1;
    156                 }
    157                 return i;
    158             }
    159             start = end;
    160         }
    161         return mSize - 1;
    162     }
    163 
    164     public int getPartitionCount() {
    165         return mSize;
    166     }
    167 
    168     public void invalidate() {
    169         mCacheValid = false;
    170     }
    171 
    172     private void ensureCacheValid() {
    173         if (mCacheValid) {
    174             return;
    175         }
    176         mCount = 0;
    177         for (int i = 0; i < mSize; i++) {
    178             mCount += mPartitions[i].getCount();
    179         }
    180     }
    181 
    182     @Override
    183     public int getCount() {
    184         ensureCacheValid();
    185         return mCount;
    186     }
    187 
    188     public int getCount(final int index) {
    189         ensureCacheValid();
    190         return mPartitions[index].getCount();
    191     }
    192 
    193     @Override
    194     public Object getItem(final int position) {
    195         ensureCacheValid();
    196         int start = 0;
    197         for (int i = 0; i < mSize; i++) {
    198             final int end = start + mPartitions[i].getCount();
    199             if (position >= start && position < end) {
    200                 final int offset = position - start;
    201                 final Partition partition = mPartitions[i];
    202                 if (partition.hasHeader() && offset == 0 &&
    203                         (partition.getCount() > 0 || partition.showIfEmpty())) {
    204                     // This is the header
    205                     return null;
    206                 }
    207                 return mPartitions[i].getAdapter().getItem(offset);
    208             }
    209             start = end;
    210         }
    211 
    212         return null;
    213     }
    214 
    215     @Override
    216     public long getItemId(final int position) {
    217         ensureCacheValid();
    218         int start = 0;
    219         for (int i = 0; i < mSize; i++) {
    220             final int end = start + mPartitions[i].getCount();
    221             if (position >= start && position < end) {
    222                 final int offset = position - start;
    223                 final Partition partition = mPartitions[i];
    224                 if (partition.hasHeader() && offset == 0 &&
    225                         (partition.getCount() > 0 || partition.showIfEmpty())) {
    226                     // Header
    227                     return 0;
    228                 }
    229                 return mPartitions[i].getAdapter().getItemId(offset);
    230             }
    231             start = end;
    232         }
    233 
    234         return 0;
    235     }
    236 
    237     @Override
    238     public boolean isEnabled(int position) {
    239         ensureCacheValid();
    240         int start = 0;
    241         for (int i = 0; i < mSize; i++) {
    242             final int end = start + mPartitions[i].getCount();
    243             if (position >= start && position < end) {
    244                 final int offset = position - start;
    245                 final Partition partition = mPartitions[i];
    246                 if (partition.hasHeader() && offset == 0 &&
    247                         (partition.getCount() > 0 || partition.showIfEmpty())) {
    248                     // This is the header
    249                     return false;
    250                 }
    251                 return true;
    252             }
    253             start = end;
    254         }
    255         return true;
    256     }
    257 
    258     @Override
    259     public View getView(final int position, final View convertView, final ViewGroup parentView) {
    260         ensureCacheValid();
    261         int start = 0;
    262         for (int i = 0; i < mSize; i++) {
    263             final Partition partition = mPartitions[i];
    264             final int end = start + partition.getCount();
    265             if (position >= start && position < end) {
    266                 int offset = position - start;
    267                 View view;
    268                 if (partition.hasHeader() &&
    269                         (partition.getCount() > 0 || partition.showIfEmpty())) {
    270                     offset = offset - 1;
    271                 }
    272                 if (offset == -1) {
    273                     view = partition.getHeaderView(convertView, parentView);
    274                 } else {
    275                     view = partition.getAdapter().getView(offset, convertView, parentView);
    276                 }
    277                 if (view == null) {
    278                     throw new NullPointerException("View should not be null, partition: " + i
    279                             + " position: " + offset);
    280                 }
    281                 return view;
    282             }
    283             start = end;
    284         }
    285 
    286         throw new ArrayIndexOutOfBoundsException(position);
    287     }
    288 }
    289