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