blob: 72a07eafd08fbc2819881d919b6dcb390acc34fb [file] [log] [blame]
/*
* Copyright (C) 2011 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.browser.util;
import android.content.Context;
import android.database.Cursor;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.CursorAdapter;
import com.android.browser.R;
import java.lang.ref.WeakReference;
public abstract class ThreadedCursorAdapter<T> extends BaseAdapter {
private static final String LOGTAG = "BookmarksThreadedAdapter";
private static final boolean DEBUG = false;
private Context mContext;
private Object mCursorLock = new Object();
private CursorAdapter mCursorAdapter;
private T mLoadingObject;
private Handler mLoadHandler;
private Handler mHandler;
private int mSize;
private boolean mHasCursor;
private long mGeneration;
private class LoadContainer {
WeakReference<View> view;
int position;
T bind_object;
Adapter owner;
boolean loaded;
long generation;
}
public ThreadedCursorAdapter(Context context, Cursor c) {
mContext = context;
mHasCursor = (c != null);
mCursorAdapter = new CursorAdapter(context, c, 0) {
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
throw new IllegalStateException("not supported");
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
throw new IllegalStateException("not supported");
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
mSize = getCount();
mGeneration++;
ThreadedCursorAdapter.this.notifyDataSetChanged();
}
@Override
public void notifyDataSetInvalidated() {
super.notifyDataSetInvalidated();
mSize = getCount();
mGeneration++;
ThreadedCursorAdapter.this.notifyDataSetInvalidated();
}
};
mSize = mCursorAdapter.getCount();
HandlerThread thread = new HandlerThread("threaded_adapter_" + this,
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mLoadHandler = new Handler(thread.getLooper()) {
@SuppressWarnings("unchecked")
@Override
public void handleMessage(Message msg) {
if (DEBUG) {
Log.d(LOGTAG, "loading: " + msg.what);
}
loadRowObject(msg.what, (LoadContainer) msg.obj);
}
};
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@SuppressWarnings("unchecked")
LoadContainer container = (LoadContainer) msg.obj;
if (container == null) {
return;
}
View view = container.view.get();
if (view == null
|| container.owner != ThreadedCursorAdapter.this
|| container.position != msg.what
|| view.getWindowToken() == null
|| container.generation != mGeneration) {
return;
}
container.loaded = true;
bindView(view, container.bind_object);
}
};
}
@Override
public int getCount() {
return mSize;
}
@Override
public Cursor getItem(int position) {
return (Cursor) mCursorAdapter.getItem(position);
}
@Override
public long getItemId(int position) {
synchronized (mCursorLock) {
return getItemId(getItem(position));
}
}
private void loadRowObject(int position, LoadContainer container) {
if (container == null
|| container.position != position
|| container.owner != ThreadedCursorAdapter.this
|| container.view.get() == null) {
return;
}
synchronized (mCursorLock) {
Cursor c = (Cursor) mCursorAdapter.getItem(position);
if (c == null || c.isClosed()) {
return;
}
container.bind_object = getRowObject(c, container.bind_object);
}
mHandler.obtainMessage(position, container).sendToTarget();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = newView(mContext, parent);
}
@SuppressWarnings("unchecked")
LoadContainer container = (LoadContainer) convertView.getTag(R.id.load_object);
if (container == null) {
container = new LoadContainer();
container.view = new WeakReference<View>(convertView);
convertView.setTag(R.id.load_object, container);
}
if (container.position == position
&& container.owner == this
&& container.loaded
&& container.generation == mGeneration) {
bindView(convertView, container.bind_object);
} else {
bindView(convertView, cachedLoadObject());
if (mHasCursor) {
container.position = position;
container.loaded = false;
container.owner = this;
container.generation = mGeneration;
mLoadHandler.obtainMessage(position, container).sendToTarget();
}
}
return convertView;
}
private T cachedLoadObject() {
if (mLoadingObject == null) {
mLoadingObject = getLoadingObject();
}
return mLoadingObject;
}
public void changeCursor(Cursor cursor) {
mLoadHandler.removeCallbacksAndMessages(null);
mHandler.removeCallbacksAndMessages(null);
synchronized (mCursorLock) {
mHasCursor = (cursor != null);
mCursorAdapter.changeCursor(cursor);
}
}
public abstract View newView(Context context, ViewGroup parent);
public abstract void bindView(View view, T object);
public abstract T getRowObject(Cursor c, T recycleObject);
public abstract T getLoadingObject();
protected abstract long getItemId(Cursor c);
}