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; 24ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani 253e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Millerimport android.Manifest; 2681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager; 27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context; 28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent; 293e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Millerimport android.content.pm.PackageManager; 30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler; 31499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread; 32499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder; 33499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper; 3481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message; 35c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasaniimport android.os.Process; 362625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException; 37c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasaniimport android.os.UserHandle; 38fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log; 393e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Millerimport android.util.Slog; 40a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater; 41499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View; 42181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec; 4384bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup; 44a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohenimport android.widget.RemoteViews.OnClickHandler; 45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection; 47499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory; 48c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasaniimport com.android.internal.widget.LockPatternUtils; 49499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 50499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** 51499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews 52499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views. 53499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 54499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */ 5581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback { 563e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller private static final String MULTI_USER_PERM = Manifest.permission.INTERACT_ACROSS_USERS_FULL; 573e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller 58fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung private static final String TAG = "RemoteViewsAdapter"; 59499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 603e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller // The max number of items in the cache 61b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sDefaultCacheSize = 40; 6281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The delay (in millis) to wait until attempting to unbind from a service after a request. 6381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // This ensures that we don't stay continually bound to the service and that it can be destroyed 6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // if we need the memory elsewhere in the system. 65b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sUnbindServiceDelay = 5000; 66b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 67b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Default height for the default loading view, in case we cannot get inflate the first view 68b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen private static final int sDefaultLoadingViewHeight = 50; 69b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 7081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Type defs for controlling different messages across the main and worker message queues 713e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller private static final int sDefaultMessageType = 0; 7281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceMessageType = 1; 7381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 7481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Context mContext; 7581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Intent mIntent; 7681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final int mAppWidgetId; 77a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung private LayoutInflater mLayoutInflater; 78499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private RemoteViewsAdapterServiceConnection mServiceConnection; 793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteAdapterConnectionCallback> mCallback; 80a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen private OnClickHandler mRemoteViewsOnClickHandler; 813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private FixedSizeRemoteViewsCache mCache; 82b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowLowerBound; 83b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowUpperBound; 843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // A flag to determine whether we should notify data set changed after we connect 8616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mNotifyDataSetChangedAfterOnServiceConnected = false; 8716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of requested views that are to be notified when the associated RemoteViews are 893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loaded. 903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayoutRefSet mRequestedViews; 91499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 92499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private HandlerThread mWorkerThread; 93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung // items may be interrupted within the normally processed queues 94499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mWorkerQueue; 95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mMainQueue; 96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 97335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data 983e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller // structures; 99ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani private static final HashMap<RemoteViewsCacheKey, 100ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani FixedSizeRemoteViewsCache> sCachedRemoteViewsCaches 101ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani = new HashMap<RemoteViewsCacheKey, 1024a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen FixedSizeRemoteViewsCache>(); 103ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani private static final HashMap<RemoteViewsCacheKey, Runnable> 104ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani sRemoteViewsCacheRemoveRunnables 105ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani = new HashMap<RemoteViewsCacheKey, Runnable>(); 106ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani 107335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static HandlerThread sCacheRemovalThread; 108335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static Handler sCacheRemovalQueue; 109335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 110335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We keep the cache around for a duration after onSaveInstanceState for use on re-inflation. 111335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this 112335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // duration, the cache is dropped. 113335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static final int REMOTE_VIEWS_CACHE_DURATION = 5000; 114335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 115335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // Used to indicate to the AdapterView that it can use this Adapter immediately after 116335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // construction (happens when we have a cached FixedSizeRemoteViewsCache). 117335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private boolean mDataReady = false; 118335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 119c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani int mUserId; 120c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani 121499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 122499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An interface for the RemoteAdapter to notify other classes when adapters 123499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * are actually connected to/disconnected from their actual services. 124499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 125499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public interface RemoteAdapterConnectionCallback { 12616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung /** 12716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung * @return whether the adapter was set or not. 12816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung */ 12916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean onRemoteAdapterConnected(); 130499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 131499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterDisconnected(); 1322148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen 1332148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen /** 1342148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not 1352148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * connected yet. 1362148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen */ 1372148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen public void deferNotifyDataSetChanged(); 138499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 139499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 140499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 141499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * The service connection that gets populated when the RemoteViewsService is 1423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * bound. This must be a static inner class to ensure that no references to the outer 1433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being 1443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * garbage collected, and would cause us to leak activities due to the caching mechanism for 1453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * FrameLayouts in the adapter). 146499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 14781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static class RemoteViewsAdapterServiceConnection extends 14881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung IRemoteViewsAdapterConnection.Stub { 14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnected; 15016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnecting; 1513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteViewsAdapter> mAdapter; 152499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private IRemoteViewsFactory mRemoteViewsFactory; 153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { 1553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); 156499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 157499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 15816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void bind(Context context, int appWidgetId, Intent intent) { 15916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mIsConnecting) { 16016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 161c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani RemoteViewsAdapter adapter; 16216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 1633e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller if ((adapter = mAdapter.get()) != null) { 1643e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller checkInteractAcrossUsersPermission(context, adapter.mUserId); 165c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(), 166c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani new UserHandle(adapter.mUserId)); 167c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani } else { 1683e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller Slog.w(TAG, "bind: adapter was null"); 169c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani } 17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = true; 17116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 17216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage()); 17316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 17416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 17516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 17616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 17716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 17816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 17916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void unbind(Context context, int appWidgetId, Intent intent) { 18016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 181c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani RemoteViewsAdapter adapter; 18216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 1833e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller if ((adapter = mAdapter.get()) != null) { 1843e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller checkInteractAcrossUsersPermission(context, adapter.mUserId); 185c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani mgr.unbindRemoteViewsService(appWidgetId, intent, 186c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani new UserHandle(adapter.mUserId)); 187c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani } else { 1883e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller Slog.w(TAG, "unbind: adapter was null"); 189c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani } 19016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 19116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage()); 19316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 19416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 19516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 19616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 19716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 19816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceConnected(IBinder service) { 199499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); 200c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 20116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any deferred unbind messages 2023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 2033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 20416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 20516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up work that we need to do for the callback to run 2063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mWorkerQueue.post(new Runnable() { 207499cb9f516062b654952d282f211bee44c31a3c2Winson Chung @Override 208499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void run() { 20916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) { 21016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Handle queued notifyDataSetChanged() if necessary 21116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.onNotifyDataSetChanged(); 21216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 2133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = 2143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mServiceConnection.getRemoteViewsFactory(); 2153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 21616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!factory.isCreated()) { 21716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We only call onDataSetChanged() if this is the factory was just 21816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // create in response to this bind 21916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 22016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 2212625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 2223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error notifying factory of data set changed in " + 2233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung "onServiceConnected(): " + e.getMessage()); 2243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent anything further from being notified 2263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // (effectively nothing has changed) 2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 2282625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 2292625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error notifying factory of data set changed in " + 2302625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen "onServiceConnected(): " + e.getMessage()); 231499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Request meta data so that we have up to date data when calling back to 2343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the remote adapter callback 23516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.updateTemporaryMetaData(); 2363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 23716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Notify the host that we've connected 23861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung adapter.mMainQueue.post(new Runnable() { 2393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 2403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 24116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (adapter.mCache) { 24216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mCache.commitTemporaryMetaData(); 24316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 24416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 2453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = 2463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mCallback.get(); 2473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 2483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterConnected(); 2493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 2523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 25316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 25416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Enqueue unbind message 25516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.enqueueDeferredUnbindServiceMessage(); 25616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = true; 25716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung }); 260499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 261499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 26216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceDisconnected() { 26316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 26416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 2653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsFactory = null; 266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 26716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Clear the main/worker queues 2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 2693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 2703e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller 27116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.post(new Runnable() { 27216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 27316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 27416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 27516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.removeMessages(sUnbindServiceMessageType); 2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 27716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); 27816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (callback != null) { 27916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung callback.onRemoteAdapterDisconnected(); 28016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 28116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 28216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 28516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized IRemoteViewsFactory getRemoteViewsFactory() { 286499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mRemoteViewsFactory; 287499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 288499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 28916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized boolean isConnected() { 29016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mIsConnected; 291499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 292499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 293499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 294499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when 2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * they are loaded. 297499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 298a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class RemoteViewsFrameLayout extends FrameLayout { 2993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayout(Context context) { 3003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung super(context); 3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 302499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 303499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 3043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Updates this RemoteViewsFrameLayout depending on the view that was loaded. 3053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded 3063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * successfully. 307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 308a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler) { 30961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung try { 31061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Remove all the children of this layout first 31161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung removeAllViews(); 312a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen addView(view.apply(getContext(), this, handler)); 31361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } catch (Exception e) { 31461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung Log.e(TAG, "Failed to apply RemoteViews."); 31561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } 3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 318499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the 3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * adapter that have not yet had their RemoteViews loaded. 3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayoutRefSet { 3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; 325ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen private HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>> 326ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList; 327499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayoutRefSet() { 3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); 330ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList = 331ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen new HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>(); 332499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 333499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 334499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. 336499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void add(int position, RemoteViewsFrameLayout layout) { 3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung LinkedList<RemoteViewsFrameLayout> refs; 3403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create the list if necessary 3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = mReferences.get(pos); 3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = new LinkedList<RemoteViewsFrameLayout>(); 3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.put(pos, refs); 347499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 348ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList.put(layout, refs); 3493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add the references to the list 3513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.add(layout); 352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 353499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that 3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * the associated RemoteViews has loaded. 3573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 358a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) { 35961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung if (view == null) return; 36061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the references for that position of the newly loaded RemoteViews 3643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); 3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (final RemoteViewsFrameLayout ref : refs) { 366a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler); 367ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen if (mViewToLinkedList.containsKey(ref)) { 368ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList.remove(ref); 369ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen } 3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.clear(); 3723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Remove this set from the original mapping 3733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.remove(pos); 374499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 375499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 376499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 378ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen * We need to remove views from this set if they have been recycled by the AdapterView. 379ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen */ 380ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen public void removeView(RemoteViewsFrameLayout rvfl) { 381ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen if (mViewToLinkedList.containsKey(rvfl)) { 382ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList.get(rvfl).remove(rvfl); 383ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList.remove(rvfl); 384ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen } 385ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen } 386ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen 387ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen /** 3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. 3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void clear() { 3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We currently just clear the references, and leave all the previous layouts returned 3923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // in their default state of the loading view. 3933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.clear(); 394ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mViewToLinkedList.clear(); 395499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 397499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with the cache in it's current state. 4003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 4014a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static class RemoteViewsMetaData { 4023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count; 4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount; 4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds; 4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Used to determine how to construct loading views. If a loading view is not specified 4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by the user, then we try and load the first view, and use its height as the height for 4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the default loading view. 4093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mUserLoadingView; 4103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mFirstView; 4113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mFirstViewHeight; 4123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A mapping from type id to a set of unique type ids 41416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>(); 4153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData() { 4173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung reset(); 418499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 419499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 42016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void set(RemoteViewsMetaData d) { 42116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (d) { 42216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung count = d.count; 42316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung viewTypeCount = d.viewTypeCount; 42416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasStableIds = d.hasStableIds; 42516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView); 42616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 42716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 42816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 4293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 4303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = 0; 43116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 4323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by default there is at least one dummy view type 4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung viewTypeCount = 1; 4343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung hasStableIds = true; 4353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = null; 4363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 4373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = 0; 43816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTypeIdIndexMap.clear(); 439499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 440499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { 4423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = loadingView; 4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (firstView != null) { 4443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = firstView; 4453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = -1; 446499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 447499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 448499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getMappedViewType(int typeId) { 4503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mTypeIdIndexMap.containsKey(typeId)) { 4513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mTypeIdIndexMap.get(typeId); 4523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 4533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We +1 because the loading view always has view type id of 0 4543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int incrementalTypeId = mTypeIdIndexMap.size() + 1; 4553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap.put(typeId, incrementalTypeId); 4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return incrementalTypeId; 4576394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4586394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4596394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 460a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public boolean isViewTypeInRange(int typeId) { 461a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int mappedType = getMappedViewType(typeId); 462a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (mappedType >= viewTypeCount) { 463a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return false; 464a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 465a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return true; 466a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 467a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 468a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 4693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayout createLoadingView(int position, View convertView, 470a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen ViewGroup parent, Object lock, LayoutInflater layoutInflater, OnClickHandler 471a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen handler) { 4723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create and return a new FrameLayout, and setup the references for this position 4733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Context context = parent.getContext(); 4743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); 4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create a new loading view 477a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (lock) { 478b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen boolean customLoadingViewAvailable = false; 479b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 4803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mUserLoadingView != null) { 481b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Try to inflate user-specified loading view 482b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 483a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen View loadingView = mUserLoadingView.apply(parent.getContext(), parent, 484a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen handler); 485b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, 486b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(0)); 487b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(loadingView); 488b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen customLoadingViewAvailable = true; 489b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e) { 490b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating custom loading view, using default loading" + 491b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "view instead", e); 492b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 493b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 494b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (!customLoadingViewAvailable) { 4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A default loading view 4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Use the size of the first row as a guide for the size of the loading view 4973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mFirstViewHeight < 0) { 498b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 499a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen View firstView = mFirstView.apply(parent.getContext(), parent, handler); 500b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen firstView.measure( 501b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 502b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 503b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstViewHeight = firstView.getMeasuredHeight(); 504b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstView = null; 505b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e) { 506a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen float density = context.getResources().getDisplayMetrics().density; 507b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstViewHeight = (int) 508b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Math.round(sDefaultLoadingViewHeight * density); 509b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstView = null; 510b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating first RemoteViews" + e); 511b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 5123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 5133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Compose the loading view text 515a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen TextView loadingTextView = (TextView) layoutInflater.inflate( 516fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen com.android.internal.R.layout.remote_views_adapter_default_loading_view, 517fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen layout, false); 518a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setHeight(mFirstViewHeight); 519a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setTag(new Integer(0)); 520a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung 521a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung layout.addView(loadingTextView); 522fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung } 5236394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 5246394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return layout; 5266394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 5286394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with a single item in the cache. 5313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 5324a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static class RemoteViewsIndexMetaData { 5333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId; 5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId; 535499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 536591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen public RemoteViewsIndexMetaData(RemoteViews v, long itemId) { 537591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen set(v, itemId); 538499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 540591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen public void set(RemoteViews v, long id) { 5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = id; 542a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (v != null) { 5433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = v.getLayoutId(); 544a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = 0; 546a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 549499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 5513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * 5523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 5534a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen private static class FixedSizeRemoteViewsCache { 5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final String TAG = "FixedSizeRemoteViewsCache"; 5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The meta data related to all the RemoteViews, ie. count, is stable, etc. 5574c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // The meta data objects are made final so that they can be locked on independently 5584c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in 5594c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // the order mTemporaryMetaData followed by mMetaData. 5604c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mMetaData; 5614c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mTemporaryMetaData; 5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be 5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // greater than or equal to the set of RemoteViews. 5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: The reason that we keep this separate from the RemoteViews cache below is that this 5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we still need to be able to access the mapping of position to meta data, without keeping 5673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. 5683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // memory and size, but this metadata cache will retain information until the data at the 5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). 5703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; 5713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses 5733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // too much memory. 5743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViews> mIndexRemoteViews; 5753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices that have been explicitly requested by the collection view 5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mRequestedIndices; 5783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 579b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // We keep a reference of the last requested index to determine which item to prune the 580b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest items from when we hit the memory limit 581b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private int mLastRequestedIndex; 582b90a91c633e99d4559095184af27d1416541d3c0Winson Chung 5833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices to load, including those explicitly requested, as well as those 5843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // determined by the preloading algorithm to be prefetched 5853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mLoadIndices; 5863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The lower and upper bounds of the preloaded range 5883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadLowerBound; 5893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadUpperBound; 5903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The bounds of this fixed cache, we will try and fill as many items into the cache up to 5923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the maxCount number of items, or the maxSize memory usage. 5933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The maxCountSlack is used to determine if a new position in the cache to be loaded is 5943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // sufficiently ouside the old set, prompting a shifting of the "window" of items to be 5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloaded. 5963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCount; 5973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCountSlack; 5983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final float sMaxCountSlackPercent = 0.75f; 599b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024; 6003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public FixedSizeRemoteViewsCache(int maxCacheSize) { 6023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCount = maxCacheSize; 6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); 6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 6053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 6063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMetaData = new RemoteViewsMetaData(); 60716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTemporaryMetaData = new RemoteViewsMetaData(); 6083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 6093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 6103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices = new HashSet<Integer>(); 611b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 6123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices = new HashSet<Integer>(); 6133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 614499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 615591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen public void insert(int position, RemoteViews v, long itemId, 616591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> visibleWindow) { 6173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the count 6183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.size() >= mMaxCount) { 619591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mIndexRemoteViews.remove(getFarthestPositionFrom(position, visibleWindow)); 620499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 621499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the available memory size constraints 623b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position; 624b90a91c633e99d4559095184af27d1416541d3c0Winson Chung while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) { 6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: This is currently the most naive mechanism for deciding what to prune when 6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we hit the memory limit. In the future, we may want to calculate which index to 6273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // remove based on both its position as well as it's current memory usage, as well 6283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // as whether it was directly requested vs. whether it was preloaded by our caching 6293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // mechanism. 630591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition, visibleWindow)); 631499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Update the metadata cache 6343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); 636591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen metaData.set(v, itemId); 6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 638591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId)); 639499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.put(position, v); 6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 642499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData getMetaData() { 6443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mMetaData; 6453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 64616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public RemoteViewsMetaData getTemporaryMetaData() { 64716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mTemporaryMetaData; 64816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 6493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViews getRemoteViewsAt(int position) { 6503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.containsKey(position)) { 6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.get(position); 6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 653499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 654499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData getMetaDataAt(int position) { 6563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 6573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.get(position); 658499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return null; 6603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 661499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 66216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void commitTemporaryMetaData() { 66316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mTemporaryMetaData) { 66416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mMetaData) { 66516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMetaData.set(mTemporaryMetaData); 66616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 66716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 66816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 66916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getRemoteViewsBitmapMemoryUsage() { 6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Calculate the memory usage of all the RemoteViews bitmaps being cached 6723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mem = 0; 6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (Integer i : mIndexRemoteViews.keySet()) { 6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews v = mIndexRemoteViews.get(i); 675aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung if (v != null) { 6765d20064651b9947a4573c9a0eefec90f66eb1b59Adam Cohen mem += v.estimateMemoryUsage(); 677aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung } 678499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mem; 680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 681591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen 682591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen private int getFarthestPositionFrom(int pos, ArrayList<Integer> visibleWindow) { 6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Find the index farthest away and remove that 6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDist = 0; 6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDistIndex = -1; 686591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen int maxDistNotVisible = 0; 687591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen int maxDistIndexNotVisible = -1; 6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i : mIndexRemoteViews.keySet()) { 6893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int dist = Math.abs(i-pos); 690591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen if (dist > maxDistNotVisible && !visibleWindow.contains(i)) { 691591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // maxDistNotVisible/maxDistIndexNotVisible will store the index of the 692591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // farthest non-visible position 693591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen maxDistIndexNotVisible = i; 694591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen maxDistNotVisible = dist; 695b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 69635fbe2a5923d45ebcdfb3ad74efd1089a05e8737Adam Cohen if (dist >= maxDist) { 697b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDist/maxDistIndex will store the index of the farthest position 698591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // regardless of whether it is visible or not 6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDistIndex = i; 7003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDist = dist; 701c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 702c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 703591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen if (maxDistIndexNotVisible > -1) { 704591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen return maxDistIndexNotVisible; 705b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return maxDistIndex; 707c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 708c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 7093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queueRequestedPositionToLoad(int position) { 710b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = position; 7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.add(position); 7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(position); 714499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 715499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 71616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) { 7173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Check if we need to preload any items 7183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { 7193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; 7203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (Math.abs(position - center) < mMaxCountSlack) { 72116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return false; 722499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 723499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 724499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = 0; 7263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mMetaData) { 7273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = mMetaData.count; 728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 7313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the requested indices 7333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.addAll(mRequestedIndices); 7343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the preload indices 7363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int halfMaxCount = mMaxCount / 2; 7373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = position - halfMaxCount; 7383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = position + halfMaxCount; 7393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveLowerBound = Math.max(0, mPreloadLowerBound); 7403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); 7413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { 7423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(i); 7433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 744499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // But remove all the indices that have already been loaded and are cached 7463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.removeAll(mIndexRemoteViews.keySet()); 747499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 74816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return true; 749499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 750b90a91c633e99d4559095184af27d1416541d3c0Winson Chung /** Returns the next index to load, and whether that index was directly requested or not */ 751b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public int[] getNextIndexToLoad() { 7523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We try and prioritize items that have been requested directly, instead 7533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // of items that are loaded as a result of the caching mechanism 7543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Prioritize requested indices to be loaded first 7563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mRequestedIndices.isEmpty()) { 7573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mRequestedIndices.iterator().next(); 7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.remove(i); 7593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 760b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 1}; 7613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 762499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, preload other indices as necessary 7643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mLoadIndices.isEmpty()) { 7653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mLoadIndices.iterator().next(); 7663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 767b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 0}; 7683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 769499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 770b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{-1, 0}; 771c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 7723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 773c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 7743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsRemoteViewAt(int position) { 7753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.containsKey(position); 7763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsMetaDataAt(int position) { 7783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.containsKey(position); 7793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 780499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 78261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Note: We do not try and reset the meta data, since that information is still used by 78361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // collection views to validate it's own contents (and will be re-requested if the data 78461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // is invalidated through the notifyDataSetChanged() flow). 78561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 7863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 7873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 788b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 7893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.clear(); 7903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.clear(); 7913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.clear(); 7933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 794499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 795499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 796499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 797499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 798ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani static class RemoteViewsCacheKey { 799ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani final Intent.FilterComparison filter; 800ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani final int widgetId; 801ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani final int userId; 802ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani 803ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId, int userId) { 804ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani this.filter = filter; 805ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani this.widgetId = widgetId; 806ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani this.userId = userId; 807ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani } 808ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani 809ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani @Override 810ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani public boolean equals(Object o) { 811ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani if (!(o instanceof RemoteViewsCacheKey)) { 812ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani return false; 813ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani } 814ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani RemoteViewsCacheKey other = (RemoteViewsCacheKey) o; 815ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani return other.filter.equals(filter) && other.widgetId == widgetId 816ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani && other.userId == userId; 817ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani } 818ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani 819ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani @Override 820ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani public int hashCode() { 821ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2) ^ (userId << 10); 822ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani } 823ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani } 824ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani 825499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { 826499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mContext = context; 827499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mIntent = intent; 82881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); 829a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung mLayoutInflater = LayoutInflater.from(context); 8309b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung if (mIntent == null) { 8319b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung throw new IllegalArgumentException("Non-null Intent must be specified."); 8329b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews = new RemoteViewsFrameLayoutRefSet(); 834499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 8353e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller checkInteractAcrossUsersPermission(context, UserHandle.myUserId()); 8363e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller mUserId = context.getUserId(); 8373e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller 83881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Strip the previously injected app widget id from service intent 83981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { 84081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); 84181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 84281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 84381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the worker thread 844499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); 845499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread.start(); 846499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerQueue = new Handler(mWorkerThread.getLooper()); 84781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue = new Handler(Looper.myLooper(), this); 848499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 849335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCacheRemovalThread == null) { 850335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner"); 851335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalThread.start(); 852335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper()); 853335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 854335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 85581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the cache and the service connection on startup 8563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); 8573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mServiceConnection = new RemoteViewsAdapterServiceConnection(this); 858335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 859ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent), 860ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani mAppWidgetId, mUserId); 861335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 862335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(sCachedRemoteViewsCaches) { 863335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCachedRemoteViewsCaches.containsKey(key)) { 8644a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen mCache = sCachedRemoteViewsCaches.get(key); 8654a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen synchronized (mCache.mMetaData) { 8664a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if (mCache.mMetaData.count > 0) { 8674a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // As a precautionary measure, we verify that the meta data indicates a 8684a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // non-zero count before declaring that data is ready. 8694a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen mDataReady = true; 8704a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 8714a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 872335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } else { 873335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); 8744a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 8754a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if (!mDataReady) { 876335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen requestBindService(); 877335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 878335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 879499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 880499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 8813e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller private static void checkInteractAcrossUsersPermission(Context context, int userId) { 8823e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller if (context.getUserId() != userId 8833e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller && context.checkCallingOrSelfPermission(MULTI_USER_PERM) 8843e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller != PackageManager.PERMISSION_GRANTED) { 8853e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller throw new SecurityException("Must have permission " + MULTI_USER_PERM 8863e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller + " to inflate another user's widget"); 8873e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller } 8883e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller } 8893e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller 890fc442bdea14289656ef1f537103578eb71faf473Jeff Brown @Override 891fc442bdea14289656ef1f537103578eb71faf473Jeff Brown protected void finalize() throws Throwable { 892fc442bdea14289656ef1f537103578eb71faf473Jeff Brown try { 893fc442bdea14289656ef1f537103578eb71faf473Jeff Brown if (mWorkerThread != null) { 894fc442bdea14289656ef1f537103578eb71faf473Jeff Brown mWorkerThread.quit(); 895fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 896fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } finally { 897fc442bdea14289656ef1f537103578eb71faf473Jeff Brown super.finalize(); 898fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 899fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 900fc442bdea14289656ef1f537103578eb71faf473Jeff Brown 901335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public boolean isDataReady() { 902335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen return mDataReady; 903335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 904335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 905a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen public void setRemoteViewsOnClickHandler(OnClickHandler handler) { 906a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen mRemoteViewsOnClickHandler = handler; 907a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen } 908a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen 909335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void saveRemoteViewsCache() { 910ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani final RemoteViewsCacheKey key = new RemoteViewsCacheKey( 911ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani new Intent.FilterComparison(mIntent), mAppWidgetId, mUserId); 912335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 913335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(sCachedRemoteViewsCaches) { 914335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // If we already have a remove runnable posted for this key, remove it. 915335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { 916335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key)); 917335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.remove(key); 918335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 919335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 9204a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen int metaDataCount = 0; 9214a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen int numRemoteViewsCached = 0; 9224a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen synchronized (mCache.mMetaData) { 9234a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen metaDataCount = mCache.mMetaData.count; 924335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 9254a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen synchronized (mCache) { 9264a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen numRemoteViewsCached = mCache.mIndexRemoteViews.size(); 9274a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 9284a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if (metaDataCount > 0 && numRemoteViewsCached > 0) { 9294a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen sCachedRemoteViewsCaches.put(key, mCache); 9304a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 9314a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen 932335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Runnable r = new Runnable() { 933335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 934335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void run() { 935335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized (sCachedRemoteViewsCaches) { 936335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCachedRemoteViewsCaches.containsKey(key)) { 937335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCachedRemoteViewsCaches.remove(key); 938335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 939335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { 940335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.remove(key); 941335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 942335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 943335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 944335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen }; 945335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.put(key, r); 946335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION); 947335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 948335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 949335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 9503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void loadNextIndexInBackground() { 9513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mWorkerQueue.post(new Runnable() { 9523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 9533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 95416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mServiceConnection.isConnected()) { 95516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Get the next index to load 95616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int position = -1; 95716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 958b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int[] res = mCache.getNextIndexToLoad(); 959b90a91c633e99d4559095184af27d1416541d3c0Winson Chung position = res[0]; 96016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 96116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (position > -1) { 96216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item, and notify any existing RemoteViewsFrameLayouts 963591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen updateRemoteViews(position, true); 9643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 96516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up for the next one to load 96616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 96716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 96816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // No more items to load, so queue unbind 96916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 97016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 9713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 9743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 97616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void processException(String method, Exception e) { 97716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage()); 9783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 97916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we encounter a crash when updating, we should reset the metadata & cache and trigger 98016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // a notifyDataSetChanged to update the widget accordingly 98116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 98216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 98316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung metaData.reset(); 98416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 98516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 98616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 98716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 98816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 98916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 99016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 99116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 9923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 99316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 99416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 99516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 99616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void updateTemporaryMetaData() { 99716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 99816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 99916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 100016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // get the properties/first view (so that we can use it to 100116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // measure our dummy views) 100216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasStableIds = factory.hasStableIds(); 100316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int viewTypeCount = factory.getViewTypeCount(); 100416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int count = factory.getCount(); 100516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews loadingView = factory.getLoadingView(); 100616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews firstView = null; 100716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if ((count > 0) && (loadingView == null)) { 100816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung firstView = factory.getViewAt(0); 100916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 101016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData(); 101116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (tmpMetaData) { 101216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.hasStableIds = hasStableIds; 101316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We +1 because the base view type is the loading view 101416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.viewTypeCount = viewTypeCount + 1; 101516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.count = count; 101616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.setLoadingViewTemplates(loadingView, firstView); 101716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 10182625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch(RemoteException e) { 101916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung processException("updateMetaData", e); 1020fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen } catch(RuntimeException e) { 1021fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen processException("updateMetaData", e); 10223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1025591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen private void updateRemoteViews(final int position, boolean notifyWhenLoaded) { 102616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 102716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 102816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item information from the remote service 102916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews remoteViews = null; 103016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung long itemId = 0; 103116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 103216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung remoteViews = factory.getViewAt(position); 103394022e8997084be458a1faf46e72efab1d59e973Amith Yamasani remoteViews.setUser(new UserHandle(mUserId)); 103416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung itemId = factory.getItemId(position); 10352625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 103616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 103716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 103816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent additional work in re-centering the view cache, and 103916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // swapping from the loading view 104016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 10412625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 10422625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 10432625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 104416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 10453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 104616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (remoteViews == null) { 104716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If a null view was returned, we break early to prevent it from getting 104816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // into our cache and causing problems later. The effect is that the child at this 104916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // position will remain as a loading view until it is updated. 105016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + 105116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung "returned from RemoteViewsFactory."); 105216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 105316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 1054a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 1055a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int layoutId = remoteViews.getLayoutId(); 1056a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen RemoteViewsMetaData metaData = mCache.getMetaData(); 1057a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen boolean viewTypeInRange; 1058591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen int cacheCount; 1059a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (metaData) { 1060a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen viewTypeInRange = metaData.isViewTypeInRange(layoutId); 1061591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen cacheCount = mCache.mMetaData.count; 1062a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 106316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 1064a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (viewTypeInRange) { 1065591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> visibleWindow = getVisibleWindow(mVisibleWindowLowerBound, 1066591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mVisibleWindowUpperBound, cacheCount); 1067a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Cache the RemoteViews we loaded 1068591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mCache.insert(position, remoteViews, itemId, visibleWindow); 1069a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 1070a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Notify all the views that we have previously returned for this index that 1071a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // there is new data for it. 1072a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen final RemoteViews rv = remoteViews; 1073a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (notifyWhenLoaded) { 1074a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mMainQueue.post(new Runnable() { 1075a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen @Override 1076a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void run() { 1077a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mRequestedViews.notifyOnRemoteViewsLoaded(position, rv); 1078a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 1079a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen }); 1080a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 1081a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 1082a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // We need to log an error here, as the the view type count specified by the 1083a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // factory is less than the number of view types returned. We don't return this 1084a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // view to the AdapterView, as this will cause an exception in the hosting process, 1085a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // which contains the associated AdapterView. 1086a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " + 1087a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen " indicated by getViewTypeCount() "); 1088b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 10893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1090499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1091499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 10929b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung public Intent getRemoteViewsServiceIntent() { 10939b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung return mIntent; 10949b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 10959b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 1096499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getCount() { 10973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 10983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 10993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.count; 11003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1101499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public Object getItem(int position) { 11043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Disallow arbitrary object to be associated with an item for the time being 1105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 1106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public long getItemId(int position) { 11093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 11103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 11113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mCache.getMetaDataAt(position).itemId; 11123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 11143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1115499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1116499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getItemViewType(int position) { 11183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = 0; 11193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 11203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 11213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = mCache.getMetaDataAt(position).typeId; 11223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 11233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 11243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 11273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 11283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 11293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.getMappedViewType(typeId); 11303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 11333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 11343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Returns the item type id for the specified convert view. Returns -1 if the convert view 11353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * is invalid. 11363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 11373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getConvertViewTypeId(View convertView) { 11383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = -1; 1139a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (convertView != null) { 1140a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId); 1141a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (tag != null) { 1142a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen typeId = (Integer) tag; 1143a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen } 11443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return typeId; 1146499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1147499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1148b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen /** 1149b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * This method allows an AdapterView using this Adapter to provide information about which 1150b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * views are currently being displayed. This allows for certain optimizations and preloading 1151b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * which wouldn't otherwise be possible. 1152b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen */ 1153b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen public void setVisibleRangeHint(int lowerBound, int upperBound) { 1154b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowLowerBound = lowerBound; 1155b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowUpperBound = upperBound; 1156b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1157b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 1158499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public View getView(int position, View convertView, ViewGroup parent) { 115916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // "Request" an index so that we can queue it for loading, initiate subsequent 116016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // preloading, etc. 116116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 116216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isInCache = mCache.containsRemoteViewAt(position); 116316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isConnected = mServiceConnection.isConnected(); 116416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasNewItems = false; 116516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1166ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen if (convertView != null && convertView instanceof RemoteViewsFrameLayout) { 1167ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen mRequestedViews.removeView((RemoteViewsFrameLayout) convertView); 1168ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen } 1169ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen 11707ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung if (!isInCache && !isConnected) { 117116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Requesting bind service will trigger a super.notifyDataSetChanged(), which will 117216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // in turn trigger another request to getView() 117316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 117416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 11753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up other indices to be preloaded based on this position 117616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position); 117716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 117816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 117916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (isInCache) { 11803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View convertViewChild = null; 11813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int convertViewTypeId = 0; 1182181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen RemoteViewsFrameLayout layout = null; 1183181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen 1184181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (convertView instanceof RemoteViewsFrameLayout) { 1185181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen layout = (RemoteViewsFrameLayout) convertView; 11863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewChild = layout.getChildAt(0); 11873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewTypeId = getConvertViewTypeId(convertViewChild); 11883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 11903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Second, we try and retrieve the RemoteViews from the cache, returning a loading 11913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // view and queueing it to be loaded if it has not already been loaded. 119216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Context context = parent.getContext(); 119316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews rv = mCache.getRemoteViewsAt(position); 1194aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position); 1195aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen int typeId = indexMetaData.typeId; 119616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1197b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 1198b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Reuse the convert view where possible 1199b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (layout != null) { 1200b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (convertViewTypeId == typeId) { 1201a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen rv.reapply(context, convertViewChild, mRemoteViewsOnClickHandler); 1202b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1203b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1204b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.removeAllViews(); 1205b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } else { 1206b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout = new RemoteViewsFrameLayout(context); 12073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 12083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1209b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Otherwise, create a new view to be returned 1210a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen View newView = rv.apply(context, parent, mRemoteViewsOnClickHandler); 1211b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen newView.setTagInternal(com.android.internal.R.id.rowTypeId, 1212b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(typeId)); 1213b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(newView); 1214b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1215b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1216b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e){ 1217b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // We have to make sure that we successfully inflated the RemoteViews, if not 1218b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // we return the loading view instead. 1219b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" + 1220b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "loading view instead" + e); 1221b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1222b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen RemoteViewsFrameLayout loadingView = null; 1223b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen final RemoteViewsMetaData metaData = mCache.getMetaData(); 1224b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen synchronized (metaData) { 1225a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1226a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen mCache, mLayoutInflater, mRemoteViewsOnClickHandler); 1227b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1228b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return loadingView; 1229b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } finally { 1230b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (hasNewItems) loadNextIndexInBackground(); 1231b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 123216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 123316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If the cache does not have the RemoteViews at this position, then create a 123416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // loading view and queue the actual position to be loaded in the background 123516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViewsFrameLayout loadingView = null; 123616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 123716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 1238a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1239a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen mCache, mLayoutInflater, mRemoteViewsOnClickHandler); 12403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 124116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 124216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mRequestedViews.add(position, loadingView); 124316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.queueRequestedPositionToLoad(position); 124416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 124516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 124616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return loadingView; 12473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 12483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1249499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1250499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1251499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getViewTypeCount() { 12523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 12533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 12543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.viewTypeCount; 12553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1256499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1257499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean hasStableIds() { 12593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 12603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 12613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.hasStableIds; 12623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isEmpty() { 1266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return getCount() <= 0; 1267499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1268499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 126916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void onNotifyDataSetChanged() { 127016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Complete the actual notifyDataSetChanged() call initiated earlier 127116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 127216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 127316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 12742625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 127516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 127616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 127716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent from further being notified (since nothing has 127816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // changed) 127916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 12802625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 12812625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 12822625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 128316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 128416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 128516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Flush the cache so that we can reload new items from the service 128616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 128716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 128816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 128916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 129016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Re-request the new metadata (only after the notification to the factory) 129116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung updateTemporaryMetaData(); 12924c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen int newCount; 1293591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> visibleWindow; 12944c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen synchronized(mCache.getTemporaryMetaData()) { 12954c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen newCount = mCache.getTemporaryMetaData().count; 1296591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen visibleWindow = getVisibleWindow(mVisibleWindowLowerBound, 1297591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen mVisibleWindowUpperBound, newCount); 12984c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 129916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1300b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // Pre-load (our best guess of) the views which are currently visible in the AdapterView. 1301b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // This mitigates flashing and flickering of loading views when a widget notifies that 1302b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // its data has changed. 1303591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i: visibleWindow) { 13044c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // Because temporary meta data is only ever modified from this thread (ie. 13054c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // mWorkerThread), it is safe to assume that count is a valid representation. 13064c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen if (i < newCount) { 1307591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen updateRemoteViews(i, false); 13084c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 1309b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1310b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 131116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Propagate the notification back to the base adapter 131216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 13136364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 13146364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 13156364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung synchronized (mCache) { 131616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.commitTemporaryMetaData(); 13176364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 131861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 131916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 132016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 13213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 13223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 13236364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 132416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Reset the notify flagflag 132516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = false; 132616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 132716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1328591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen private ArrayList<Integer> getVisibleWindow(int lower, int upper, int count) { 1329591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen ArrayList<Integer> window = new ArrayList<Integer>(); 13304a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen 13314a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen // In the case that the window is invalid or uninitialized, return an empty window. 13324a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) { 13334a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen return window; 13344a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen } 13354a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen 1336591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen if (lower <= upper) { 1337591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i = lower; i <= upper; i++){ 1338591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen window.add(i); 1339591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1340591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } else { 1341591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // If the upper bound is less than the lower bound it means that the visible window 1342591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen // wraps around. 1343591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i = lower; i < count; i++) { 1344591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen window.add(i); 1345591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1346591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen for (int i = 0; i <= upper; i++) { 1347591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen window.add(i); 1348591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1349591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1350591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen return window; 1351591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen } 1352591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen 135316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void notifyDataSetChanged() { 135416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 135516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 135616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 135716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we are not connected, queue up the notifyDataSetChanged to be handled when we do 135816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // connect 135916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mServiceConnection.isConnected()) { 136016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mNotifyDataSetChangedAfterOnServiceConnected) { 136116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 136216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 136316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 136416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = true; 136516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 136616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 136716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 136816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 136916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mWorkerQueue.post(new Runnable() { 137016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 137116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 137216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung onNotifyDataSetChanged(); 137316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 137416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 13753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 13763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1377fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen void superNotifyDataSetChanged() { 1378499cb9f516062b654952d282f211bee44c31a3c2Winson Chung super.notifyDataSetChanged(); 1379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1380499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 138181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung @Override 138281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public boolean handleMessage(Message msg) { 138381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung boolean result = false; 138481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung switch (msg.what) { 138581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung case sUnbindServiceMessageType: 138681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (mServiceConnection.isConnected()) { 138716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.unbind(mContext, mAppWidgetId, mIntent); 138881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 138981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung result = true; 139081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 139181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung default: 139281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 139381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 139481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung return result; 139581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 139681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 139781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private void enqueueDeferredUnbindServiceMessage() { 139881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Remove any existing deferred-unbind messages 139981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 140081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay); 140181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 140281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 1403499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean requestBindService() { 140481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Try binding the service (which will start it if it's not already running) 1405499cb9f516062b654952d282f211bee44c31a3c2Winson Chung if (!mServiceConnection.isConnected()) { 140616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.bind(mContext, mAppWidgetId, mIntent); 1407499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1408499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 140916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any existing deferred-unbind messages 141016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 1411499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mServiceConnection.isConnected(); 1412499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1413499cb9f516062b654952d282f211bee44c31a3c2Winson Chung} 1414