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