RemoteViewsAdapter.java revision 499cb9f516062b654952d282f211bee44c31a3c2
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.Arrays;
20499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap;
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;
88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // notifyDataSetChanged should be called first, to ensure that the
89499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // views are not updated twice
90499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            notifyDataSetChanged();
91499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
92499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // post a new runnable to load the appropriate data, then callback
93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mWorkerPriorityQueue.post(new Runnable() {
94499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // we need to get the viewTypeCount specifically, so just get all the
97499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // metadata
98499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache.requestMetaData();
99499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
100499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // post a runnable to call the callback on the main thread
101499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mMainPriorityQueue.post(new Runnable() {
102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        @Override
103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        public void run() {
104499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            if (mCallback != null)
105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                mCallback.onRemoteAdapterConnected();
106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
109499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
110499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
112499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onServiceDisconnected(ComponentName name) {
113499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = null;
114499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mConnected = false;
115499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mCallback != null)
116499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mCallback.onRemoteAdapterDisconnected();
117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
118499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // clear the main/worker queues
119499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mMainQueue.removeMessages(0);
120499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mWorkerQueue.removeMessages(0);
121499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
122499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
123499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public IRemoteViewsFactory getRemoteViewsFactory() {
124499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mRemoteViewsFactory;
125499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
126499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
127499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public boolean isConnected() {
128499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mConnected;
129499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
130499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
131499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
132499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
133499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An internal cache of remote views.
134499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
135499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private class RemoteViewsCache {
136499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsInfo mViewCacheInfo;
137499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsIndexInfo[] mViewCache;
138499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
139499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // if a user loading view is not provided, then we create a temporary one
140499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // for the user using the height of the first view
141499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViews mUserLoadingView;
142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViews mFirstView;
143499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mFirstViewHeight;
144499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
145499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // determines when the current cache window needs to be updated with new
146499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // items (ie. when there is not enough slack)
147499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mViewCacheStartPosition;
148499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mViewCacheEndPosition;
149499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mHalfCacheSize;
150499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mCacheSlack;
151499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final float mCacheSlackPercentage = 0.75f;
152499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // determines whether to reorder the posted items on the worker thread
154499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // so that the items in the current window can be loaded first
155499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mPriorityLoadingWindowSize;
156499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mPriorityLoadingWindowStart;
157499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private int mPriorityLoadingWindowEnd;
158499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
159499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // determines which way to load items in the current window based on how
160499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // the window shifted last
161499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private boolean mLoadUpwards;
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;
221499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mPriorityLoadingWindowSize = 4;
222499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mPriorityLoadingWindowStart = 0;
223499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mPriorityLoadingWindowEnd = 0;
224499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mLoadUpwards = false;
225499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
226499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // initialize the cache
227499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCacheInfo = new RemoteViewsInfo();
228499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mViewCache = new RemoteViewsIndexInfo[2 * mHalfCacheSize + 1];
229499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            for (int i = 0; i < mViewCache.length; ++i) {
230499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCache[i] = new RemoteViewsIndexInfo();
231499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
232499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final boolean contains(int position) {
235499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // take the modulo of the position
236499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return (mViewCacheStartPosition <= position) && (position < mViewCacheEndPosition);
237499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
239499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final boolean containsAndIsValid(int position) {
240499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (contains(position)) {
241499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViewsIndexInfo indexInfo = mViewCache[getCacheIndex(position)];
242499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (indexInfo.isValid()) {
243499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return true;
244499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
245499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
246499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return false;
247499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
248499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
249499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private final int getCacheIndex(int 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
301499cb9f516062b654952d282f211bee44c31a3c2Winson 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
319499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    // recompose the flipper
320499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    View loadingView = flipper.getChildAt(0);
321499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    loadingView.setVisibility(View.GONE);
322499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    flipper.removeAllViews();
323499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    flipper.addView(loadingView);
324499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    flipper.addView(indexInfo.view.apply(mContext, flipper));
325499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
326499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    // hide the loader view and bring the new view to the front
327499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    flipper.requestLayout();
328499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    flipper.invalidate();
329499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                }
330499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
331499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
332499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
333499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
334499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
335499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
336499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
337499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) {
338499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int indicesToLoadCount = 0;
339499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int[] indicesToLoad = null;
340499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
341499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
342499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                indicesToLoad = new int[mViewCache.length];
343499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                Arrays.fill(indicesToLoad, 0);
344499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
345499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
346499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // return the info if it exists in the window and is loaded
347499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return mViewCache[getCacheIndex(position)];
348499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
349499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
350499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // if necessary update the window and load the new information
351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2;
352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) {
353499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int newStartPosition = position - mHalfCacheSize;
354499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int newEndPosition = position + mHalfCacheSize;
355499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
356499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // prune/add before the current start position
357499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int effectiveStart = Math.max(newStartPosition, 0);
358499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int effectiveEnd = Math.min(newEndPosition, getCount());
359499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
360499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mWorkerQueue.removeMessages(0);
361499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
362499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // invalidate items in the queue
363499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    boolean loadFromBeginning = effectiveStart < mViewCacheStartPosition;
364499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int numLoadFromBeginning = mViewCacheStartPosition - effectiveStart;
365499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    boolean loadFromEnd = effectiveEnd > mViewCacheEndPosition;
366499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart);
367499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd);
368499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    for (int i = newStartPosition; i < newEndPosition; ++i) {
369499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        if (loadFromBeginning && (effectiveStart <= i) && (i < overlapStart)) {
370499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // load new items at the beginning in reverse order
371499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mViewCache[getCacheIndex(i)].invalidate();
372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            indicesToLoad[indicesToLoadCount++] = effectiveStart
373499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    + (numLoadFromBeginning - (i - effectiveStart) - 1);
374499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        } else if (loadFromEnd && (overlapEnd <= i) && (i < effectiveEnd)) {
375499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mViewCache[getCacheIndex(i)].invalidate();
376499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            indicesToLoad[indicesToLoadCount++] = i;
377499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        } else if ((overlapStart <= i) && (i < overlapEnd)) {
378499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // load the stuff in the middle that has not already
379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // been loaded
380499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            if (!mViewCache[getCacheIndex(i)].isValid()) {
381499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                indicesToLoad[indicesToLoadCount++] = i;
382499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
383499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        } else {
384499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // invalidate all other cache indices (outside the effective start/end)
385499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            mViewCache[getCacheIndex(i)].invalidate();
386499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
387499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
388499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
389499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCacheStartPosition = newStartPosition;
390499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCacheEndPosition = newEndPosition;
391499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mPriorityLoadingWindowStart = position;
392499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mPriorityLoadingWindowEnd = position + mPriorityLoadingWindowSize;
393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mLoadUpwards = loadFromBeginning && !loadFromEnd;
394499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                } else if (contains(position)) {
395499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // prioritize items around this position so that they load first
396499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (position < mPriorityLoadingWindowStart || position > mPriorityLoadingWindowEnd) {
397499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mWorkerQueue.removeMessages(0);
398499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
399499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        int index;
400499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        int effectiveStart = Math.max(position - mPriorityLoadingWindowSize, 0);
401499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        int effectiveEnd = 0;
402499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        synchronized (mViewCacheInfo) {
403499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            effectiveEnd = Math.min(position + mPriorityLoadingWindowSize - 1,
404499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    mViewCacheInfo.count - 1);
405499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
406499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
407499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 0; i < mViewCache.length; ++i) {
408499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            if (mLoadUpwards) {
409499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                index = effectiveEnd - i;
410499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            } else {
411499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                index = effectiveStart + i;
412499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
413499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            if (!mViewCache[getCacheIndex(index)].isValid()) {
414499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                indicesToLoad[indicesToLoadCount++] = index;
415499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
416499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
417499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
418499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mPriorityLoadingWindowStart = effectiveStart;
419499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mPriorityLoadingWindowEnd = position + mPriorityLoadingWindowSize;
420499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
421499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
422499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
423499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
424499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // post items to be loaded
425499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            int length = 0;
426499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
427499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                length = mViewCacheInfo.count;
428499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
429499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            for (int i = 0; i < indicesToLoadCount; ++i) {
430499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                final int index = indicesToLoad[i];
431499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (0 <= index && index < length) {
432499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mWorkerQueue.post(new Runnable() {
433499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        @Override
434499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        public void run() {
435499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            updateRemoteViewsInfo(index);
436499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
437499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    });
438499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
439499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
440499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
441499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // return null so that a dummy view can be retrieved
442499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return null;
443499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
444499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
445499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public View getView(int position, View convertView, ViewGroup parent) {
446499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            if (mServiceConnection.isConnected()) {
447499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // create the flipper views if necessary (we have to do this now
448499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // for all the flippers while we have the reference to the parent)
449499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                createInitialLoadingFlipperViews(parent);
450499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
451499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // request the item from the cache (queueing it to load if not
452499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // in the cache already)
453499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                RemoteViewsIndexInfo indexInfo = requestCachedIndexInfo(position);
454499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
455499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // update the flipper appropriately
456499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                synchronized (mViewCache) {
457499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int cacheIndex = getCacheIndex(position);
458499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    FrameLayout flipper = mViewCache[cacheIndex].flipper;
459499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
460499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (indexInfo == null) {
461499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        // hide the item view and show the loading view
462499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.getChildAt(0).setVisibility(View.VISIBLE);
463499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 1; i < flipper.getChildCount(); ++i) {
464499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.getChildAt(i).setVisibility(View.GONE);
465499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
466499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.requestLayout();
467499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.invalidate();
468499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    } else {
469499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        // hide the loading view and show the item view
470499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        for (int i = 0; i < flipper.getChildCount() - 1; ++i) {
471499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.getChildAt(i).setVisibility(View.GONE);
472499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
473499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE);
474499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.requestLayout();
475499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        flipper.invalidate();
476499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
477499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return flipper;
478499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
479499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
480499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return new View(mContext);
481499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
482499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
483499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private void createInitialLoadingFlipperViews(ViewGroup parent) {
484499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // ensure that the cache has the appropriate initial flipper
485499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
486499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (mViewCache[0].flipper == null) {
487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    for (int i = 0; i < mViewCache.length; ++i) {
488499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        FrameLayout flipper = new FrameLayout(mContext);
489499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        if (mUserLoadingView != null) {
490499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // use the user-specified loading view
491499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.addView(mUserLoadingView.apply(mContext, parent));
492499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        } else {
493499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // calculate the original size of the first row for the loader view
494499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            synchronized (mViewCacheInfo) {
495499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                if (mFirstViewHeight < 0) {
496499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    View firstView = mFirstView.apply(mContext, parent);
497499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    firstView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
498499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
499499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                    mFirstViewHeight = firstView.getMeasuredHeight();
500499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                                }
501499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            }
502499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
503499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // construct a new loader and add it to the flipper as the fallback
504499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            // default view
505499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            TextView textView = new TextView(mContext);
506499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setText("Loading...");
507499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setHeight(mFirstViewHeight);
508499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
509499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setTextSize(18.0f);
510499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setTextColor(Color.argb(96, 255, 255, 255));
511499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK);
512499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
513499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                            flipper.addView(textView);
514499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
515499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        mViewCache[i].flipper = flipper;
516499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
517499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
518499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
519499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
520499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
521499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public long getItemId(int position) {
522499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
523499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
524499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    return mViewCache[getCacheIndex(position)].itemId;
525499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
526499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
527499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return 0;
528499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
529499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
530499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getItemViewType(int position) {
531499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // synchronize to ensure that the type id/index map is updated synchronously
532499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
533499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                if (containsAndIsValid(position)) {
534499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    int viewId = mViewCache[getCacheIndex(position)].typeId;
535499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    Map<Integer, Integer> typeMap = mViewCacheInfo.mTypeIdIndexMap;
536499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    // we +1 because the default dummy view get view type 0
537499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    if (typeMap.containsKey(viewId)) {
538499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return typeMap.get(viewId);
539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    } else {
540499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        int newIndex = typeMap.size() + 1;
541499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        typeMap.put(viewId, newIndex);
542499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        return newIndex;
543499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    }
544499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
545499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
546499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            // return the type of the default item
547499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return 0;
548499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
549499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
550499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getCount() {
551499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
552499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.count;
553499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
554499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
555499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
556499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public int getViewTypeCount() {
557499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
558499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.viewTypeCount;
559499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
560499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
561499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
562499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public boolean hasStableIds() {
563499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCacheInfo) {
564499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                return mViewCacheInfo.hasStableIds;
565499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
566499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
567499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
568499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void flushCache() {
569499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            synchronized (mViewCache) {
570499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                // flush the internal cache and invalidate the adapter for future loads
571499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mWorkerQueue.removeMessages(0);
572499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mMainQueue.removeMessages(0);
573499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
574499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                for (int i = 0; i < mViewCache.length; ++i) {
575499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                    mViewCache[i].invalidate();
576499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
577499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
578499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCacheStartPosition = 0;
579499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                mViewCacheEndPosition = -1;
580499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
581499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
582499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
584499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
585499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
586499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
587499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
588499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // initialize the worker thread
589499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
590499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
591499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
592499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerPriorityQueue = new Handler(mWorkerThread.getLooper());
593499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mMainQueue = new Handler(Looper.myLooper());
594499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mMainPriorityQueue = new Handler(Looper.myLooper());
595499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
596499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // initialize the cache and the service connection on startup
597499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mViewCache = new RemoteViewsCache(25);
598499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(callback);
599499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
600499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
601499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
602499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    protected void finalize() throws Throwable {
603499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // remember to unbind from the service when finalizing
604499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        unbindService();
605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
606499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
607499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
608499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
609499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getCount();
610499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
611499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // disallow arbitrary object to be associated with an item for the time being
614499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
615499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
616499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
617499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
618499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
619499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getItemId(position);
620499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
621499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
622499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
623499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
624499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getItemViewType(position);
625499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
626499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
627499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
628499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
629499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getView(position, convertView, parent);
630499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
631499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
632499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
633499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
634499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.getViewTypeCount();
635499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
636499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
637499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
638499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
639499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mViewCache.hasStableIds();
640499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
641499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
642499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
643499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
644499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
645499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
646499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public void notifyDataSetChanged() {
647499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // flush the cache so that we can reload new items from the service
648499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mViewCache.flushCache();
649499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
650499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
651499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
652499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
653499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        // try binding the service (which will start it if it's not already running)
654499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
655499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
656499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
657499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
658499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
659499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
660499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
661499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private void unbindService() {
662499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (mServiceConnection.isConnected()) {
663499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mContext.unbindService(mServiceConnection);
664499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
665499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
666499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
667