RemoteViewsAdapter.java revision 4a9df8dda5c826ae72a2b5370b9d786ef8d0efd0
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 193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.lang.ref.WeakReference; 20591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohenimport java.util.ArrayList; 21499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap; 223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.util.HashSet; 23c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chungimport java.util.LinkedList; 2481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager; 25499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context; 26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent; 27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler; 28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread; 29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder; 30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper; 3181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message; 322625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException; 33fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log; 34335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohenimport android.util.Pair; 35a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater; 36499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View; 37181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec; 3884bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup; 39499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection; 41499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory; 42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** 44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews 45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views. 46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 47499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */ 4881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback { 49fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung private static final String TAG = "RemoteViewsAdapter"; 50499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 514a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // The max number of items in the cache 52b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sDefaultCacheSize = 40; 5381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The delay (in millis) to wait until attempting to unbind from a service after a request. 5481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // This ensures that we don't stay continually bound to the service and that it can be destroyed 5581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // if we need the memory elsewhere in the system. 56b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sUnbindServiceDelay = 5000; 57b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 58b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Default height for the default loading view, in case we cannot get inflate the first view 59b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen private static final int sDefaultLoadingViewHeight = 50; 60b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Type defs for controlling different messages across the main and worker message queues 624a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static final int sDefaultMessageType = 0; 6381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceMessageType = 1; 6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 6581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Context mContext; 6681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Intent mIntent; 6781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final int mAppWidgetId; 68a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung private LayoutInflater mLayoutInflater; 69499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private RemoteViewsAdapterServiceConnection mServiceConnection; 703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteAdapterConnectionCallback> mCallback; 713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private FixedSizeRemoteViewsCache mCache; 72b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowLowerBound; 73b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowUpperBound; 743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // A flag to determine whether we should notify data set changed after we connect 7616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mNotifyDataSetChangedAfterOnServiceConnected = false; 7716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of requested views that are to be notified when the associated RemoteViews are 793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loaded. 803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayoutRefSet mRequestedViews; 81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private HandlerThread mWorkerThread; 83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung // items may be interrupted within the normally processed queues 84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mWorkerQueue; 85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mMainQueue; 86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 87335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data 88335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // structures; 894a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static final HashMap<Pair<Intent.FilterComparison, Integer>, FixedSizeRemoteViewsCache> 90335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCachedRemoteViewsCaches = new HashMap<Pair<Intent.FilterComparison, Integer>, 914a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen FixedSizeRemoteViewsCache>(); 92335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static final HashMap<Pair<Intent.FilterComparison, Integer>, Runnable> 93335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables = new HashMap<Pair<Intent.FilterComparison, Integer>, 94335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Runnable>(); 95335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static HandlerThread sCacheRemovalThread; 96335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static Handler sCacheRemovalQueue; 97335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 98335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We keep the cache around for a duration after onSaveInstanceState for use on re-inflation. 99335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this 100335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // duration, the cache is dropped. 101335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static final int REMOTE_VIEWS_CACHE_DURATION = 5000; 102335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 103335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // Used to indicate to the AdapterView that it can use this Adapter immediately after 104335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // construction (happens when we have a cached FixedSizeRemoteViewsCache). 105335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private boolean mDataReady = false; 106335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An interface for the RemoteAdapter to notify other classes when adapters 109499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * are actually connected to/disconnected from their actual services. 110499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public interface RemoteAdapterConnectionCallback { 11216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung /** 11316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung * @return whether the adapter was set or not. 11416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung */ 11516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean onRemoteAdapterConnected(); 116499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterDisconnected(); 1182148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen 1192148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen /** 1202148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not 1212148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * connected yet. 1222148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen */ 1232148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen public void deferNotifyDataSetChanged(); 124499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 125499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 126499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 127499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * The service connection that gets populated when the RemoteViewsService is 1283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * bound. This must be a static inner class to ensure that no references to the outer 1293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being 1303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * garbage collected, and would cause us to leak activities due to the caching mechanism for 1313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * FrameLayouts in the adapter). 132499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 13381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static class RemoteViewsAdapterServiceConnection extends 13481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung IRemoteViewsAdapterConnection.Stub { 13516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnected; 13616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnecting; 1373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteViewsAdapter> mAdapter; 138499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private IRemoteViewsFactory mRemoteViewsFactory; 139499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { 1413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); 142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 143499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 14416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void bind(Context context, int appWidgetId, Intent intent) { 14516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mIsConnecting) { 14616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 14716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 14816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.bindRemoteViewsService(appWidgetId, intent, asBinder()); 14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = true; 15016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 15116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage()); 15216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 15316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 15416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 15716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 15816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void unbind(Context context, int appWidgetId, Intent intent) { 15916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 16016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 16116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.unbindRemoteViewsService(appWidgetId, intent); 16216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 16316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 16416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage()); 16516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 16616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 16716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 16816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 16916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceConnected(IBinder service) { 171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); 172c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 17316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any deferred unbind messages 1743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 1753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 17616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 17716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up work that we need to do for the callback to run 1783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mWorkerQueue.post(new Runnable() { 179499cb9f516062b654952d282f211bee44c31a3c2Winson Chung @Override 180499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void run() { 18116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) { 18216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Handle queued notifyDataSetChanged() if necessary 18316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.onNotifyDataSetChanged(); 18416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = 1863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mServiceConnection.getRemoteViewsFactory(); 1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 18816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!factory.isCreated()) { 18916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We only call onDataSetChanged() if this is the factory was just 19016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // create in response to this bind 19116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 1932625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 1943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error notifying factory of data set changed in " + 1953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung "onServiceConnected(): " + e.getMessage()); 1963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent anything further from being notified 1983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // (effectively nothing has changed) 1993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 2002625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 2012625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error notifying factory of data set changed in " + 2022625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen "onServiceConnected(): " + e.getMessage()); 203499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Request meta data so that we have up to date data when calling back to 2063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the remote adapter callback 20716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.updateTemporaryMetaData(); 2083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 20916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Notify the host that we've connected 21061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung adapter.mMainQueue.post(new Runnable() { 2113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 2123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 21316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (adapter.mCache) { 21416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mCache.commitTemporaryMetaData(); 21516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 21616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 2173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = 2183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mCallback.get(); 2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterConnected(); 2213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 2243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 22516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 22616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Enqueue unbind message 22716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.enqueueDeferredUnbindServiceMessage(); 22816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = true; 22916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 230499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 231499cb9f516062b654952d282f211bee44c31a3c2Winson Chung }); 232499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 23416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceDisconnected() { 23516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 23616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 2373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsFactory = null; 238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 23916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Clear the main/worker queues 2403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 2413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 242c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 24316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.post(new Runnable() { 24416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 24516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 24616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 24716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.removeMessages(sUnbindServiceMessageType); 2483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 24916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); 25016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (callback != null) { 25116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung callback.onRemoteAdapterDisconnected(); 25216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 25316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 25416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 256499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 25716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized IRemoteViewsFactory getRemoteViewsFactory() { 258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mRemoteViewsFactory; 259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 260499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 26116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized boolean isConnected() { 26216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mIsConnected; 263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when 2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * they are loaded. 269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 270a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class RemoteViewsFrameLayout extends FrameLayout { 2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayout(Context context) { 2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung super(context); 2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 274499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 275499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Updates this RemoteViewsFrameLayout depending on the view that was loaded. 2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded 2783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * successfully. 279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void onRemoteViewsLoaded(RemoteViews view) { 28161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung try { 28261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Remove all the children of this layout first 28361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung removeAllViews(); 28461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung addView(view.apply(getContext(), this)); 28561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } catch (Exception e) { 28661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung Log.e(TAG, "Failed to apply RemoteViews."); 28761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } 2883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 290499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the 2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * adapter that have not yet had their RemoteViews loaded. 2943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayoutRefSet { 2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; 297499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayoutRefSet() { 2993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); 300499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 301499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 302499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 3033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. 304499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 3053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void add(int position, RemoteViewsFrameLayout layout) { 3063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung LinkedList<RemoteViewsFrameLayout> refs; 3083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create the list if necessary 3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = mReferences.get(pos); 3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = new LinkedList<RemoteViewsFrameLayout>(); 3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.put(pos, refs); 315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add the references to the list 3183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.add(layout); 319499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 320499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that 3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * the associated RemoteViews has loaded. 3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 325a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) { 32661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung if (view == null) return; 32761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the references for that position of the newly loaded RemoteViews 3313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); 3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (final RemoteViewsFrameLayout ref : refs) { 3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ref.onRemoteViewsLoaded(view); 3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.clear(); 336499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Remove this set from the original mapping 3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.remove(pos); 339499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 340499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 341499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. 3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void clear() { 3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We currently just clear the references, and leave all the previous layouts returned 3473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // in their default state of the loading view. 3483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.clear(); 349499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with the cache in it's current state. 3543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3554a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static class RemoteViewsMetaData { 3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count; 3573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount; 3583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds; 3593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Used to determine how to construct loading views. If a loading view is not specified 3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by the user, then we try and load the first view, and use its height as the height for 3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the default loading view. 3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mUserLoadingView; 3643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mFirstView; 3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mFirstViewHeight; 3663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A mapping from type id to a set of unique type ids 36816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>(); 3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData() { 3713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung reset(); 372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 373499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 37416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void set(RemoteViewsMetaData d) { 37516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (d) { 37616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung count = d.count; 37716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung viewTypeCount = d.viewTypeCount; 37816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasStableIds = d.hasStableIds; 37916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView); 38016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 38116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 38216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = 0; 38516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by default there is at least one dummy view type 3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung viewTypeCount = 1; 3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung hasStableIds = true; 3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = null; 3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = 0; 39216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTypeIdIndexMap.clear(); 393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 394499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { 3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = loadingView; 3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (firstView != null) { 3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = firstView; 3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = -1; 400499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 401499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 402499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getMappedViewType(int typeId) { 4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mTypeIdIndexMap.containsKey(typeId)) { 4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mTypeIdIndexMap.get(typeId); 4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We +1 because the loading view always has view type id of 0 4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int incrementalTypeId = mTypeIdIndexMap.size() + 1; 4093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap.put(typeId, incrementalTypeId); 4103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return incrementalTypeId; 4116394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4126394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4136394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 414a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public boolean isViewTypeInRange(int typeId) { 415a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int mappedType = getMappedViewType(typeId); 416a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (mappedType >= viewTypeCount) { 417a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return false; 418a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 419a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return true; 420a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 421a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 422a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 4233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayout createLoadingView(int position, View convertView, 424a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen ViewGroup parent, Object lock, LayoutInflater layoutInflater) { 4253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create and return a new FrameLayout, and setup the references for this position 4263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Context context = parent.getContext(); 4273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); 4283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create a new loading view 430a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (lock) { 431b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen boolean customLoadingViewAvailable = false; 432b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mUserLoadingView != null) { 434b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Try to inflate user-specified loading view 435b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 436b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View loadingView = mUserLoadingView.apply(parent.getContext(), parent); 437b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, 438b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(0)); 439b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(loadingView); 440b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen customLoadingViewAvailable = true; 441b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e) { 442b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating custom loading view, using default loading" + 443b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "view instead", e); 444b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 445b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 446b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (!customLoadingViewAvailable) { 4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A default loading view 4483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Use the size of the first row as a guide for the size of the loading view 4493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mFirstViewHeight < 0) { 450b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 451b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View firstView = mFirstView.apply(parent.getContext(), parent); 452b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen firstView.measure( 453b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 454b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 455b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstViewHeight = firstView.getMeasuredHeight(); 456b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstView = null; 457b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e) { 458a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen float density = context.getResources().getDisplayMetrics().density; 459b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstViewHeight = (int) 460b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Math.round(sDefaultLoadingViewHeight * density); 461b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstView = null; 462b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating first RemoteViews" + e); 463b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Compose the loading view text 467a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen TextView loadingTextView = (TextView) layoutInflater.inflate( 468fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen com.android.internal.R.layout.remote_views_adapter_default_loading_view, 469fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen layout, false); 470a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setHeight(mFirstViewHeight); 471a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setTag(new Integer(0)); 472a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung 473a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung layout.addView(loadingTextView); 474fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung } 4756394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4766394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return layout; 4786394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4806394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 4823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with a single item in the cache. 4833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 4844a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static class RemoteViewsIndexMetaData { 4853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId; 4863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId; 487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 488591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen public RemoteViewsIndexMetaData(RemoteViews v, long itemId) { 489591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen set(v, itemId); 490499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 491499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 492591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen public void set(RemoteViews v, long id) { 4933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = id; 494a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (v != null) { 4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = v.getLayoutId(); 496a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 4973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = 0; 498a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 5003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 501499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 5033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * 5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 5054a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static class FixedSizeRemoteViewsCache { 5063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final String TAG = "FixedSizeRemoteViewsCache"; 5073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The meta data related to all the RemoteViews, ie. count, is stable, etc. 5094c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // The meta data objects are made final so that they can be locked on independently 5104c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in 5114c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // the order mTemporaryMetaData followed by mMetaData. 5124c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mMetaData; 5134c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mTemporaryMetaData; 5143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be 5163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // greater than or equal to the set of RemoteViews. 5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: The reason that we keep this separate from the RemoteViews cache below is that this 5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we still need to be able to access the mapping of position to meta data, without keeping 5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. 5203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // memory and size, but this metadata cache will retain information until the data at the 5213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). 5223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; 5233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses 5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // too much memory. 5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViews> mIndexRemoteViews; 5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices that have been explicitly requested by the collection view 5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mRequestedIndices; 5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 531b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // We keep a reference of the last requested index to determine which item to prune the 532b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest items from when we hit the memory limit 533b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private int mLastRequestedIndex; 534b90a91c633e99d4559095184af27d1416541d3c0Winson Chung 5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices to load, including those explicitly requested, as well as those 5363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // determined by the preloading algorithm to be prefetched 5373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mLoadIndices; 5383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The lower and upper bounds of the preloaded range 5403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadLowerBound; 5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadUpperBound; 5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The bounds of this fixed cache, we will try and fill as many items into the cache up to 5443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the maxCount number of items, or the maxSize memory usage. 5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The maxCountSlack is used to determine if a new position in the cache to be loaded is 5463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // sufficiently ouside the old set, prompting a shifting of the "window" of items to be 5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloaded. 5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCount; 5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCountSlack; 5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final float sMaxCountSlackPercent = 0.75f; 551b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024; 5523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public FixedSizeRemoteViewsCache(int maxCacheSize) { 5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCount = maxCacheSize; 5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); 5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMetaData = new RemoteViewsMetaData(); 55916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTemporaryMetaData = new RemoteViewsMetaData(); 5603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 5613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices = new HashSet<Integer>(); 563b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices = new HashSet<Integer>(); 5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 566499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 567591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen public void insert(int position, RemoteViews v, long itemId, 568591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> visibleWindow) { 5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the count 5703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.size() >= mMaxCount) { 571591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mIndexRemoteViews.remove(getFarthestPositionFrom(position, visibleWindow)); 572499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 573499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the available memory size constraints 575b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position; 576b90a91c633e99d4559095184af27d1416541d3c0Winson Chung while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) { 5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: This is currently the most naive mechanism for deciding what to prune when 5783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we hit the memory limit. In the future, we may want to calculate which index to 5793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // remove based on both its position as well as it's current memory usage, as well 5803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // as whether it was directly requested vs. whether it was preloaded by our caching 5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // mechanism. 582591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition, visibleWindow)); 583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Update the metadata cache 5863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 5873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); 588591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen metaData.set(v, itemId); 5893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 590591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId)); 591499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.put(position, v); 5933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 594499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData getMetaData() { 5963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mMetaData; 5973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 59816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public RemoteViewsMetaData getTemporaryMetaData() { 59916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mTemporaryMetaData; 60016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 6013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViews getRemoteViewsAt(int position) { 6023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.containsKey(position)) { 6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.get(position); 6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 606499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData getMetaDataAt(int position) { 6083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 6093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.get(position); 610499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return null; 6123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 61416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void commitTemporaryMetaData() { 61516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mTemporaryMetaData) { 61616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mMetaData) { 61716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMetaData.set(mTemporaryMetaData); 61816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 61916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 62016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 62116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getRemoteViewsBitmapMemoryUsage() { 6233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Calculate the memory usage of all the RemoteViews bitmaps being cached 6243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mem = 0; 6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (Integer i : mIndexRemoteViews.keySet()) { 6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews v = mIndexRemoteViews.get(i); 627aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung if (v != null) { 6285d20064651b9947a4573c9a0eefec90f66eb1b59Adam Cohen mem += v.estimateMemoryUsage(); 629aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung } 630499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mem; 632499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 633591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen 634591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen private int getFarthestPositionFrom(int pos, ArrayList<Integer> visibleWindow) { 6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Find the index farthest away and remove that 6363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDist = 0; 6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDistIndex = -1; 638591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen int maxDistNotVisible = 0; 639591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen int maxDistIndexNotVisible = -1; 6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i : mIndexRemoteViews.keySet()) { 6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int dist = Math.abs(i-pos); 642591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen if (dist > maxDistNotVisible && !visibleWindow.contains(i)) { 643591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // maxDistNotVisible/maxDistIndexNotVisible will store the index of the 644591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // farthest non-visible position 645591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen maxDistIndexNotVisible = i; 646591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen maxDistNotVisible = dist; 647b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 64835fbe2a5923d45ebcdfb3ad74efd1089a05e8737Adam Cohen if (dist >= maxDist) { 649b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDist/maxDistIndex will store the index of the farthest position 650591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // regardless of whether it is visible or not 6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDistIndex = i; 6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDist = dist; 653c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 654c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 655591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen if (maxDistIndexNotVisible > -1) { 656591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen return maxDistIndexNotVisible; 657b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return maxDistIndex; 659c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 660c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queueRequestedPositionToLoad(int position) { 662b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = position; 6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.add(position); 6653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(position); 666499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 667499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 66816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) { 6693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Check if we need to preload any items 6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { 6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; 6723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (Math.abs(position - center) < mMaxCountSlack) { 67316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return false; 674499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 675499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 676499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = 0; 6783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mMetaData) { 6793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = mMetaData.count; 680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the requested indices 6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.addAll(mRequestedIndices); 6863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the preload indices 6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int halfMaxCount = mMaxCount / 2; 6893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = position - halfMaxCount; 6903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = position + halfMaxCount; 6913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveLowerBound = Math.max(0, mPreloadLowerBound); 6923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); 6933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { 6943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(i); 6953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 696499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // But remove all the indices that have already been loaded and are cached 6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.removeAll(mIndexRemoteViews.keySet()); 699499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 70016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return true; 701499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 702b90a91c633e99d4559095184af27d1416541d3c0Winson Chung /** Returns the next index to load, and whether that index was directly requested or not */ 703b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public int[] getNextIndexToLoad() { 7043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We try and prioritize items that have been requested directly, instead 7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // of items that are loaded as a result of the caching mechanism 7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Prioritize requested indices to be loaded first 7083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mRequestedIndices.isEmpty()) { 7093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mRequestedIndices.iterator().next(); 7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.remove(i); 7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 712b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 1}; 7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 714499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, preload other indices as necessary 7163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mLoadIndices.isEmpty()) { 7173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mLoadIndices.iterator().next(); 7183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 719b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 0}; 7203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 721499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 722b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{-1, 0}; 723c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 7243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 725c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 7263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsRemoteViewAt(int position) { 7273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.containsKey(position); 7283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsMetaDataAt(int position) { 7303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.containsKey(position); 7313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 732499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 73461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Note: We do not try and reset the meta data, since that information is still used by 73561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // collection views to validate it's own contents (and will be re-requested if the data 73661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // is invalidated through the notifyDataSetChanged() flow). 73761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 7383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 7393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 740b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 7413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.clear(); 7423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.clear(); 7433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.clear(); 7453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 746499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 747499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 748499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 749499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 750499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { 751499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mContext = context; 752499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mIntent = intent; 75381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); 754a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung mLayoutInflater = LayoutInflater.from(context); 7559b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung if (mIntent == null) { 7569b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung throw new IllegalArgumentException("Non-null Intent must be specified."); 7579b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews = new RemoteViewsFrameLayoutRefSet(); 759499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 76081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Strip the previously injected app widget id from service intent 76181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { 76281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); 76381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 76481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 76581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the worker thread 766499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); 767499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread.start(); 768499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerQueue = new Handler(mWorkerThread.getLooper()); 76981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue = new Handler(Looper.myLooper(), this); 770499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 771335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCacheRemovalThread == null) { 772335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner"); 773335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalThread.start(); 774335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper()); 775335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 776335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 77781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the cache and the service connection on startup 7783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); 7793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mServiceConnection = new RemoteViewsAdapterServiceConnection(this); 780335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 781335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, Integer> 782335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen (new Intent.FilterComparison(mIntent), mAppWidgetId); 783335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 784335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(sCachedRemoteViewsCaches) { 785335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCachedRemoteViewsCaches.containsKey(key)) { 7864a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen mCache = sCachedRemoteViewsCaches.get(key); 7874a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen synchronized (mCache.mMetaData) { 7884a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if (mCache.mMetaData.count > 0) { 7894a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // As a precautionary measure, we verify that the meta data indicates a 7904a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // non-zero count before declaring that data is ready. 7914a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen mDataReady = true; 7924a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 7934a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 794335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } else { 795335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); 7964a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 7974a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if (!mDataReady) { 798335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen requestBindService(); 799335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 800335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 801499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 802499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 803fc442bdea14289656ef1f537103578eb71faf473Jeff Brown @Override 804fc442bdea14289656ef1f537103578eb71faf473Jeff Brown protected void finalize() throws Throwable { 805fc442bdea14289656ef1f537103578eb71faf473Jeff Brown try { 806fc442bdea14289656ef1f537103578eb71faf473Jeff Brown if (mWorkerThread != null) { 807fc442bdea14289656ef1f537103578eb71faf473Jeff Brown mWorkerThread.quit(); 808fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 809fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } finally { 810fc442bdea14289656ef1f537103578eb71faf473Jeff Brown super.finalize(); 811fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 812fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 813fc442bdea14289656ef1f537103578eb71faf473Jeff Brown 814335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public boolean isDataReady() { 815335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen return mDataReady; 816335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 817335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 818335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void saveRemoteViewsCache() { 819335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen final Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, 820335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Integer> (new Intent.FilterComparison(mIntent), mAppWidgetId); 821335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 822335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(sCachedRemoteViewsCaches) { 823335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // If we already have a remove runnable posted for this key, remove it. 824335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { 825335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key)); 826335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.remove(key); 827335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 828335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 8294a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen int metaDataCount = 0; 8304a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen int numRemoteViewsCached = 0; 8314a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen synchronized (mCache.mMetaData) { 8324a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen metaDataCount = mCache.mMetaData.count; 833335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 8344a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen synchronized (mCache) { 8354a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen numRemoteViewsCached = mCache.mIndexRemoteViews.size(); 8364a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 8374a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if (metaDataCount > 0 && numRemoteViewsCached > 0) { 8384a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen sCachedRemoteViewsCaches.put(key, mCache); 8394a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 8404a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen 841335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Runnable r = new Runnable() { 842335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 843335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void run() { 844335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized (sCachedRemoteViewsCaches) { 845335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCachedRemoteViewsCaches.containsKey(key)) { 846335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCachedRemoteViewsCaches.remove(key); 847335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 848335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { 849335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.remove(key); 850335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 851335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 852335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 853335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen }; 854335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.put(key, r); 855335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION); 856335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 857335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 858335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 8593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void loadNextIndexInBackground() { 8603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mWorkerQueue.post(new Runnable() { 8613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 8623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 86316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mServiceConnection.isConnected()) { 86416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Get the next index to load 86516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int position = -1; 86616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 867b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int[] res = mCache.getNextIndexToLoad(); 868b90a91c633e99d4559095184af27d1416541d3c0Winson Chung position = res[0]; 86916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 87016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (position > -1) { 87116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item, and notify any existing RemoteViewsFrameLayouts 872591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen updateRemoteViews(position, true); 8733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 87416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up for the next one to load 87516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 87616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 87716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // No more items to load, so queue unbind 87816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 87916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 8803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 8833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 88516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void processException(String method, Exception e) { 88616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage()); 8873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 88816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we encounter a crash when updating, we should reset the metadata & cache and trigger 88916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // a notifyDataSetChanged to update the widget accordingly 89016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 89116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 89216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung metaData.reset(); 89316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 89416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 89516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 89616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 89716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 89816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 89916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 90016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 9013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 90216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 90316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 90416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 90516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void updateTemporaryMetaData() { 90616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 90716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 90816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 90916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // get the properties/first view (so that we can use it to 91016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // measure our dummy views) 91116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasStableIds = factory.hasStableIds(); 91216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int viewTypeCount = factory.getViewTypeCount(); 91316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int count = factory.getCount(); 91416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews loadingView = factory.getLoadingView(); 91516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews firstView = null; 91616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if ((count > 0) && (loadingView == null)) { 91716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung firstView = factory.getViewAt(0); 91816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 91916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData(); 92016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (tmpMetaData) { 92116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.hasStableIds = hasStableIds; 92216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We +1 because the base view type is the loading view 92316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.viewTypeCount = viewTypeCount + 1; 92416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.count = count; 92516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.setLoadingViewTemplates(loadingView, firstView); 92616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 9272625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch(RemoteException e) { 92816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung processException("updateMetaData", e); 929fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen } catch(RuntimeException e) { 930fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen processException("updateMetaData", e); 9313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 934591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen private void updateRemoteViews(final int position, boolean notifyWhenLoaded) { 93516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 93616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 93716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item information from the remote service 93816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews remoteViews = null; 93916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung long itemId = 0; 94016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 94116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung remoteViews = factory.getViewAt(position); 94216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung itemId = factory.getItemId(position); 9432625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 94416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 94516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 94616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent additional work in re-centering the view cache, and 94716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // swapping from the loading view 94816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 9492625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 9502625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 9512625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 95216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 9533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 95416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (remoteViews == null) { 95516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If a null view was returned, we break early to prevent it from getting 95616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // into our cache and causing problems later. The effect is that the child at this 95716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // position will remain as a loading view until it is updated. 95816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + 95916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung "returned from RemoteViewsFactory."); 96016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 96116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 962a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 963a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int layoutId = remoteViews.getLayoutId(); 964a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen RemoteViewsMetaData metaData = mCache.getMetaData(); 965a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen boolean viewTypeInRange; 966591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen int cacheCount; 967a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (metaData) { 968a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen viewTypeInRange = metaData.isViewTypeInRange(layoutId); 969591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen cacheCount = mCache.mMetaData.count; 970a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 97116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 972a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (viewTypeInRange) { 973591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> visibleWindow = getVisibleWindow(mVisibleWindowLowerBound, 974591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mVisibleWindowUpperBound, cacheCount); 975a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Cache the RemoteViews we loaded 976591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mCache.insert(position, remoteViews, itemId, visibleWindow); 977a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 978a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Notify all the views that we have previously returned for this index that 979a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // there is new data for it. 980a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen final RemoteViews rv = remoteViews; 981a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (notifyWhenLoaded) { 982a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mMainQueue.post(new Runnable() { 983a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen @Override 984a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void run() { 985a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mRequestedViews.notifyOnRemoteViewsLoaded(position, rv); 986a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 987a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen }); 988a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 989a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 990a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // We need to log an error here, as the the view type count specified by the 991a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // factory is less than the number of view types returned. We don't return this 992a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // view to the AdapterView, as this will cause an exception in the hosting process, 993a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // which contains the associated AdapterView. 994a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " + 995a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen " indicated by getViewTypeCount() "); 996b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 9973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 998499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 999499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 10009b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung public Intent getRemoteViewsServiceIntent() { 10019b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung return mIntent; 10029b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 10039b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 1004499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getCount() { 10053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 10063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 10073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.count; 10083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1009499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1010499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1011499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public Object getItem(int position) { 10123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Disallow arbitrary object to be associated with an item for the time being 1013499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 1014499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1015499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1016499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public long getItemId(int position) { 10173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 10183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 10193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mCache.getMetaDataAt(position).itemId; 10203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 10223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1023499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1024499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1025499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getItemViewType(int position) { 10263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = 0; 10273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 10283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 10293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = mCache.getMetaDataAt(position).typeId; 10303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 10313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 10323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 10353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 10363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 10373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.getMappedViewType(typeId); 10383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 10413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 10423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Returns the item type id for the specified convert view. Returns -1 if the convert view 10433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * is invalid. 10443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 10453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getConvertViewTypeId(View convertView) { 10463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = -1; 1047a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (convertView != null) { 1048a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId); 1049a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (tag != null) { 1050a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen typeId = (Integer) tag; 1051a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen } 10523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return typeId; 1054499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1055499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1056b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen /** 1057b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * This method allows an AdapterView using this Adapter to provide information about which 1058b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * views are currently being displayed. This allows for certain optimizations and preloading 1059b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * which wouldn't otherwise be possible. 1060b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen */ 1061b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen public void setVisibleRangeHint(int lowerBound, int upperBound) { 1062b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowLowerBound = lowerBound; 1063b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowUpperBound = upperBound; 1064b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1065b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 1066499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public View getView(int position, View convertView, ViewGroup parent) { 106716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // "Request" an index so that we can queue it for loading, initiate subsequent 106816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // preloading, etc. 106916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 107016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isInCache = mCache.containsRemoteViewAt(position); 107116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isConnected = mServiceConnection.isConnected(); 107216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasNewItems = false; 107316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 10747ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung if (!isInCache && !isConnected) { 107516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Requesting bind service will trigger a super.notifyDataSetChanged(), which will 107616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // in turn trigger another request to getView() 107716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 107816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 10793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up other indices to be preloaded based on this position 108016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position); 108116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 108216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 108316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (isInCache) { 10843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View convertViewChild = null; 10853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int convertViewTypeId = 0; 1086181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen RemoteViewsFrameLayout layout = null; 1087181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen 1088181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (convertView instanceof RemoteViewsFrameLayout) { 1089181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen layout = (RemoteViewsFrameLayout) convertView; 10903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewChild = layout.getChildAt(0); 10913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewTypeId = getConvertViewTypeId(convertViewChild); 10923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 10943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Second, we try and retrieve the RemoteViews from the cache, returning a loading 10953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // view and queueing it to be loaded if it has not already been loaded. 109616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Context context = parent.getContext(); 109716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews rv = mCache.getRemoteViewsAt(position); 1098aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position); 1099aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen int typeId = indexMetaData.typeId; 110016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1101b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 1102b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Reuse the convert view where possible 1103b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (layout != null) { 1104b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (convertViewTypeId == typeId) { 1105b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen rv.reapply(context, convertViewChild); 1106b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1107b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1108b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.removeAllViews(); 1109b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } else { 1110b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout = new RemoteViewsFrameLayout(context); 11113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1113b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Otherwise, create a new view to be returned 1114b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View newView = rv.apply(context, parent); 1115b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen newView.setTagInternal(com.android.internal.R.id.rowTypeId, 1116b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(typeId)); 1117b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(newView); 1118b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1119b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1120b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e){ 1121b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // We have to make sure that we successfully inflated the RemoteViews, if not 1122b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // we return the loading view instead. 1123b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" + 1124b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "loading view instead" + e); 1125b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1126b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen RemoteViewsFrameLayout loadingView = null; 1127b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen final RemoteViewsMetaData metaData = mCache.getMetaData(); 1128b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen synchronized (metaData) { 1129a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1130a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache, mLayoutInflater); 1131b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1132b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return loadingView; 1133b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } finally { 1134b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (hasNewItems) loadNextIndexInBackground(); 1135b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 113616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 113716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If the cache does not have the RemoteViews at this position, then create a 113816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // loading view and queue the actual position to be loaded in the background 113916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViewsFrameLayout loadingView = null; 114016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 114116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 1142a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1143a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache, mLayoutInflater); 11443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 114516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 114616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mRequestedViews.add(position, loadingView); 114716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.queueRequestedPositionToLoad(position); 114816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 114916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 115016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return loadingView; 11513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1154499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1155499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getViewTypeCount() { 11563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 11573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 11583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.viewTypeCount; 11593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1160499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1161499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1162499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean hasStableIds() { 11633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 11643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 11653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.hasStableIds; 11663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1167499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1168499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isEmpty() { 1170499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return getCount() <= 0; 1171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1172499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 117316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void onNotifyDataSetChanged() { 117416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Complete the actual notifyDataSetChanged() call initiated earlier 117516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 117616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 117716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 11782625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 117916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 118016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 118116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent from further being notified (since nothing has 118216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // changed) 118316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 11842625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 11852625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 11862625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 118716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 118816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 118916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Flush the cache so that we can reload new items from the service 119016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 119116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 119216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 119316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 119416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Re-request the new metadata (only after the notification to the factory) 119516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung updateTemporaryMetaData(); 11964c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen int newCount; 1197591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> visibleWindow; 11984c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen synchronized(mCache.getTemporaryMetaData()) { 11994c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen newCount = mCache.getTemporaryMetaData().count; 1200591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen visibleWindow = getVisibleWindow(mVisibleWindowLowerBound, 1201591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mVisibleWindowUpperBound, newCount); 12024c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 120316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1204b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // Pre-load (our best guess of) the views which are currently visible in the AdapterView. 1205b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // This mitigates flashing and flickering of loading views when a widget notifies that 1206b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // its data has changed. 1207591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i: visibleWindow) { 12084c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // Because temporary meta data is only ever modified from this thread (ie. 12094c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // mWorkerThread), it is safe to assume that count is a valid representation. 12104c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen if (i < newCount) { 1211591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen updateRemoteViews(i, false); 12124c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 1213b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1214b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 121516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Propagate the notification back to the base adapter 121616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 12176364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 12186364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 12196364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung synchronized (mCache) { 122016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.commitTemporaryMetaData(); 12216364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 122261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 122316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 122416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 12253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 12263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 12276364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 122816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Reset the notify flagflag 122916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = false; 123016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 123116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1232591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen private ArrayList<Integer> getVisibleWindow(int lower, int upper, int count) { 1233591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> window = new ArrayList<Integer>(); 12344a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen 12354a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // In the case that the window is invalid or uninitialized, return an empty window. 12364a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) { 12374a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen return window; 12384a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 12394a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen 1240591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen if (lower <= upper) { 1241591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i = lower; i <= upper; i++){ 1242591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen window.add(i); 1243591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1244591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } else { 1245591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // If the upper bound is less than the lower bound it means that the visible window 1246591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // wraps around. 1247591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i = lower; i < count; i++) { 1248591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen window.add(i); 1249591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1250591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i = 0; i <= upper; i++) { 1251591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen window.add(i); 1252591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1253591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1254591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen return window; 1255591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1256591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen 125716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void notifyDataSetChanged() { 125816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 125916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 126016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 126116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we are not connected, queue up the notifyDataSetChanged to be handled when we do 126216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // connect 126316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mServiceConnection.isConnected()) { 126416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mNotifyDataSetChangedAfterOnServiceConnected) { 126516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 126616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 126716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 126816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = true; 126916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 127016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 127116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 127216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 127316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mWorkerQueue.post(new Runnable() { 127416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 127516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 127616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung onNotifyDataSetChanged(); 127716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 127816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 12793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 12803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1281fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen void superNotifyDataSetChanged() { 1282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung super.notifyDataSetChanged(); 1283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 128581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung @Override 128681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public boolean handleMessage(Message msg) { 128781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung boolean result = false; 128881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung switch (msg.what) { 128981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung case sUnbindServiceMessageType: 129081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (mServiceConnection.isConnected()) { 129116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.unbind(mContext, mAppWidgetId, mIntent); 129281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 129381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung result = true; 129481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 129581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung default: 129681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 129781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 129881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung return result; 129981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 130081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 130181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private void enqueueDeferredUnbindServiceMessage() { 130281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Remove any existing deferred-unbind messages 130381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 130481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay); 130581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 130681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 1307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean requestBindService() { 130881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Try binding the service (which will start it if it's not already running) 1309499cb9f516062b654952d282f211bee44c31a3c2Winson Chung if (!mServiceConnection.isConnected()) { 131016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.bind(mContext, mAppWidgetId, mIntent); 1311499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1312499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 131316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any existing deferred-unbind messages 131416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 1315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mServiceConnection.isConnected(); 1316499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1317499cb9f516062b654952d282f211bee44c31a3c2Winson Chung} 1318