RemoteViewsAdapter.java revision 6394c0e52cf641d93f678fd052499aa952e3595d
1499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/*
2499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Copyright (C) 2007 The Android Open Source Project
3499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
4499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * you may not use this file except in compliance with the License.
6499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * You may obtain a copy of the License at
7499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
8499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
10499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Unless required by applicable law or agreed to in writing, software
11499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * See the License for the specific language governing permissions and
14499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * limitations under the License.
15499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
16499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
17499cb9f516062b654952d282f211bee44c31a3c2Winson Chungpackage android.widget;
18499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
19499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap;
20c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chungimport java.util.LinkedList;
21499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.Map;
22499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
23499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.ComponentName;
24499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context;
25499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent;
26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.ServiceConnection;
27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.graphics.Color;
28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler;
29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread;
30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder;
31499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper;
32499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.RemoteException;
33499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.Gravity;
34499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View;
35499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.ViewGroup;
36499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View.MeasureSpec;
37499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
38499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory;
39499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
40499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/**
41499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews
42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views.
43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */
45499cb9f516062b654952d282f211bee44c31a3c2Winson Chungpublic class RemoteViewsAdapter extends BaseAdapter {
46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
47499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private static final String LOG_TAG = "RemoteViewsAdapter";
48499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
49499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Context mContext;
50499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Intent mIntent;
51499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private RemoteViewsAdapterServiceConnection mServiceConnection;
52499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private RemoteViewsCache mViewCache;
53499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
54499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private HandlerThread mWorkerThread;
55499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    // items may be interrupted within the normally processed queues
56499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mWorkerQueue;
57499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mMainQueue;
58499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    // items are never dequeued from the priority queue and must run
59499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mWorkerPriorityQueue;
60499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mMainPriorityQueue;
61499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
62499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
63499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An interface for the RemoteAdapter to notify other classes when adapters
64499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * are actually connected to/disconnected from their actual services.
65499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
66499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public interface RemoteAdapterConnectionCallback {
67499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterConnected();
68499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
69499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterDisconnected();
70499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
71499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
72499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
73499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * The service connection that gets populated when the RemoteViewsService is
74499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * bound.
75499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
76499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private class RemoteViewsAdapterServiceConnection implements ServiceConnection {
77499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private boolean mConnected;
78499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private IRemoteViewsFactory mRemoteViewsFactory;
79499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteAdapterConnectionCallback mCallback;
80499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public RemoteViewsAdapterServiceConnection(RemoteAdapterConnectionCallback callback) {
82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mCallback = callback;
83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onServiceConnected(ComponentName name, IBinder service) {
86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
87499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mConnected = true;
88c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
89499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // notifyDataSetChanged should be called first, to ensure that the
90499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // views are not updated twice
91499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            notifyDataSetChanged();
92499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // post a new runnable to load the appropriate data, then callback
94499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mWorkerPriorityQueue.post(new Runnable() {
95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
97499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // we need to get the viewTypeCount specifically, so just get all the
98499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // metadata
99499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache.requestMetaData();
100499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
101499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // post a runnable to call the callback on the main thread
102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mMainPriorityQueue.post(new Runnable() {
103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        @Override
104499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        public void run() {
105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            if (mCallback != null)
106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                mCallback.onRemoteAdapterConnected();
107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
109499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
110499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
1113ad4a1b13f789757a46dc0aea98c97b9fdc5ef6cAdam Cohen
1123ad4a1b13f789757a46dc0aea98c97b9fdc5ef6cAdam Cohen            // start the background loader
1133ad4a1b13f789757a46dc0aea98c97b9fdc5ef6cAdam Cohen            mViewCache.startBackgroundLoader();
114499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
115499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
116499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onServiceDisconnected(ComponentName name) {
117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = null;
118499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mConnected = false;
119499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
120499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // clear the main/worker queues
121499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mMainQueue.removeMessages(0);
122c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
123c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // stop the background loader
124c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mViewCache.stopBackgroundLoader();
125c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
126c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            if (mCallback != null)
127c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                mCallback.onRemoteAdapterDisconnected();
128499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
129499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
130499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public IRemoteViewsFactory getRemoteViewsFactory() {
131499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mRemoteViewsFactory;
132499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
133499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
134499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public boolean isConnected() {
135499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mConnected;
136499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
137499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
138499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
139499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
140499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An internal cache of remote views.
141499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private class RemoteViewsCache {
143499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsInfo mViewCacheInfo;
144499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsIndexInfo[] mViewCache;
145c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        private int[] mTmpViewCacheLoadIndices;
146c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        private LinkedList<Integer> mViewCacheLoadIndices;
147c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        private boolean mBackgroundLoaderEnabled;
148499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
149499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // if a user loading view is not provided, then we create a temporary one
150499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // for the user using the height of the first view
151499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViews mUserLoadingView;
152499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViews mFirstView;
153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mFirstViewHeight;
154499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
155499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // determines when the current cache window needs to be updated with new
156499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // items (ie. when there is not enough slack)
157499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mViewCacheStartPosition;
158499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mViewCacheEndPosition;
159499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mHalfCacheSize;
160499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mCacheSlack;
161499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final float mCacheSlackPercentage = 0.75f;
162499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
163499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
164499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         * The data structure stored at each index of the cache. Any member
165499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         * that is not invalidated persists throughout the lifetime of the cache.
166499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
167499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private class RemoteViewsIndexInfo {
168499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            FrameLayout flipper;
169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            RemoteViews view;
170499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            long itemId;
171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int typeId;
172499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
173499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            RemoteViewsIndexInfo() {
174499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                invalidate();
175499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
176499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
177499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            void set(RemoteViews v, long id) {
178499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                view = v;
179499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                itemId = id;
180499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (v != null)
181499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    typeId = v.getLayoutId();
182499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                else
183499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    typeId = 0;
184499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
185499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
186499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            void invalidate() {
187499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                view = null;
188499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                itemId = 0;
189499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                typeId = 0;
190499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
191499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
192499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            final boolean isValid() {
193499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return (view != null);
194499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
195499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
196499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
197499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
198499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         * Remote adapter metadata. Useful for when we have to lock on something
199499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         * before updating the metadata.
200499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
201499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private class RemoteViewsInfo {
202499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int count;
203499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int viewTypeCount;
204499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            boolean hasStableIds;
2056394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            boolean isDataDirty;
206499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            Map<Integer, Integer> mTypeIdIndexMap;
207499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
208499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            RemoteViewsInfo() {
209499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                count = 0;
210499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // by default there is at least one dummy view type
211499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                viewTypeCount = 1;
212499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                hasStableIds = true;
2136394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                isDataDirty = false;
214499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mTypeIdIndexMap = new HashMap<Integer, Integer>();
215499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
216499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
217499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
218499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public RemoteViewsCache(int halfCacheSize) {
219499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mHalfCacheSize = halfCacheSize;
220499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize);
221499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheStartPosition = 0;
222499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheEndPosition = -1;
223c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mBackgroundLoaderEnabled = false;
224499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
225499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // initialize the cache
226c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            int cacheSize = 2 * mHalfCacheSize + 1;
227499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheInfo = new RemoteViewsInfo();
228c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mViewCache = new RemoteViewsIndexInfo[cacheSize];
229499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            for (int i = 0; i < mViewCache.length; ++i) {
230499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCache[i] = new RemoteViewsIndexInfo();
231499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
232c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mTmpViewCacheLoadIndices = new int[cacheSize];
233c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mViewCacheLoadIndices = new LinkedList<Integer>();
234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
235499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
236499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final boolean contains(int position) {
237c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            return (mViewCacheStartPosition <= position) && (position <= mViewCacheEndPosition);
238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
239499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
240499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final boolean containsAndIsValid(int position) {
241499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (contains(position)) {
242499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViewsIndexInfo indexInfo = mViewCache[getCacheIndex(position)];
243499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (indexInfo.isValid()) {
244499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return true;
245499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
246499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
247499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return false;
248499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
249499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
250499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final int getCacheIndex(int position) {
251c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // take the modulo of the position
252499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return (mViewCache.length + (position % mViewCache.length)) % mViewCache.length;
253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void requestMetaData() {
256499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
257499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                try {
258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
260499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // get the properties/first view (so that we can use it to
261499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // measure our dummy views)
262499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    boolean hasStableIds = factory.hasStableIds();
263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int viewTypeCount = factory.getViewTypeCount();
264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int count = factory.getCount();
265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    RemoteViews loadingView = factory.getLoadingView();
266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    RemoteViews firstView = null;
267499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if ((count > 0) && (loadingView == null)) {
268499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        firstView = factory.getViewAt(0);
269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
270499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    synchronized (mViewCacheInfo) {
271499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        RemoteViewsInfo info = mViewCacheInfo;
272499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        info.hasStableIds = hasStableIds;
273499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        info.viewTypeCount = viewTypeCount + 1;
274499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        info.count = count;
275499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mUserLoadingView = loadingView;
276499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        if (firstView != null) {
277499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mFirstView = firstView;
278499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mFirstViewHeight = -1;
279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
280499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
281499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                } catch (RemoteException e) {
282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    e.printStackTrace();
283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
285499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
286499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2876394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        protected void onNotifyDataSetChanged() {
2886394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            // we mark the data as dirty so that the next call to fetch views will result in
2896394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            // an onDataSetDirty() call from the adapter
2906394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            synchronized (mViewCacheInfo) {
2916394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                mViewCacheInfo.isDataDirty = true;
2926394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
2936394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
2946394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
2956394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        private void updateNotifyDataSetChanged() {
2966394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            // actually calls through to the factory to notify it to update
2976394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            if (mServiceConnection.isConnected()) {
2986394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
2996394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                try {
3006394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                    factory.onDataSetChanged();
3016394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                } catch (RemoteException e) {
3026394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                    e.printStackTrace();
3036394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                }
3046394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3056394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
3066394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3076394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            // re-request the new metadata (only after the notification to the factory)
3086394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            requestMetaData();
3096394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3106394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            // post a new runnable on the main thread to propagate the notification back
3116394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            // to the base adapter
3126394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            mMainQueue.post(new Runnable() {
3136394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                @Override
3146394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                public void run() {
3156394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                   completeNotifyDataSetChanged();
3166394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                }
3176394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            });
3186394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
3196394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
320499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        protected void updateRemoteViewsInfo(int position) {
321499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
322499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
323499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
324499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // load the item information
325499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViews remoteView = null;
326499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                long itemId = 0;
327499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                try {
328499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    remoteView = factory.getViewAt(position);
329499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    itemId = factory.getItemId(position);
330499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                } catch (RemoteException e) {
331499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    e.printStackTrace();
332499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
333499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
334499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                synchronized (mViewCache) {
335499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // skip if the window has moved
336c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    if (position < mViewCacheStartPosition || position > mViewCacheEndPosition)
337499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return;
338499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
339499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    final int positionIndex = position;
340499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    final int cacheIndex = getCacheIndex(position);
341499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache[cacheIndex].set(remoteView, itemId);
342499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
343499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // notify the main thread when done loading
344499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // flush pending updates
345499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mMainQueue.post(new Runnable() {
346499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        @Override
347499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        public void run() {
348499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // swap the loader view for this view
349499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            synchronized (mViewCache) {
350499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                if (containsAndIsValid(positionIndex)) {
351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex];
352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    FrameLayout flipper = indexInfo.flipper;
353499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
354c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    // update the flipper
355c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    flipper.getChildAt(0).setVisibility(View.GONE);
356c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    boolean addNewView = true;
357c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    if (flipper.getChildCount() > 1) {
358c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        View v = flipper.getChildAt(1);
359c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        int typeId = ((Integer) v.getTag()).intValue();
360c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        if (typeId == indexInfo.typeId) {
361c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            // we can reapply since it is the same type
362c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            indexInfo.view.reapply(mContext, v);
363c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            v.setVisibility(View.VISIBLE);
364c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            if (v.getAnimation() != null)
365c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                                v.buildDrawingCache();
366c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            addNewView = false;
367c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        } else {
368c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            flipper.removeViewAt(1);
369c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        }
370c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    }
371c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    if (addNewView) {
372c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        View v = indexInfo.view.apply(mContext, flipper);
373c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        v.setTag(new Integer(indexInfo.typeId));
374c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        flipper.addView(v);
375c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    }
376499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                }
377499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
378499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
380499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
381499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
382499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
383499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
384499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
385499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int indicesToLoadCount = 0;
386499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
387499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
388499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
389499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // return the info if it exists in the window and is loaded
390499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return mViewCache[getCacheIndex(position)];
391c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
392499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // if necessary update the window and load the new information
394499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2;
395499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
396499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int newStartPosition = position - mHalfCacheSize;
397499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int newEndPosition = position + mHalfCacheSize;
398c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    int frameSize = mHalfCacheSize / 4;
399c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    int frameCount = (int) Math.ceil(mViewCache.length / (float) frameSize);
400499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
401499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // prune/add before the current start position
402499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int effectiveStart = Math.max(newStartPosition, 0);
403c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    int effectiveEnd = Math.min(newEndPosition, getCount() - 1);
404499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
405499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // invalidate items in the queue
406499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
407499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
408c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    for (int i = 0; i < (frameSize * frameCount); ++i) {
409c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        int index = newStartPosition + ((i % frameSize) * frameCount + (i / frameSize));
410c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
411c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        if (index <= newEndPosition) {
412c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            if ((overlapStart <= index) && (index <= overlapEnd)) {
413c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // load the stuff in the middle that has not already
414c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // been loaded
415c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                if (!mViewCache[getCacheIndex(index)].isValid()) {
416c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
417c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                }
418c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } else if ((effectiveStart <= index) && (index <= effectiveEnd)) {
419c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // invalidate and load all new effective items
420c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                mViewCache[getCacheIndex(index)].invalidate();
421c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
422c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } else {
423c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // invalidate all other cache indices (outside the effective start/end)
424c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // but don't load
425c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                mViewCache[getCacheIndex(index)].invalidate();
426499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
427499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
428499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
429499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
430499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCacheStartPosition = newStartPosition;
431499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCacheEndPosition = newEndPosition;
432499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
433499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
434499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
435499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // post items to be loaded
436499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int length = 0;
437499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
438499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                length = mViewCacheInfo.count;
439499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
440c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            if (indicesToLoadCount > 0) {
441c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                synchronized (mViewCacheLoadIndices) {
442c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    mViewCacheLoadIndices.clear();
443c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    for (int i = 0; i < indicesToLoadCount; ++i) {
444c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        final int index = mTmpViewCacheLoadIndices[i];
445c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        if (0 <= index && index < length) {
446c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            mViewCacheLoadIndices.addLast(index);
447499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
448c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    }
449499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
450499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
451499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
452499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // return null so that a dummy view can be retrieved
453499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return null;
454499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
455499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
456499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public View getView(int position, View convertView, ViewGroup parent) {
457499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
458499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // create the flipper views if necessary (we have to do this now
459499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // for all the flippers while we have the reference to the parent)
460c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                initializeLoadingViews(parent);
461499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
462499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // request the item from the cache (queueing it to load if not
463499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // in the cache already)
464499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViewsIndexInfo indexInfo = requestCachedIndexInfo(position);
465499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
466499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // update the flipper appropriately
467499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                synchronized (mViewCache) {
468499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int cacheIndex = getCacheIndex(position);
469499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    FrameLayout flipper = mViewCache[cacheIndex].flipper;
470c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    flipper.setVisibility(View.VISIBLE);
47144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    flipper.setAlpha(1.0f);
472499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
473499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (indexInfo == null) {
474499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        // hide the item view and show the loading view
475499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.getChildAt(0).setVisibility(View.VISIBLE);
476499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 1; i < flipper.getChildCount(); ++i) {
477499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.getChildAt(i).setVisibility(View.GONE);
478499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
479499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    } else {
480499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        // hide the loading view and show the item view
481499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
482499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.getChildAt(i).setVisibility(View.GONE);
483499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
484499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
485499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
486499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return flipper;
487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
488499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
489499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return new View(mContext);
490499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
491499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
492c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        private void initializeLoadingViews(ViewGroup parent) {
493499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // ensure that the cache has the appropriate initial flipper
494499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
495499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (mViewCache[0].flipper == null) {
496499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    for (int i = 0; i < mViewCache.length; ++i) {
497499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        FrameLayout flipper = new FrameLayout(mContext);
498499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        if (mUserLoadingView != null) {
499499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // use the user-specified loading view
500499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.addView(mUserLoadingView.apply(mContext, parent));
501499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        } else {
502499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // calculate the original size of the first row for the loader view
503499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            synchronized (mViewCacheInfo) {
504499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                if (mFirstViewHeight < 0) {
505499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    View firstView = mFirstView.apply(mContext, parent);
506499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    firstView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
507499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
508499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    mFirstViewHeight = firstView.getMeasuredHeight();
509499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                }
510499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
511499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
512499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // construct a new loader and add it to the flipper as the fallback
513499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // default view
514499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            TextView textView = new TextView(mContext);
515499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setText("Loading...");
516499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setHeight(mFirstViewHeight);
517499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
518499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setTextSize(18.0f);
519499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setTextColor(Color.argb(96, 255, 255, 255));
520499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK);
521499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
522499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.addView(textView);
523499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
524499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mViewCache[i].flipper = flipper;
525499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
526499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
527499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
528499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
529499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
530c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        public void startBackgroundLoader() {
531c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // initialize the worker runnable
532c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mBackgroundLoaderEnabled = true;
533c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mWorkerQueue.post(new Runnable() {
534c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                @Override
535c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                public void run() {
536c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    while (mBackgroundLoaderEnabled) {
5376394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                        // notify the RemoteViews factory if necessary
5386394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                        boolean isDataDirty = false;
5396394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                        synchronized (mViewCacheInfo) {
5406394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                            isDataDirty = mViewCacheInfo.isDataDirty;
5416394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                            mViewCacheInfo.isDataDirty = false;
5426394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                        }
5436394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                        if (isDataDirty) {
5446394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                            updateNotifyDataSetChanged();
5456394c0e52cf641d93f678fd052499aa952e3595dWinson Chung                        }
5466394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
547c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        int index = -1;
548c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        synchronized (mViewCacheLoadIndices) {
549c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            if (!mViewCacheLoadIndices.isEmpty()) {
550c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                index = mViewCacheLoadIndices.removeFirst();
551c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            }
552c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        }
553c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        if (index < 0) {
554c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            // there were no items to load, so sleep for a bit
555c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            try {
556c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                Thread.sleep(10);
557c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } catch (InterruptedException e) {
558c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                e.printStackTrace();
559c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            }
560c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        } else {
561c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            // otherwise, try and load the item
562c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            updateRemoteViewsInfo(index);
563c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
564c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            // sleep for a bit to allow things to catch up after the load
565c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            try {
566c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                Thread.sleep(50);
567c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } catch (InterruptedException e) {
568c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                e.printStackTrace();
569c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            }
570c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        }
571c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    }
572c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
573c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            });
574c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
575c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
576c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        public void stopBackgroundLoader() {
577c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // clear the items to be loaded
578c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mBackgroundLoaderEnabled = false;
579c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            synchronized (mViewCacheLoadIndices) {
580c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                mViewCacheLoadIndices.clear();
581c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
582c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
583c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
584499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public long getItemId(int position) {
585499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
586499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
587499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return mViewCache[getCacheIndex(position)].itemId;
588499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
589499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
590499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return 0;
591499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
592499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
593499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getItemViewType(int position) {
594499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // synchronize to ensure that the type id/index map is updated synchronously
595499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
596499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
597499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int viewId = mViewCache[getCacheIndex(position)].typeId;
598499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    Map<Integer, Integer> typeMap = mViewCacheInfo.mTypeIdIndexMap;
599499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // we +1 because the default dummy view get view type 0
600499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (typeMap.containsKey(viewId)) {
601499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return typeMap.get(viewId);
602499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    } else {
603499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        int newIndex = typeMap.size() + 1;
604499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        typeMap.put(viewId, newIndex);
605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return newIndex;
606499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
607499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
608499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
609499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // return the type of the default item
610499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return 0;
611499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getCount() {
614499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
615499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.count;
616499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
617499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
618499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
619499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getViewTypeCount() {
620499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
621499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.viewTypeCount;
622499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
623499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
624499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
625499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public boolean hasStableIds() {
626499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
627499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.hasStableIds;
628499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
629499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
630499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
631499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void flushCache() {
632c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // clear the items to be loaded
633c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            synchronized (mViewCacheLoadIndices) {
634c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                mViewCacheLoadIndices.clear();
635c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
636c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
637499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
638499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // flush the internal cache and invalidate the adapter for future loads
639499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mMainQueue.removeMessages(0);
640499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
641499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                for (int i = 0; i < mViewCache.length; ++i) {
642499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache[i].invalidate();
643499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
644499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
645499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCacheStartPosition = 0;
646499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCacheEndPosition = -1;
647499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
648499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
649499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
650499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
651499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
652499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
653499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
654499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
655499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // initialize the worker thread
656499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
657499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
658499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
659499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerPriorityQueue = new Handler(mWorkerThread.getLooper());
660499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mMainQueue = new Handler(Looper.myLooper());
661499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mMainPriorityQueue = new Handler(Looper.myLooper());
662499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
663499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // initialize the cache and the service connection on startup
664499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mViewCache = new RemoteViewsCache(25);
665499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(callback);
666499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
667499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
668499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
669499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    protected void finalize() throws Throwable {
670499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // remember to unbind from the service when finalizing
671499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        unbindService();
672499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
673499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
674499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
675499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
676499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getCount();
677499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
678499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
679499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // disallow arbitrary object to be associated with an item for the time being
681499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
682499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
683499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
684499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
685499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
686499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getItemId(position);
687499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
688499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
689499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
690499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
691499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getItemViewType(position);
692499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
693499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
694499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
695499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
696499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getView(position, convertView, parent);
697499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
698499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
699499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
700499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
701499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getViewTypeCount();
702499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
703499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
704499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
705499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
706499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.hasStableIds();
707499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
708499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
709499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
710499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
711499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
712499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
713499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public void notifyDataSetChanged() {
714499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // flush the cache so that we can reload new items from the service
715499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mViewCache.flushCache();
7166394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
7176394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        // notify the factory that it's data may no longer be valid
7186394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        mViewCache.onNotifyDataSetChanged();
7196394c0e52cf641d93f678fd052499aa952e3595dWinson Chung    }
7206394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
7216394c0e52cf641d93f678fd052499aa952e3595dWinson Chung    public void completeNotifyDataSetChanged() {
722499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
723499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
724499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
725499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
726499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // try binding the service (which will start it if it's not already running)
727499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
729499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
730499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
731499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
732499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
733499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
734499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private void unbindService() {
735499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (mServiceConnection.isConnected()) {
736499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mContext.unbindService(mServiceConnection);
737499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
738499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
739499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
740