1f94abcf44fc1611f76e55461f48220e621fc31b7John Reck/*
2f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * Copyright (C) 2011 The Android Open Source Project
3f94abcf44fc1611f76e55461f48220e621fc31b7John Reck *
4f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * Licensed under the Apache License, Version 2.0 (the "License");
5f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * you may not use this file except in compliance with the License.
6f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * You may obtain a copy of the License at
7f94abcf44fc1611f76e55461f48220e621fc31b7John Reck *
8f94abcf44fc1611f76e55461f48220e621fc31b7John Reck *      http://www.apache.org/licenses/LICENSE-2.0
9f94abcf44fc1611f76e55461f48220e621fc31b7John Reck *
10f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * Unless required by applicable law or agreed to in writing, software
11f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * distributed under the License is distributed on an "AS IS" BASIS,
12f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * See the License for the specific language governing permissions and
14f94abcf44fc1611f76e55461f48220e621fc31b7John Reck * limitations under the License.
15f94abcf44fc1611f76e55461f48220e621fc31b7John Reck */
16f94abcf44fc1611f76e55461f48220e621fc31b7John Reckpackage com.android.browser.util;
17f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
18f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.content.Context;
19f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.database.Cursor;
20f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.Handler;
21f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.HandlerThread;
22f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.Message;
23f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.os.Process;
243ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reckimport android.os.SystemProperties;
25f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.util.Log;
26f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.view.View;
27f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.view.ViewGroup;
28f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.widget.Adapter;
29f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.widget.BaseAdapter;
30f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport android.widget.CursorAdapter;
31f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
32f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport com.android.browser.R;
33f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
34f94abcf44fc1611f76e55461f48220e621fc31b7John Reckimport java.lang.ref.WeakReference;
35f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
36f94abcf44fc1611f76e55461f48220e621fc31b7John Reckpublic abstract class ThreadedCursorAdapter<T> extends BaseAdapter {
37f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
383ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck    private static final String LOGTAG = "BookmarksThreadedAdapter";
39f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private static final boolean DEBUG = false;
40f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
41f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private Context mContext;
42f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private Object mCursorLock = new Object();
43f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private CursorAdapter mCursorAdapter;
44f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private T mLoadingObject;
45f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private Handler mLoadHandler;
46f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private Handler mHandler;
47f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private int mSize;
483ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck    private boolean mHasCursor;
493ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck    private long mGeneration;
503ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck
51f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private class LoadContainer {
52f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        WeakReference<View> view;
53f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        int position;
54f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        T bind_object;
55f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        Adapter owner;
56f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        boolean loaded;
573ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck        long generation;
58f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
59f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
60f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public ThreadedCursorAdapter(Context context, Cursor c) {
61f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        mContext = context;
623ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck        mHasCursor = (c != null);
63f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        mCursorAdapter = new CursorAdapter(context, c, 0) {
64f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
65f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @Override
66f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            public View newView(Context context, Cursor cursor, ViewGroup parent) {
67f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                throw new IllegalStateException("not supported");
68f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            }
69f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
70f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @Override
71f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            public void bindView(View view, Context context, Cursor cursor) {
72f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                throw new IllegalStateException("not supported");
73f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            }
74f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
75f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @Override
76f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            public void notifyDataSetChanged() {
77f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                super.notifyDataSetChanged();
78f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                mSize = getCount();
793ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                mGeneration++;
80f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                ThreadedCursorAdapter.this.notifyDataSetChanged();
81f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            }
82f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
83f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @Override
84f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            public void notifyDataSetInvalidated() {
85f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                super.notifyDataSetInvalidated();
86f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                mSize = getCount();
873ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                mGeneration++;
88f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                ThreadedCursorAdapter.this.notifyDataSetInvalidated();
89f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            }
90f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
91f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        };
92f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        mSize = mCursorAdapter.getCount();
93f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        HandlerThread thread = new HandlerThread("threaded_adapter_" + this,
94f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                Process.THREAD_PRIORITY_BACKGROUND);
95f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        thread.start();
96f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        mLoadHandler = new Handler(thread.getLooper()) {
97f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @SuppressWarnings("unchecked")
98f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @Override
99f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            public void handleMessage(Message msg) {
100f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                if (DEBUG) {
101f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                    Log.d(LOGTAG, "loading: " + msg.what);
102f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                }
103f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                loadRowObject(msg.what, (LoadContainer) msg.obj);
104f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            }
105f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        };
106f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        mHandler = new Handler() {
107f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            @Override
108f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            public void handleMessage(Message msg) {
109f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                @SuppressWarnings("unchecked")
110f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                LoadContainer container = (LoadContainer) msg.obj;
111f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                if (container == null) {
112f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                    return;
113f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                }
114f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                View view = container.view.get();
115f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                if (view == null
116f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                        || container.owner != ThreadedCursorAdapter.this
1173ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                        || container.position != msg.what
1183ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                        || view.getWindowToken() == null
1193ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                        || container.generation != mGeneration) {
120f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                    return;
121f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                }
122f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                container.loaded = true;
123f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                bindView(view, container.bind_object);
124f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            }
125f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        };
126f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
127f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
128f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    @Override
129f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public int getCount() {
130f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        return mSize;
131f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
132f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
133f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    @Override
134f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public Cursor getItem(int position) {
135f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        return (Cursor) mCursorAdapter.getItem(position);
136f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
137f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
138f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    @Override
139f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public long getItemId(int position) {
1409ee87d9ce5aae1460f53bea66bfcafd3671505a0John Reck        synchronized (mCursorLock) {
1419ee87d9ce5aae1460f53bea66bfcafd3671505a0John Reck            return getItemId(getItem(position));
1429ee87d9ce5aae1460f53bea66bfcafd3671505a0John Reck        }
143f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
144f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
145f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private void loadRowObject(int position, LoadContainer container) {
146f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        if (container == null
147f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                || container.position != position
148f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                || container.owner != ThreadedCursorAdapter.this
149f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                || container.view.get() == null) {
150f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            return;
151f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
152f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        synchronized (mCursorLock) {
153f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            Cursor c = (Cursor) mCursorAdapter.getItem(position);
1543ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck            if (c == null || c.isClosed()) {
1553ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                return;
1563ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck            }
157dedcee2bbf80f32165398af66c5d72e886c102e8John Reck            container.bind_object = getRowObject(c, container.bind_object);
158f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
159f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        mHandler.obtainMessage(position, container).sendToTarget();
160f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
161f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
162f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    @Override
163f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public View getView(int position, View convertView, ViewGroup parent) {
164f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        if (convertView == null) {
165f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            convertView = newView(mContext, parent);
166f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
167f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        @SuppressWarnings("unchecked")
168f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        LoadContainer container = (LoadContainer) convertView.getTag(R.id.load_object);
169f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        if (container == null) {
170f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            container = new LoadContainer();
171f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            container.view = new WeakReference<View>(convertView);
172f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            convertView.setTag(R.id.load_object, container);
173f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
174f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        if (container.position == position
175f94abcf44fc1611f76e55461f48220e621fc31b7John Reck                && container.owner == this
1763ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                && container.loaded
1773ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                && container.generation == mGeneration) {
178f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            bindView(convertView, container.bind_object);
179f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        } else {
180f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            bindView(convertView, cachedLoadObject());
1813ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck            if (mHasCursor) {
1823ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                container.position = position;
1833ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                container.loaded = false;
1843ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                container.owner = this;
1853ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                container.generation = mGeneration;
1863ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck                mLoadHandler.obtainMessage(position, container).sendToTarget();
1873ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck            }
188f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
189f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        return convertView;
190f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
191f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
192f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    private T cachedLoadObject() {
193f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        if (mLoadingObject == null) {
194f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            mLoadingObject = getLoadingObject();
195f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
196f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        return mLoadingObject;
197f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
198f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
199f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public void changeCursor(Cursor cursor) {
2003ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck        mLoadHandler.removeCallbacksAndMessages(null);
2013ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck        mHandler.removeCallbacksAndMessages(null);
202f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        synchronized (mCursorLock) {
2033ad8d5cf0911ba015e019bc75e8fb13c091efe0eJohn Reck            mHasCursor = (cursor != null);
204f94abcf44fc1611f76e55461f48220e621fc31b7John Reck            mCursorAdapter.changeCursor(cursor);
205f94abcf44fc1611f76e55461f48220e621fc31b7John Reck        }
206f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    }
207f94abcf44fc1611f76e55461f48220e621fc31b7John Reck
208f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public abstract View newView(Context context, ViewGroup parent);
209f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public abstract void bindView(View view, T object);
210f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public abstract T getRowObject(Cursor c, T recycleObject);
211f94abcf44fc1611f76e55461f48220e621fc31b7John Reck    public abstract T getLoadingObject();
2129ee87d9ce5aae1460f53bea66bfcafd3671505a0John Reck    protected abstract long getItemId(Cursor c);
213f94abcf44fc1611f76e55461f48220e621fc31b7John Reck}