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 }