• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ThreadedCursorAdapter(Context context, Cursor c)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
getCount()129     public int getCount() {
130         return mSize;
131     }
132 
133     @Override
getItem(int position)134     public Cursor getItem(int position) {
135         return (Cursor) mCursorAdapter.getItem(position);
136     }
137 
138     @Override
getItemId(int position)139     public long getItemId(int position) {
140         synchronized (mCursorLock) {
141             return getItemId(getItem(position));
142         }
143     }
144 
loadRowObject(int position, LoadContainer container)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
getView(int position, View convertView, ViewGroup parent)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 
cachedLoadObject()192     private T cachedLoadObject() {
193         if (mLoadingObject == null) {
194             mLoadingObject = getLoadingObject();
195         }
196         return mLoadingObject;
197     }
198 
changeCursor(Cursor cursor)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 
newView(Context context, ViewGroup parent)208     public abstract View newView(Context context, ViewGroup parent);
bindView(View view, T object)209     public abstract void bindView(View view, T object);
getRowObject(Cursor c, T recycleObject)210     public abstract T getRowObject(Cursor c, T recycleObject);
getLoadingObject()211     public abstract T getLoadingObject();
getItemId(Cursor c)212     protected abstract long getItemId(Cursor c);
213 }