RemoteViewsAdapter.java revision c6d6d4a4e73fcb63eaa13d66fcbf26d847799838
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
89c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // start the background loader
90c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mViewCache.startBackgroundLoader();
91c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
92499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // notifyDataSetChanged should be called first, to ensure that the
93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // views are not updated twice
94499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            notifyDataSetChanged();
95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // post a new runnable to load the appropriate data, then callback
97499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mWorkerPriorityQueue.post(new Runnable() {
98499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
99499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
100499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // we need to get the viewTypeCount specifically, so just get all the
101499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // metadata
102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache.requestMetaData();
103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
104499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // post a runnable to call the callback on the main thread
105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mMainPriorityQueue.post(new Runnable() {
106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        @Override
107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        public void run() {
108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            if (mCallback != null)
109499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                mCallback.onRemoteAdapterConnected();
110499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
112499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
113499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
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;
205499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            Map<Integer, Integer> mTypeIdIndexMap;
206499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
207499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            RemoteViewsInfo() {
208499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                count = 0;
209499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // by default there is at least one dummy view type
210499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                viewTypeCount = 1;
211499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                hasStableIds = true;
212499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mTypeIdIndexMap = new HashMap<Integer, Integer>();
213499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
214499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
215499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
216499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public RemoteViewsCache(int halfCacheSize) {
217499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mHalfCacheSize = halfCacheSize;
218499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize);
219499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheStartPosition = 0;
220499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheEndPosition = -1;
221c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mBackgroundLoaderEnabled = false;
222499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
223499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // initialize the cache
224c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            int cacheSize = 2 * mHalfCacheSize + 1;
225499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheInfo = new RemoteViewsInfo();
226c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mViewCache = new RemoteViewsIndexInfo[cacheSize];
227499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            for (int i = 0; i < mViewCache.length; ++i) {
228499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCache[i] = new RemoteViewsIndexInfo();
229499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
230c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mTmpViewCacheLoadIndices = new int[cacheSize];
231c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mViewCacheLoadIndices = new LinkedList<Integer>();
232499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final boolean contains(int position) {
235c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            return (mViewCacheStartPosition <= position) && (position <= mViewCacheEndPosition);
236499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
237499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final boolean containsAndIsValid(int position) {
239499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (contains(position)) {
240499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViewsIndexInfo indexInfo = mViewCache[getCacheIndex(position)];
241499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (indexInfo.isValid()) {
242499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return true;
243499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
244499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
245499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return false;
246499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
247499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
248499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final int getCacheIndex(int position) {
249c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // take the modulo of the position
250499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return (mViewCache.length + (position % mViewCache.length)) % mViewCache.length;
251499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
252499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void requestMetaData() {
254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                try {
256499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
257499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // get the properties/first view (so that we can use it to
259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // measure our dummy views)
260499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    boolean hasStableIds = factory.hasStableIds();
261499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int viewTypeCount = factory.getViewTypeCount();
262499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int count = factory.getCount();
263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    RemoteViews loadingView = factory.getLoadingView();
264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    RemoteViews firstView = null;
265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if ((count > 0) && (loadingView == null)) {
266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        firstView = factory.getViewAt(0);
267499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
268499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    synchronized (mViewCacheInfo) {
269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        RemoteViewsInfo info = mViewCacheInfo;
270499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        info.hasStableIds = hasStableIds;
271499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        info.viewTypeCount = viewTypeCount + 1;
272499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        info.count = count;
273499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mUserLoadingView = loadingView;
274499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        if (firstView != null) {
275499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mFirstView = firstView;
276499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mFirstViewHeight = -1;
277499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
278499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                } catch (RemoteException e) {
280499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    e.printStackTrace();
281499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
285499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        protected void updateRemoteViewsInfo(int position) {
286499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
287499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
288499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
289499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // load the item information
290499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViews remoteView = null;
291499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                long itemId = 0;
292499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                try {
293499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    remoteView = factory.getViewAt(position);
294499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    itemId = factory.getItemId(position);
295499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                } catch (RemoteException e) {
296499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    e.printStackTrace();
297499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
298499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                synchronized (mViewCache) {
300499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // skip if the window has moved
301c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    if (position < mViewCacheStartPosition || position > mViewCacheEndPosition)
302499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return;
303499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
304499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    final int positionIndex = position;
305499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    final int cacheIndex = getCacheIndex(position);
306499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache[cacheIndex].set(remoteView, itemId);
307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
308499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // notify the main thread when done loading
309499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // flush pending updates
310499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mMainQueue.post(new Runnable() {
311499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        @Override
312499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        public void run() {
313499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // swap the loader view for this view
314499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            synchronized (mViewCache) {
315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                if (containsAndIsValid(positionIndex)) {
316499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex];
317499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    FrameLayout flipper = indexInfo.flipper;
318499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
319c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    // update the flipper
320c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    flipper.getChildAt(0).setVisibility(View.GONE);
321c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    boolean addNewView = true;
322c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    if (flipper.getChildCount() > 1) {
323c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        View v = flipper.getChildAt(1);
324c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        int typeId = ((Integer) v.getTag()).intValue();
325c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        if (typeId == indexInfo.typeId) {
326c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            // we can reapply since it is the same type
327c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            indexInfo.view.reapply(mContext, v);
328c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            v.setVisibility(View.VISIBLE);
329c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            if (v.getAnimation() != null)
330c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                                v.buildDrawingCache();
331c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            addNewView = false;
332c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        } else {
333c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                            flipper.removeViewAt(1);
334c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        }
335c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    }
336c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    if (addNewView) {
337c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        View v = indexInfo.view.apply(mContext, flipper);
338c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        v.setTag(new Integer(indexInfo.typeId));
339c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                        flipper.addView(v);
340c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    }
341499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                }
342499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
343499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
344499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
345499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
346499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
347499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
348499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
349499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
350499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int indicesToLoadCount = 0;
351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
353499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
354499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // return the info if it exists in the window and is loaded
355499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return mViewCache[getCacheIndex(position)];
356c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
357499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
358499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // if necessary update the window and load the new information
359499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2;
360499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
361499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int newStartPosition = position - mHalfCacheSize;
362499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int newEndPosition = position + mHalfCacheSize;
363c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    int frameSize = mHalfCacheSize / 4;
364c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    int frameCount = (int) Math.ceil(mViewCache.length / (float) frameSize);
365499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
366499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // prune/add before the current start position
367499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int effectiveStart = Math.max(newStartPosition, 0);
368c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    int effectiveEnd = Math.min(newEndPosition, getCount() - 1);
369499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
370499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // invalidate items in the queue
371499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
373c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    for (int i = 0; i < (frameSize * frameCount); ++i) {
374c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        int index = newStartPosition + ((i % frameSize) * frameCount + (i / frameSize));
375c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
376c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        if (index <= newEndPosition) {
377c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            if ((overlapStart <= index) && (index <= overlapEnd)) {
378c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // load the stuff in the middle that has not already
379c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // been loaded
380c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                if (!mViewCache[getCacheIndex(index)].isValid()) {
381c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                    mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
382c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                }
383c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } else if ((effectiveStart <= index) && (index <= effectiveEnd)) {
384c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // invalidate and load all new effective items
385c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                mViewCache[getCacheIndex(index)].invalidate();
386c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                mTmpViewCacheLoadIndices[indicesToLoadCount++] = index;
387c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } else {
388c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // invalidate all other cache indices (outside the effective start/end)
389c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                // but don't load
390c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                mViewCache[getCacheIndex(index)].invalidate();
391499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
392499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
394499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
395499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCacheStartPosition = newStartPosition;
396499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCacheEndPosition = newEndPosition;
397499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
398499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
399499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
400499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // post items to be loaded
401499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int length = 0;
402499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
403499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                length = mViewCacheInfo.count;
404499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
405c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            if (indicesToLoadCount > 0) {
406c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                synchronized (mViewCacheLoadIndices) {
407c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    mViewCacheLoadIndices.clear();
408c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    for (int i = 0; i < indicesToLoadCount; ++i) {
409c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        final int index = mTmpViewCacheLoadIndices[i];
410c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        if (0 <= index && index < length) {
411c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            mViewCacheLoadIndices.addLast(index);
412499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
413c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    }
414499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
415499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
416499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
417499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // return null so that a dummy view can be retrieved
418499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return null;
419499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
420499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
421499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public View getView(int position, View convertView, ViewGroup parent) {
422499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
423499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // create the flipper views if necessary (we have to do this now
424499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // for all the flippers while we have the reference to the parent)
425c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                initializeLoadingViews(parent);
426499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
427499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // request the item from the cache (queueing it to load if not
428499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // in the cache already)
429499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViewsIndexInfo indexInfo = requestCachedIndexInfo(position);
430499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
431499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // update the flipper appropriately
432499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                synchronized (mViewCache) {
433499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int cacheIndex = getCacheIndex(position);
434499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    FrameLayout flipper = mViewCache[cacheIndex].flipper;
435c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    flipper.setVisibility(View.VISIBLE);
436499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
437499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (indexInfo == null) {
438499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        // hide the item view and show the loading view
439499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.getChildAt(0).setVisibility(View.VISIBLE);
440499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 1; i < flipper.getChildCount(); ++i) {
441499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.getChildAt(i).setVisibility(View.GONE);
442499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
443499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    } else {
444499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        // hide the loading view and show the item view
445499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
446499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.getChildAt(i).setVisibility(View.GONE);
447499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
448499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
449499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
450499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return flipper;
451499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
452499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
453499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return new View(mContext);
454499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
455499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
456c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        private void initializeLoadingViews(ViewGroup parent) {
457499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // ensure that the cache has the appropriate initial flipper
458499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
459499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (mViewCache[0].flipper == null) {
460499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    for (int i = 0; i < mViewCache.length; ++i) {
461499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        FrameLayout flipper = new FrameLayout(mContext);
462499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        if (mUserLoadingView != null) {
463499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // use the user-specified loading view
464499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.addView(mUserLoadingView.apply(mContext, parent));
465499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        } else {
466499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // calculate the original size of the first row for the loader view
467499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            synchronized (mViewCacheInfo) {
468499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                if (mFirstViewHeight < 0) {
469499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    View firstView = mFirstView.apply(mContext, parent);
470499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    firstView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
471499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
472499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    mFirstViewHeight = firstView.getMeasuredHeight();
473499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                }
474499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
475499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
476499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // construct a new loader and add it to the flipper as the fallback
477499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // default view
478499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            TextView textView = new TextView(mContext);
479499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setText("Loading...");
480499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setHeight(mFirstViewHeight);
481499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
482499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setTextSize(18.0f);
483499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setTextColor(Color.argb(96, 255, 255, 255));
484499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK);
485499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
486499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.addView(textView);
487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
488499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mViewCache[i].flipper = flipper;
489499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
490499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
491499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
492499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
493499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
494c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        public void startBackgroundLoader() {
495c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // initialize the worker runnable
496c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mBackgroundLoaderEnabled = true;
497c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mWorkerQueue.post(new Runnable() {
498c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                @Override
499c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                public void run() {
500c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    while (mBackgroundLoaderEnabled) {
501c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        int index = -1;
502c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        synchronized (mViewCacheLoadIndices) {
503c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            if (!mViewCacheLoadIndices.isEmpty()) {
504c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                index = mViewCacheLoadIndices.removeFirst();
505c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            }
506c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        }
507c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        if (index < 0) {
508c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            // there were no items to load, so sleep for a bit
509c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            try {
510c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                Thread.sleep(10);
511c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } catch (InterruptedException e) {
512c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                e.printStackTrace();
513c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            }
514c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        } else {
515c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            // otherwise, try and load the item
516c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            updateRemoteViewsInfo(index);
517c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
518c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            // sleep for a bit to allow things to catch up after the load
519c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            try {
520c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                Thread.sleep(50);
521c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            } catch (InterruptedException e) {
522c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                                e.printStackTrace();
523c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                            }
524c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                        }
525c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                    }
526c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
527c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            });
528c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
529c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
530c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        public void stopBackgroundLoader() {
531c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // clear the items to be loaded
532c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            mBackgroundLoaderEnabled = false;
533c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            synchronized (mViewCacheLoadIndices) {
534c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                mViewCacheLoadIndices.clear();
535c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
536c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
537c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
538499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public long getItemId(int position) {
539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
540499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
541499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return mViewCache[getCacheIndex(position)].itemId;
542499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
543499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
544499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return 0;
545499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
546499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
547499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getItemViewType(int position) {
548499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // synchronize to ensure that the type id/index map is updated synchronously
549499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
550499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
551499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int viewId = mViewCache[getCacheIndex(position)].typeId;
552499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    Map<Integer, Integer> typeMap = mViewCacheInfo.mTypeIdIndexMap;
553499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // we +1 because the default dummy view get view type 0
554499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (typeMap.containsKey(viewId)) {
555499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return typeMap.get(viewId);
556499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    } else {
557499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        int newIndex = typeMap.size() + 1;
558499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        typeMap.put(viewId, newIndex);
559499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return newIndex;
560499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
561499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
562499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
563499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // return the type of the default item
564499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return 0;
565499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
566499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
567499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getCount() {
568499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
569499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.count;
570499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
571499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
572499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
573499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getViewTypeCount() {
574499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
575499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.viewTypeCount;
576499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
577499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
578499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
579499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public boolean hasStableIds() {
580499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
581499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.hasStableIds;
582499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
584499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
585499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void flushCache() {
586c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            // clear the items to be loaded
587c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            synchronized (mViewCacheLoadIndices) {
588c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                mViewCacheLoadIndices.clear();
589c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
590c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
591499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
592499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // flush the internal cache and invalidate the adapter for future loads
593499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mMainQueue.removeMessages(0);
594499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
595499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                for (int i = 0; i < mViewCache.length; ++i) {
596499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache[i].invalidate();
597499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
598499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
599499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCacheStartPosition = 0;
600499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCacheEndPosition = -1;
601499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
602499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
603499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
604499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
606499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
607499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
608499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
609499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // initialize the worker thread
610499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
611499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerPriorityQueue = new Handler(mWorkerThread.getLooper());
614499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mMainQueue = new Handler(Looper.myLooper());
615499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mMainPriorityQueue = new Handler(Looper.myLooper());
616499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
617499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // initialize the cache and the service connection on startup
618499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mViewCache = new RemoteViewsCache(25);
619499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(callback);
620499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
621499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
622499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
623499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    protected void finalize() throws Throwable {
624499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // remember to unbind from the service when finalizing
625499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        unbindService();
626499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
627499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
628499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
629499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
630499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getCount();
631499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
632499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
633499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
634499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // disallow arbitrary object to be associated with an item for the time being
635499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
636499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
637499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
638499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
639499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
640499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getItemId(position);
641499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
642499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
643499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
644499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
645499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getItemViewType(position);
646499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
647499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
648499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
649499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
650499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getView(position, convertView, parent);
651499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
652499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
653499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
654499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
655499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getViewTypeCount();
656499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
657499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
658499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
659499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
660499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.hasStableIds();
661499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
662499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
663499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
664499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
665499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
666499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
667499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public void notifyDataSetChanged() {
668499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // flush the cache so that we can reload new items from the service
669499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mViewCache.flushCache();
670499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
671499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
672499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
673499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
674499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // try binding the service (which will start it if it's not already running)
675499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
676499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
677499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
678499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
679499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
681499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
682499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private void unbindService() {
683499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (mServiceConnection.isConnected()) {
684499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mContext.unbindService(mServiceConnection);
685499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
686499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
687499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
688