RemoteViewsAdapter.java revision 81f39eb6e76d0be1dd341af835e8002a0f80524e
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; 20499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap; 213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.util.HashSet; 22c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chungimport java.util.LinkedList; 23499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.Map; 24499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager; 26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context; 27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent; 28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler; 29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread; 30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder; 31499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper; 3281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message; 33fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log; 34a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater; 35499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View; 363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport android.view.ViewGroup; 37181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec; 38499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection; 40499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory; 41499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** 43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews 44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views. 45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */ 4781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback { 48fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung private static final String TAG = "RemoteViewsAdapter"; 49499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The max number of items in the cache 5181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sDefaultCacheSize = 36; 5281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The delay (in millis) to wait until attempting to unbind from a service after a request. 5381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // This ensures that we don't stay continually bound to the service and that it can be destroyed 5481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // if we need the memory elsewhere in the system. 5581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceDelay = 5000; 5681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Type defs for controlling different messages across the main and worker message queues 5781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sDefaultMessageType = 0; 5881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceMessageType = 1; 5981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 6081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Context mContext; 6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Intent mIntent; 6281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final int mAppWidgetId; 63a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung private LayoutInflater mLayoutInflater; 64499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private RemoteViewsAdapterServiceConnection mServiceConnection; 653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteAdapterConnectionCallback> mCallback; 663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private FixedSizeRemoteViewsCache mCache; 673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of requested views that are to be notified when the associated RemoteViews are 693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loaded. 703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayoutRefSet mRequestedViews; 71499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 72499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private HandlerThread mWorkerThread; 73499cb9f516062b654952d282f211bee44c31a3c2Winson Chung // items may be interrupted within the normally processed queues 74499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mWorkerQueue; 75499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mMainQueue; 76499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 77499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 78499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An interface for the RemoteAdapter to notify other classes when adapters 79499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * are actually connected to/disconnected from their actual services. 80499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public interface RemoteAdapterConnectionCallback { 82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterConnected(); 83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterDisconnected(); 85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 87499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * The service connection that gets populated when the RemoteViewsService is 893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * bound. This must be a static inner class to ensure that no references to the outer 903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being 913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * garbage collected, and would cause us to leak activities due to the caching mechanism for 923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * FrameLayouts in the adapter). 93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 9481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static class RemoteViewsAdapterServiceConnection extends 9581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung IRemoteViewsAdapterConnection.Stub { 96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean mConnected; 973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteViewsAdapter> mAdapter; 98499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private IRemoteViewsFactory mRemoteViewsFactory; 99499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { 1013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); 102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 10481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public void onServiceConnected(IBinder service) { 105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); 106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mConnected = true; 107c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 1083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up work that we need to do for the callback to run 1093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 1103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 1113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mWorkerQueue.post(new Runnable() { 112499cb9f516062b654952d282f211bee44c31a3c2Winson Chung @Override 113499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void run() { 1143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Call back to the service to notify that the data set changed 1153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter.mServiceConnection.isConnected()) { 1163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = 1173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mServiceConnection.getRemoteViewsFactory(); 1183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 1193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // call back to the factory 1203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung factory.onDataSetChanged(); 1213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } catch (Exception e) { 1223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error notifying factory of data set changed in " + 1233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung "onServiceConnected(): " + e.getMessage()); 1243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung e.printStackTrace(); 1253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent anything further from being notified 1273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // (effectively nothing has changed) 1283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 129499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Request meta data so that we have up to date data when calling back to 1323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the remote adapter callback 1333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.updateMetaData(); 1343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Post a runnable to call back to the view to notify it that we have 1363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // connected 13761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung adapter.mMainQueue.post(new Runnable() { 1383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 1393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = 1413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mCallback.get(); 1423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 1433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterConnected(); 1443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 1473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 148499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 149499cb9f516062b654952d282f211bee44c31a3c2Winson Chung }); 150499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 151499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 15281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public void onServiceDisconnected() { 153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mConnected = false; 1543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsFactory = null; 155499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 1573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 158c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 1593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Clear the main/worker queues 16081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung adapter.mMainQueue.removeMessages(sUnbindServiceMessageType); 16181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung adapter.mMainQueue.removeMessages(sDefaultMessageType); 16281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung adapter.mWorkerQueue.removeMessages(sDefaultMessageType); 1633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); 1653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 1663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterDisconnected(); 1673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 168ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy adapter.mCache.reset(); 169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 170499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public IRemoteViewsFactory getRemoteViewsFactory() { 172499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mRemoteViewsFactory; 173499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 174499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 175499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isConnected() { 176499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mConnected; 177499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 178499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 179499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 180499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 1813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when 1823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * they are loaded. 183499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 1843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayout extends FrameLayout { 1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayout(Context context) { 1863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung super(context); 1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 188499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 189499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 1903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Updates this RemoteViewsFrameLayout depending on the view that was loaded. 1913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded 1923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * successfully. 193499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 1943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void onRemoteViewsLoaded(RemoteViews view) { 19561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung try { 19661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Remove all the children of this layout first 19761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung removeAllViews(); 19861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung addView(view.apply(getContext(), this)); 19961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } catch (Exception e) { 20061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung Log.e(TAG, "Failed to apply RemoteViews."); 20161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } 2023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 204499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the 2073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * adapter that have not yet had their RemoteViews loaded. 2083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayoutRefSet { 2103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; 211499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayoutRefSet() { 2133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); 214499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 215499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 216499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. 218499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void add(int position, RemoteViewsFrameLayout layout) { 2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 2213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung LinkedList<RemoteViewsFrameLayout> refs; 2223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create the list if necessary 2243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 2253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = mReferences.get(pos); 2263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = new LinkedList<RemoteViewsFrameLayout>(); 2283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.put(pos, refs); 229499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add the references to the list 2323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.add(layout); 233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that 2373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * the associated RemoteViews has loaded. 2383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) { 24061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung if (view == null) return; 24161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 2423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 2433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 2443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the references for that position of the newly loaded RemoteViews 2453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); 2463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (final RemoteViewsFrameLayout ref : refs) { 2473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ref.onRemoteViewsLoaded(view); 2483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.clear(); 250499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Remove this set from the original mapping 2523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.remove(pos); 253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. 2583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void clear() { 2603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We currently just clear the references, and leave all the previous layouts returned 2613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // in their default state of the loading view. 2623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.clear(); 263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with the cache in it's current state. 2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsMetaData { 2703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count; 2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount; 2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds; 2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean isDataDirty; 2743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Used to determine how to construct loading views. If a loading view is not specified 2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by the user, then we try and load the first view, and use its height as the height for 2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the default loading view. 2783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mUserLoadingView; 2793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mFirstView; 2803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mFirstViewHeight; 2813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A mapping from type id to a set of unique type ids 2833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private Map<Integer, Integer> mTypeIdIndexMap; 2843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData() { 2863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung reset(); 287499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 288499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 2903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = 0; 2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by default there is at least one dummy view type 2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung viewTypeCount = 1; 2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung hasStableIds = true; 2943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung isDataDirty = false; 2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = null; 2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = 0; 2983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap = new HashMap<Integer, Integer>(); 299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 300499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { 3023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = loadingView; 3033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (firstView != null) { 3043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = firstView; 3053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = -1; 306499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 308499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getMappedViewType(int typeId) { 3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mTypeIdIndexMap.containsKey(typeId)) { 3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mTypeIdIndexMap.get(typeId); 3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We +1 because the loading view always has view type id of 0 3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int incrementalTypeId = mTypeIdIndexMap.size() + 1; 3153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap.put(typeId, incrementalTypeId); 3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return incrementalTypeId; 3176394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3186394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3196394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 3203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayout createLoadingView(int position, View convertView, 3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ViewGroup parent) { 3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create and return a new FrameLayout, and setup the references for this position 3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Context context = parent.getContext(); 3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); 3253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create a new loading view 3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mUserLoadingView != null) { 3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A user-specified loading view 3303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View loadingView = mUserLoadingView.apply(parent.getContext(), parent); 331a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0)); 3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung layout.addView(loadingView); 3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A default loading view 3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Use the size of the first row as a guide for the size of the loading view 3363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mFirstViewHeight < 0) { 3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View firstView = mFirstView.apply(parent.getContext(), parent); 3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung firstView.measure( 3393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 3403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = firstView.getMeasuredHeight(); 3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Compose the loading view text 346a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung TextView loadingTextView = (TextView) mLayoutInflater.inflate( 347fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen com.android.internal.R.layout.remote_views_adapter_default_loading_view, 348fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen layout, false); 349a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setHeight(mFirstViewHeight); 350a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setTag(new Integer(0)); 351a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung 352a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung layout.addView(loadingTextView); 353fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung } 3546394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3556394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return layout; 3576394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3596394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 3603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with a single item in the cache. 3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsIndexMetaData { 3643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId; 3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId; 366499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData(RemoteViews v, long itemId) { 3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung set(v, itemId); 369499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 370499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void set(RemoteViews v, long id) { 3723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = id; 3733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (v != null) 3743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = v.getLayoutId(); 3753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung else 3763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = 0; 3773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * 3823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class FixedSizeRemoteViewsCache { 3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final String TAG = "FixedSizeRemoteViewsCache"; 3853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The meta data related to all the RemoteViews, ie. count, is stable, etc. 3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsMetaData mMetaData; 3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be 3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // greater than or equal to the set of RemoteViews. 3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: The reason that we keep this separate from the RemoteViews cache below is that this 3923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we still need to be able to access the mapping of position to meta data, without keeping 3933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. 3943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // memory and size, but this metadata cache will retain information until the data at the 3953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). 3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; 3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses 3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // too much memory. 4003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViews> mIndexRemoteViews; 4013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices that have been explicitly requested by the collection view 4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mRequestedIndices; 4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices to load, including those explicitly requested, as well as those 4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // determined by the preloading algorithm to be prefetched 4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mLoadIndices; 4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The lower and upper bounds of the preloaded range 4103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadLowerBound; 4113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadUpperBound; 4123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The bounds of this fixed cache, we will try and fill as many items into the cache up to 4143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the maxCount number of items, or the maxSize memory usage. 4153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The maxCountSlack is used to determine if a new position in the cache to be loaded is 4163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // sufficiently ouside the old set, prompting a shifting of the "window" of items to be 4173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloaded. 4183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCount; 4193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCountSlack; 4203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final float sMaxCountSlackPercent = 0.75f; 4213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final int sMaxMemoryUsage = 1024 * 1024; 4223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public FixedSizeRemoteViewsCache(int maxCacheSize) { 4243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCount = maxCacheSize; 4253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); 4263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 4273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 4283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMetaData = new RemoteViewsMetaData(); 4293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 4303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 4313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices = new HashSet<Integer>(); 4323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices = new HashSet<Integer>(); 4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 434499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void insert(int position, RemoteViews v, long itemId) { 4363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the count 4373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.size() >= mMaxCount) { 4383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(position)); 439499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 440499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the available memory size constraints 4423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryUsage) { 4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: This is currently the most naive mechanism for deciding what to prune when 4443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we hit the memory limit. In the future, we may want to calculate which index to 4453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // remove based on both its position as well as it's current memory usage, as well 4463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // as whether it was directly requested vs. whether it was preloaded by our caching 4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // mechanism. 4483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(position)); 449499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 4503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Update the metadata cache 4523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 4533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); 4543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung metaData.set(v, itemId); 4553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId)); 457499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 4583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.put(position, v); 4593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 460499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData getMetaData() { 4623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mMetaData; 4633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViews getRemoteViewsAt(int position) { 4653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.containsKey(position)) { 4663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.get(position); 4673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 468499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 469499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 4703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData getMetaDataAt(int position) { 4713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 4723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.get(position); 473499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 4743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return null; 4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 476499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getRemoteViewsBitmapMemoryUsage() { 4783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Calculate the memory usage of all the RemoteViews bitmaps being cached 4793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mem = 0; 4803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (Integer i : mIndexRemoteViews.keySet()) { 4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews v = mIndexRemoteViews.get(i); 482aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung if (v != null) { 483aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung mem += v.estimateBitmapMemoryUsage(); 484aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung } 485499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 4863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mem; 487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 4883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getFarthestPositionFrom(int pos) { 4893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Find the index farthest away and remove that 4903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDist = 0; 4913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDistIndex = -1; 4923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i : mIndexRemoteViews.keySet()) { 4933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int dist = Math.abs(i-pos); 4943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (dist > maxDist) { 4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDistIndex = i; 4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDist = dist; 497c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 498c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return maxDistIndex; 500c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 501c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 5023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queueRequestedPositionToLoad(int position) { 5033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.add(position); 5053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(position); 506499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 507499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queuePositionsToBePreloadedFromRequestedPosition(int position) { 5093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Check if we need to preload any items 5103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { 5113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; 5123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (Math.abs(position - center) < mMaxCountSlack) { 5133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 514499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 515499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 516499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = 0; 5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mMetaData) { 5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = mMetaData.count; 520499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 5223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 5233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the requested indices 5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.addAll(mRequestedIndices); 5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the preload indices 5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int halfMaxCount = mMaxCount / 2; 5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = position - halfMaxCount; 5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = position + halfMaxCount; 5313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveLowerBound = Math.max(0, mPreloadLowerBound); 5323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); 5333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { 5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(i); 5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 536499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // But remove all the indices that have already been loaded and are cached 5383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.removeAll(mIndexRemoteViews.keySet()); 539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 540499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getNextIndexToLoad() { 5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We try and prioritize items that have been requested directly, instead 5433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // of items that are loaded as a result of the caching mechanism 5443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Prioritize requested indices to be loaded first 5463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mRequestedIndices.isEmpty()) { 5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mRequestedIndices.iterator().next(); 5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.remove(i); 5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return i.intValue(); 5513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 552499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, preload other indices as necessary 5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mLoadIndices.isEmpty()) { 5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mLoadIndices.iterator().next(); 5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return i.intValue(); 5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 559499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return -1; 561c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 563c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsRemoteViewAt(int position) { 5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.containsKey(position); 5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 5673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsMetaDataAt(int position) { 5683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.containsKey(position); 5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 570499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 57261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Note: We do not try and reset the meta data, since that information is still used by 57361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // collection views to validate it's own contents (and will be re-requested if the data 57461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // is invalidated through the notifyDataSetChanged() flow). 57561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 5763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 5783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.clear(); 5793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.clear(); 5803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.clear(); 5823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 584499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 585499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 586499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 587499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { 588499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mContext = context; 589499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mIntent = intent; 59081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); 591a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung mLayoutInflater = LayoutInflater.from(context); 5929b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung if (mIntent == null) { 5939b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung throw new IllegalArgumentException("Non-null Intent must be specified."); 5949b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews = new RemoteViewsFrameLayoutRefSet(); 596499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 59781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Strip the previously injected app widget id from service intent 59881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { 59981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); 60081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 60181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 60281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the worker thread 603499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); 604499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread.start(); 605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerQueue = new Handler(mWorkerThread.getLooper()); 60681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue = new Handler(Looper.myLooper(), this); 607499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 60881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the cache and the service connection on startup 60981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); 6103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); 6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mServiceConnection = new RemoteViewsAdapterServiceConnection(this); 612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 614499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void loadNextIndexInBackground() { 6163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mWorkerQueue.post(new Runnable() { 6173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 6183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 6193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Get the next index to load 6203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int position = -1; 6213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung position = mCache.getNextIndexToLoad(); 6233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (position > -1) { 6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Load the item, and notify any existing RemoteViewsFrameLayouts 6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung updateRemoteViews(position); 6273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up for the next one to load 6293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung loadNextIndexInBackground(); 6303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 6333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void updateMetaData() { 6363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mServiceConnection.isConnected()) { 6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 6383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 6393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // get the properties/first view (so that we can use it to 6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // measure our dummy views) 6423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds = factory.hasStableIds(); 6433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount = factory.getViewTypeCount(); 6443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = factory.getCount(); 6453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews loadingView = factory.getLoadingView(); 6463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews firstView = null; 6473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if ((count > 0) && (loadingView == null)) { 6483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung firstView = factory.getViewAt(0); 6493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung metaData.hasStableIds = hasStableIds; 6533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung metaData.viewTypeCount = viewTypeCount + 1; 6543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung metaData.count = count; 6553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung metaData.setLoadingViewTemplates(loadingView, firstView); 6563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } catch (Exception e) { 6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // print the error 6593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error in requestMetaData(): " + e.getMessage()); 6603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // reset any members after the failed call 6623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 6643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung metaData.reset(); 6653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void updateRemoteViews(final int position) { 6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mServiceConnection.isConnected()) { 6723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Load the item information from the remote service 6753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews remoteViews = null; 6763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId = 0; 6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 6783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung remoteViews = factory.getViewAt(position); 6793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = factory.getItemId(position); 680ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy } catch (Throwable t) { 681ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + t.getMessage()); 682ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy t.printStackTrace(); 6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent additional work in re-centering the view cache, and 6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // swapping from the loading view 6863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 689490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen if (remoteViews == null) { 690490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen // If a null view was returned, we break early to prevent it from getting 691490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen // into our cache and causing problems later. The effect is that the child at this 692490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen // position will remain as a loading view until it is updated. 693490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + 694490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen "returned from RemoteViewsFactory."); 695490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen return; 696490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen } 6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Cache the RemoteViews we loaded 6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCache.insert(position, remoteViews, itemId); 7003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the views that we have previously returned for this index that 7023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // there is new data for it. 7033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews rv = remoteViews; 7043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final int typeId = mCache.getMetaDataAt(position).typeId; 7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMainQueue.post(new Runnable() { 7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 7083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId); 70981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung enqueueDeferredUnbindServiceMessage(); 7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 7123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 714499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 715499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7169b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung public Intent getRemoteViewsServiceIntent() { 7179b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung return mIntent; 7189b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 7199b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 720499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getCount() { 721499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 7223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 7233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 7243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.count; 7253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 726499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 727499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public Object getItem(int position) { 7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Disallow arbitrary object to be associated with an item for the time being 730499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 731499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 732499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 733499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public long getItemId(int position) { 734499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 7353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 7363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 7373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mCache.getMetaDataAt(position).itemId; 7383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 7403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 741499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 742499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 743499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getItemViewType(int position) { 744499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 7453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = 0; 7463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 7473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 7483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = mCache.getMetaDataAt(position).typeId; 7493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 7503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 7513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 7553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 7563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.getMappedViewType(typeId); 7573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 7613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Returns the item type id for the specified convert view. Returns -1 if the convert view 7623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * is invalid. 7633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 7643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getConvertViewTypeId(View convertView) { 7653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = -1; 766a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (convertView != null) { 767a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId); 768a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (tag != null) { 769a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen typeId = (Integer) tag; 770a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen } 7713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return typeId; 773499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 774499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 775499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public View getView(int position, View convertView, ViewGroup parent) { 776499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 7773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mServiceConnection.isConnected()) { 7783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // "Request" an index so that we can queue it for loading, initiate subsequent 7793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloading, etc. 7803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 7813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up other indices to be preloaded based on this position 7823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCache.queuePositionsToBePreloadedFromRequestedPosition(position); 7833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View convertViewChild = null; 7843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int convertViewTypeId = 0; 785181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen RemoteViewsFrameLayout layout = null; 786181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen 787181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (convertView instanceof RemoteViewsFrameLayout) { 788181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen layout = (RemoteViewsFrameLayout) convertView; 7893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewChild = layout.getChildAt(0); 7903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewTypeId = getConvertViewTypeId(convertViewChild); 7913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Second, we try and retrieve the RemoteViews from the cache, returning a loading 7943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // view and queueing it to be loaded if it has not already been loaded. 7953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsRemoteViewAt(position)) { 7963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Context context = parent.getContext(); 7973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews rv = mCache.getRemoteViewsAt(position); 7983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = mCache.getMetaDataAt(position).typeId; 7993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Reuse the convert view where possible 801181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (layout != null) { 8023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (convertViewTypeId == typeId) { 8033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung rv.reapply(context, convertViewChild); 804181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen return layout; 8053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, create a new view to be returned 8093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View newView = rv.apply(context, parent); 810a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId)); 811181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (layout != null) { 8123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung layout.removeAllViews(); 8133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 8143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung layout = new RemoteViewsFrameLayout(context); 8153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung layout.addView(newView); 8173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return layout; 8183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 8193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // If the cache does not have the RemoteViews at this position, then create a 8203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loading view and queue the actual position to be loaded in the background 8213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout loadingView = null; 8223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 8233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 8243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung loadingView = metaData.createLoadingView(position, convertView, parent); 8253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews.add(position, loadingView); 8283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCache.queueRequestedPositionToLoad(position); 8293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung loadNextIndexInBackground(); 8303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return loadingView; 8323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return new View(parent.getContext()); 836499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 837499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 838499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getViewTypeCount() { 839499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 8403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 8413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 8423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.viewTypeCount; 8433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 844499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 845499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 846499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean hasStableIds() { 847499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 8483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 8493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 8503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.hasStableIds; 8513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 852499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 853499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 854499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isEmpty() { 855499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return getCount() <= 0; 856499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 857499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 858499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void notifyDataSetChanged() { 8596364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung mWorkerQueue.post(new Runnable() { 8606364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 8616364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 8626364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // Complete the actual notifyDataSetChanged() call initiated earlier 8636364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung if (mServiceConnection.isConnected()) { 8646364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 8656364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung try { 8666364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung factory.onDataSetChanged(); 8676364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } catch (Exception e) { 8686364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 8696364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 8706364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // Return early to prevent from further being notified (since nothing has 8716364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // changed) 8726364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung return; 8736364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 8746364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 8753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8766364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // Flush the cache so that we can reload new items from the service 8776364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung synchronized (mCache) { 8786364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung mCache.reset(); 8796364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 88061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 8816364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // Re-request the new metadata (only after the notification to the factory) 8826364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung updateMetaData(); 8833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8846364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // Propagate the notification back to the base adapter 8856364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung mMainQueue.post(new Runnable() { 8866364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 8876364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 8886364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung superNotifyDataSetChanged(); 8896364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 8906364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung }); 8913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 8936364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 8946364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // Note: we do not call super.notifyDataSetChanged() until the RemoteViewsFactory has had 8956364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung // a chance to update itself and return new meta data associated with the new data. 8963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 898fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen void superNotifyDataSetChanged() { 899499cb9f516062b654952d282f211bee44c31a3c2Winson Chung super.notifyDataSetChanged(); 900499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 901499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 90281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung @Override 90381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public boolean handleMessage(Message msg) { 90481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung boolean result = false; 90581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung switch (msg.what) { 90681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung case sUnbindServiceMessageType: 90781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(mContext); 90881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (mServiceConnection.isConnected()) { 90981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mgr.unbindRemoteViewsService(mAppWidgetId, mIntent); 91081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 91181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung result = true; 91281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 91381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung default: 91481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 91581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 91681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung return result; 91781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 91881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 91981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private void enqueueDeferredUnbindServiceMessage() { 92081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Remove any existing deferred-unbind messages 92181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 92281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay); 92381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 92481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 925499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean requestBindService() { 92681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Try binding the service (which will start it if it's not already running) 927499cb9f516062b654952d282f211bee44c31a3c2Winson Chung if (!mServiceConnection.isConnected()) { 92881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(mContext); 92981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mgr.bindRemoteViewsService(mAppWidgetId, mIntent, mServiceConnection.asBinder()); 930499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 931499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 932499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mServiceConnection.isConnected(); 933499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 934499cb9f516062b654952d282f211bee44c31a3c2Winson Chung} 935