Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2011 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.browser.util;
     17 
     18 import android.content.Context;
     19 import android.database.Cursor;
     20 import android.os.Handler;
     21 import android.os.HandlerThread;
     22 import android.os.Message;
     23 import android.os.Process;
     24 import android.os.SystemProperties;
     25 import android.util.Log;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.widget.Adapter;
     29 import android.widget.BaseAdapter;
     30 import android.widget.CursorAdapter;
     31 
     32 import com.android.browser.R;
     33 
     34 import java.lang.ref.WeakReference;
     35 
     36 public abstract class ThreadedCursorAdapter<T> extends BaseAdapter {
     37 
     38     private static final String LOGTAG = "BookmarksThreadedAdapter";
     39     private static final boolean DEBUG = false;
     40 
     41     private Context mContext;
     42     private Object mCursorLock = new Object();
     43     private CursorAdapter mCursorAdapter;
     44     private T mLoadingObject;
     45     private Handler mLoadHandler;
     46     private Handler mHandler;
     47     private int mSize;
     48     private boolean mHasCursor;
     49     private long mGeneration;
     50 
     51     private class LoadContainer {
     52         WeakReference<View> view;
     53         int position;
     54         T bind_object;
     55         Adapter owner;
     56         boolean loaded;
     57         long generation;
     58     }
     59 
     60     public ThreadedCursorAdapter(Context context, Cursor c) {
     61         mContext = context;
     62         mHasCursor = (c != null);
     63         mCursorAdapter = new CursorAdapter(context, c, 0) {
     64 
     65             @Override
     66             public View newView(Context context, Cursor cursor, ViewGroup parent) {
     67                 throw new IllegalStateException("not supported");
     68             }
     69 
     70             @Override
     71             public void bindView(View view, Context context, Cursor cursor) {
     72                 throw new IllegalStateException("not supported");
     73             }
     74 
     75             @Override
     76             public void notifyDataSetChanged() {
     77                 super.notifyDataSetChanged();
     78                 mSize = getCount();
     79                 mGeneration++;
     80                 ThreadedCursorAdapter.this.notifyDataSetChanged();
     81             }
     82 
     83             @Override
     84             public void notifyDataSetInvalidated() {
     85                 super.notifyDataSetInvalidated();
     86                 mSize = getCount();
     87                 mGeneration++;
     88                 ThreadedCursorAdapter.this.notifyDataSetInvalidated();
     89             }
     90 
     91         };
     92         mSize = mCursorAdapter.getCount();
     93         HandlerThread thread = new HandlerThread("threaded_adapter_" + this,
     94                 Process.THREAD_PRIORITY_BACKGROUND);
     95         thread.start();
     96         mLoadHandler = new Handler(thread.getLooper()) {
     97             @SuppressWarnings("unchecked")
     98             @Override
     99             public void handleMessage(Message msg) {
    100                 if (DEBUG) {
    101                     Log.d(LOGTAG, "loading: " + msg.what);
    102                 }
    103                 loadRowObject(msg.what, (LoadContainer) msg.obj);
    104             }
    105         };
    106         mHandler = new Handler() {
    107             @Override
    108             public void handleMessage(Message msg) {
    109                 @SuppressWarnings("unchecked")
    110                 LoadContainer container = (LoadContainer) msg.obj;
    111                 if (container == null) {
    112                     return;
    113                 }
    114                 View view = container.view.get();
    115                 if (view == null
    116                         || container.owner != ThreadedCursorAdapter.this
    117                         || container.position != msg.what
    118                         || view.getWindowToken() == null
    119                         || container.generation != mGeneration) {
    120                     return;
    121                 }
    122                 container.loaded = true;
    123                 bindView(view, container.bind_object);
    124             }
    125         };
    126     }
    127 
    128     @Override
    129     public int getCount() {
    130         return mSize;
    131     }
    132 
    133     @Override
    134     public Cursor getItem(int position) {
    135         return (Cursor) mCursorAdapter.getItem(position);
    136     }
    137 
    138     @Override
    139     public long getItemId(int position) {
    140         synchronized (mCursorLock) {
    141             return getItemId(getItem(position));
    142         }
    143     }
    144 
    145     private void loadRowObject(int position, LoadContainer container) {
    146         if (container == null
    147                 || container.position != position
    148                 || container.owner != ThreadedCursorAdapter.this
    149                 || container.view.get() == null) {
    150             return;
    151         }
    152         synchronized (mCursorLock) {
    153             Cursor c = (Cursor) mCursorAdapter.getItem(position);
    154             if (c == null || c.isClosed()) {
    155                 return;
    156             }
    157             container.bind_object = getRowObject(c, container.bind_object);
    158         }
    159         mHandler.obtainMessage(position, container).sendToTarget();
    160     }
    161 
    162     @Override
    163     public View getView(int position, View convertView, ViewGroup parent) {
    164         if (convertView == null) {
    165             convertView = newView(mContext, parent);
    166         }
    167         @SuppressWarnings("unchecked")
    168         LoadContainer container = (LoadContainer) convertView.getTag(R.id.load_object);
    169         if (container == null) {
    170             container = new LoadContainer();
    171             container.view = new WeakReference<View>(convertView);
    172             convertView.setTag(R.id.load_object, container);
    173         }
    174         if (container.position == position
    175                 && container.owner == this
    176                 && container.loaded
    177                 && container.generation == mGeneration) {
    178             bindView(convertView, container.bind_object);
    179         } else {
    180             bindView(convertView, cachedLoadObject());
    181             if (mHasCursor) {
    182                 container.position = position;
    183                 container.loaded = false;
    184                 container.owner = this;
    185                 container.generation = mGeneration;
    186                 mLoadHandler.obtainMessage(position, container).sendToTarget();
    187             }
    188         }
    189         return convertView;
    190     }
    191 
    192     private T cachedLoadObject() {
    193         if (mLoadingObject == null) {
    194             mLoadingObject = getLoadingObject();
    195         }
    196         return mLoadingObject;
    197     }
    198 
    199     public void changeCursor(Cursor cursor) {
    200         mLoadHandler.removeCallbacksAndMessages(null);
    201         mHandler.removeCallbacksAndMessages(null);
    202         synchronized (mCursorLock) {
    203             mHasCursor = (cursor != null);
    204             mCursorAdapter.changeCursor(cursor);
    205         }
    206     }
    207 
    208     public abstract View newView(Context context, ViewGroup parent);
    209     public abstract void bindView(View view, T object);
    210     public abstract T getRowObject(Cursor c, T recycleObject);
    211     public abstract T getLoadingObject();
    212     protected abstract long getItemId(Cursor c);
    213 }