RemoteViewsAdapter.java revision 335c3b681bf1c118d9bf22d1a508c87173632ec6
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 Chung 2481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager; 25499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context; 26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent; 27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler; 28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread; 29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder; 30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper; 3181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message; 32335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohenimport android.os.Parcel; 33335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohenimport android.os.Parcelable; 342625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException; 35fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log; 36335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohenimport android.util.Pair; 37a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater; 38499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View; 39181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec; 4084bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup; 41335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohenimport android.widget.RemoteViewsService.RemoteViewsFactory; 42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection; 44499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory; 45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** 47499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews 48499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views. 49499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 50499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */ 5181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback { 52fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung private static final String TAG = "RemoteViewsAdapter"; 53499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The max number of items in the cache 55b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sDefaultCacheSize = 40; 5681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The delay (in millis) to wait until attempting to unbind from a service after a request. 5781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // This ensures that we don't stay continually bound to the service and that it can be destroyed 5881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // if we need the memory elsewhere in the system. 59b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sUnbindServiceDelay = 5000; 60b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 61b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Default height for the default loading view, in case we cannot get inflate the first view 62b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen private static final int sDefaultLoadingViewHeight = 50; 63b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Type defs for controlling different messages across the main and worker message queues 6581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sDefaultMessageType = 0; 6681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceMessageType = 1; 6781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 6881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Context mContext; 6981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Intent mIntent; 7081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final int mAppWidgetId; 71a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung private LayoutInflater mLayoutInflater; 72499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private RemoteViewsAdapterServiceConnection mServiceConnection; 733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteAdapterConnectionCallback> mCallback; 743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private FixedSizeRemoteViewsCache mCache; 75b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowLowerBound; 76b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowUpperBound; 773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // A flag to determine whether we should notify data set changed after we connect 7916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mNotifyDataSetChangedAfterOnServiceConnected = false; 8016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of requested views that are to be notified when the associated RemoteViews are 823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loaded. 833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayoutRefSet mRequestedViews; 84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private HandlerThread mWorkerThread; 86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung // items may be interrupted within the normally processed queues 87499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mWorkerQueue; 88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mMainQueue; 89499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 90335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data 91335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // structures; 92335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static final HashMap<Pair<Intent.FilterComparison, Integer>, Parcel> 93335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCachedRemoteViewsCaches = new HashMap<Pair<Intent.FilterComparison, Integer>, 94335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Parcel>(); 95335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static final HashMap<Pair<Intent.FilterComparison, Integer>, Runnable> 96335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables = new HashMap<Pair<Intent.FilterComparison, Integer>, 97335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Runnable>(); 98335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static HandlerThread sCacheRemovalThread; 99335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static Handler sCacheRemovalQueue; 100335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 101335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We keep the cache around for a duration after onSaveInstanceState for use on re-inflation. 102335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this 103335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // duration, the cache is dropped. 104335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static final int REMOTE_VIEWS_CACHE_DURATION = 5000; 105335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 106335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // Used to indicate to the AdapterView that it can use this Adapter immediately after 107335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // construction (happens when we have a cached FixedSizeRemoteViewsCache). 108335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private boolean mDataReady = false; 109335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 110499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An interface for the RemoteAdapter to notify other classes when adapters 112499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * are actually connected to/disconnected from their actual services. 113499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 114499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public interface RemoteAdapterConnectionCallback { 11516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung /** 11616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung * @return whether the adapter was set or not. 11716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung */ 11816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean onRemoteAdapterConnected(); 119499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 120499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterDisconnected(); 1212148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen 1222148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen /** 1232148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not 1242148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * connected yet. 1252148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen */ 1262148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen public void deferNotifyDataSetChanged(); 127499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 128499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 129499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 130499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * The service connection that gets populated when the RemoteViewsService is 1313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * bound. This must be a static inner class to ensure that no references to the outer 1323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being 1333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * garbage collected, and would cause us to leak activities due to the caching mechanism for 1343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * FrameLayouts in the adapter). 135499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 13681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static class RemoteViewsAdapterServiceConnection extends 13781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung IRemoteViewsAdapterConnection.Stub { 13816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnected; 13916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnecting; 1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteViewsAdapter> mAdapter; 141499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private IRemoteViewsFactory mRemoteViewsFactory; 142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { 1443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); 145499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 146499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 14716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void bind(Context context, int appWidgetId, Intent intent) { 14816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mIsConnecting) { 14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 15016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 15116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.bindRemoteViewsService(appWidgetId, intent, asBinder()); 15216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = true; 15316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 15416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage()); 15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 15716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 15816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 15916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 16016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 16116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void unbind(Context context, int appWidgetId, Intent intent) { 16216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 16316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 16416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.unbindRemoteViewsService(appWidgetId, intent); 16516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 16616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 16716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage()); 16816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 16916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 17116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 17216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 17316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceConnected(IBinder service) { 174499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); 175c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 17616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any deferred unbind messages 1773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 1783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 17916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 18016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up work that we need to do for the callback to run 1813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mWorkerQueue.post(new Runnable() { 182499cb9f516062b654952d282f211bee44c31a3c2Winson Chung @Override 183499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void run() { 18416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) { 18516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Handle queued notifyDataSetChanged() if necessary 18616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.onNotifyDataSetChanged(); 18716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 1883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = 1893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mServiceConnection.getRemoteViewsFactory(); 1903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 19116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!factory.isCreated()) { 19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We only call onDataSetChanged() if this is the factory was just 19316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // create in response to this bind 19416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 19516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 1962625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 1973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error notifying factory of data set changed in " + 1983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung "onServiceConnected(): " + e.getMessage()); 1993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent anything further from being notified 2013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // (effectively nothing has changed) 2023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 2032625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 2042625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error notifying factory of data set changed in " + 2052625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen "onServiceConnected(): " + e.getMessage()); 206499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Request meta data so that we have up to date data when calling back to 2093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the remote adapter callback 21016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.updateTemporaryMetaData(); 2113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 21216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Notify the host that we've connected 21361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung adapter.mMainQueue.post(new Runnable() { 2143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 2153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 21616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (adapter.mCache) { 21716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mCache.commitTemporaryMetaData(); 21816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 21916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = 2213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mCallback.get(); 2223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 2233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterConnected(); 2243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 22816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 22916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Enqueue unbind message 23016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.enqueueDeferredUnbindServiceMessage(); 23116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = true; 23216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung }); 235499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 236499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 23716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceDisconnected() { 23816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 23916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 2403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsFactory = null; 241499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 24216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Clear the main/worker queues 2433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 2443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 245c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 24616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.post(new Runnable() { 24716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 24816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 24916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 25016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.removeMessages(sUnbindServiceMessageType); 2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 25216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); 25316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (callback != null) { 25416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung callback.onRemoteAdapterDisconnected(); 25516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 25616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 25716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 26016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized IRemoteViewsFactory getRemoteViewsFactory() { 261499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mRemoteViewsFactory; 262499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 26416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized boolean isConnected() { 26516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mIsConnected; 266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 267499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 268499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when 2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * they are loaded. 272499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 273a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class RemoteViewsFrameLayout extends FrameLayout { 2743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayout(Context context) { 2753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung super(context); 2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 277499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 278499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Updates this RemoteViewsFrameLayout depending on the view that was loaded. 2803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded 2813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * successfully. 282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void onRemoteViewsLoaded(RemoteViews view) { 28461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung try { 28561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Remove all the children of this layout first 28661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung removeAllViews(); 28761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung addView(view.apply(getContext(), this)); 28861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } catch (Exception e) { 28961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung Log.e(TAG, "Failed to apply RemoteViews."); 29061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } 2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 293499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the 2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * adapter that have not yet had their RemoteViews loaded. 2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayoutRefSet { 2993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; 300499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayoutRefSet() { 3023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); 303499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 304499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 305499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 3063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. 307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 3083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void add(int position, RemoteViewsFrameLayout layout) { 3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung LinkedList<RemoteViewsFrameLayout> refs; 3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create the list if necessary 3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = mReferences.get(pos); 3153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = new LinkedList<RemoteViewsFrameLayout>(); 3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.put(pos, refs); 318499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add the references to the list 3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.add(layout); 322499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 323499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that 3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * the associated RemoteViews has loaded. 3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 328a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) { 32961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung if (view == null) return; 33061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 3313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the references for that position of the newly loaded RemoteViews 3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); 3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (final RemoteViewsFrameLayout ref : refs) { 3363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ref.onRemoteViewsLoaded(view); 3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.clear(); 339499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Remove this set from the original mapping 3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.remove(pos); 342499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 343499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 344499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. 3473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void clear() { 3493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We currently just clear the references, and leave all the previous layouts returned 3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // in their default state of the loading view. 3513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.clear(); 352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 354499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with the cache in it's current state. 3573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 358335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static class RemoteViewsMetaData implements Parcelable { 3593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count; 3603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount; 3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds; 3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Used to determine how to construct loading views. If a loading view is not specified 3643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by the user, then we try and load the first view, and use its height as the height for 3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the default loading view. 3663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mUserLoadingView; 3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mFirstView; 3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mFirstViewHeight; 3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A mapping from type id to a set of unique type ids 37116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>(); 3723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData() { 3743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung reset(); 375499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 376499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 377335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public RemoteViewsMetaData(Parcel src) { 378335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen count = src.readInt(); 379335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen viewTypeCount = src.readInt(); 380335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen hasStableIds = src.readInt() == 0 ? false : true; 381335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mFirstViewHeight = src.readInt(); 382335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (src.readInt() != 0) { 383335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mUserLoadingView = new RemoteViews(src); 384335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 385335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (src.readInt() != 0) { 386335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mFirstView = new RemoteViews(src); 387335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 388335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen int count = src.readInt(); 389335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen for (int i = 0; i < count; i++) { 390335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mTypeIdIndexMap.put(src.readInt(), src.readInt()); 391335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 392335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 393335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 394335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 395335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void writeToParcel(Parcel dest, int flags) { 396335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(count); 397335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(viewTypeCount); 398335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(hasStableIds ? 1 : 0); 399335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mFirstViewHeight); 400335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mUserLoadingView != null ? 1 : 0); 401335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (mUserLoadingView != null) { 402335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mUserLoadingView.writeToParcel(dest, flags); 403335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 404335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mFirstView != null ? 1 : 0); 405335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (mFirstView != null) { 406335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mFirstView.writeToParcel(dest, flags); 407335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 408335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 409335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen int count = mTypeIdIndexMap.size(); 410335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(count); 411335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen for (Integer key: mTypeIdIndexMap.keySet()) { 412335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(key); 413335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mTypeIdIndexMap.get(key)); 414335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 415335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 416335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 417335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 418335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public int describeContents() { 419335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen return 0; 420335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 421335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 42216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void set(RemoteViewsMetaData d) { 42316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (d) { 42416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung count = d.count; 42516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung viewTypeCount = d.viewTypeCount; 42616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasStableIds = d.hasStableIds; 42716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView); 42816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 42916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 43016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 4313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 4323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = 0; 43316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 4343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by default there is at least one dummy view type 4353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung viewTypeCount = 1; 4363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung hasStableIds = true; 4373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = null; 4383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 4393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = 0; 44016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTypeIdIndexMap.clear(); 441499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 442499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { 4443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = loadingView; 4453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (firstView != null) { 4463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = firstView; 4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = -1; 448499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 449499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 450499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getMappedViewType(int typeId) { 4523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mTypeIdIndexMap.containsKey(typeId)) { 4533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mTypeIdIndexMap.get(typeId); 4543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 4553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We +1 because the loading view always has view type id of 0 4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int incrementalTypeId = mTypeIdIndexMap.size() + 1; 4573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap.put(typeId, incrementalTypeId); 4583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return incrementalTypeId; 4596394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4606394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4616394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 462a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public boolean isViewTypeInRange(int typeId) { 463a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int mappedType = getMappedViewType(typeId); 464a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (mappedType >= viewTypeCount) { 465a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return false; 466a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 467a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return true; 468a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 469a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 470a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 4713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayout createLoadingView(int position, View convertView, 472a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen ViewGroup parent, Object lock, LayoutInflater layoutInflater) { 4733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create and return a new FrameLayout, and setup the references for this position 4743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Context context = parent.getContext(); 4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); 4763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create a new loading view 478a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (lock) { 479b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen boolean customLoadingViewAvailable = false; 480b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mUserLoadingView != null) { 482b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Try to inflate user-specified loading view 483b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 484b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View loadingView = mUserLoadingView.apply(parent.getContext(), parent); 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 { 499b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View firstView = mFirstView.apply(parent.getContext(), parent); 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 */ 532335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static class RemoteViewsIndexMetaData implements Parcelable { 5333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId; 5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId; 535b90a91c633e99d4559095184af27d1416541d3c0Winson Chung boolean isRequested; 536499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 537b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) { 538b90a91c633e99d4559095184af27d1416541d3c0Winson Chung set(v, itemId, requested); 539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 540499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 541335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public RemoteViewsIndexMetaData(Parcel src) { 542335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen typeId = src.readInt(); 543335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen itemId = src.readLong(); 544335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 545335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 546335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 547335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void writeToParcel(Parcel dest, int flags) { 548335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(typeId); 549335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeLong(itemId); 550335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 551335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 552335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 553335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public int describeContents() { 554335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen return 0; 555335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 556335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 557b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public void set(RemoteViews v, long id, boolean requested) { 5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = id; 559a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (v != null) { 5603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = v.getLayoutId(); 561a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = 0; 563a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 564b90a91c633e99d4559095184af27d1416541d3c0Winson Chung isRequested = requested; 5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 566335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 567335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 5683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 569499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 5713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * 5723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 573335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen private static class FixedSizeRemoteViewsCache implements Parcelable { 5743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final String TAG = "FixedSizeRemoteViewsCache"; 5753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The meta data related to all the RemoteViews, ie. count, is stable, etc. 5774c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // The meta data objects are made final so that they can be locked on independently 5784c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in 5794c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // the order mTemporaryMetaData followed by mMetaData. 5804c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mMetaData; 5814c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mTemporaryMetaData; 5823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be 5843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // greater than or equal to the set of RemoteViews. 5853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: The reason that we keep this separate from the RemoteViews cache below is that this 5863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we still need to be able to access the mapping of position to meta data, without keeping 5873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. 5883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // memory and size, but this metadata cache will retain information until the data at the 5893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). 5903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; 5913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses 5933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // too much memory. 5943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViews> mIndexRemoteViews; 5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices that have been explicitly requested by the collection view 5973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mRequestedIndices; 5983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 599b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // We keep a reference of the last requested index to determine which item to prune the 600b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest items from when we hit the memory limit 601b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private int mLastRequestedIndex; 602b90a91c633e99d4559095184af27d1416541d3c0Winson Chung 6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices to load, including those explicitly requested, as well as those 6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // determined by the preloading algorithm to be prefetched 6053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mLoadIndices; 6063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The lower and upper bounds of the preloaded range 6083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadLowerBound; 6093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadUpperBound; 6103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The bounds of this fixed cache, we will try and fill as many items into the cache up to 6123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the maxCount number of items, or the maxSize memory usage. 6133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The maxCountSlack is used to determine if a new position in the cache to be loaded is 6143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // sufficiently ouside the old set, prompting a shifting of the "window" of items to be 6153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloaded. 6163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCount; 6173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCountSlack; 6183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final float sMaxCountSlackPercent = 0.75f; 619b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024; 6203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public FixedSizeRemoteViewsCache(int maxCacheSize) { 6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCount = maxCacheSize; 6233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); 6243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMetaData = new RemoteViewsMetaData(); 62716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTemporaryMetaData = new RemoteViewsMetaData(); 6283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 6293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 6303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices = new HashSet<Integer>(); 631b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 6323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices = new HashSet<Integer>(); 6333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 634499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 635335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public FixedSizeRemoteViewsCache(Parcel src) { 636335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mMaxCount = src.readInt(); 637335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mMaxCountSlack = src.readInt(); 638335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mPreloadLowerBound = src.readInt(); 639335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mPreloadUpperBound = src.readInt(); 640335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mMetaData = new RemoteViewsMetaData(src); 641335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen int count = src.readInt(); 642335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 643335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen for (int i = 0; i < count; i++) { 644335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mIndexMetaData.put(src.readInt(), new RemoteViewsIndexMetaData(src)); 645335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 646335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen count = src.readInt(); 647335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 648335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen for (int i = 0; i < count; i++) { 649335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mIndexRemoteViews.put(src.readInt(), new RemoteViews(src)); 650335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 651335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 652335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mTemporaryMetaData = new RemoteViewsMetaData(); 653335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mRequestedIndices = new HashSet<Integer>(); 654335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mLastRequestedIndex = -1; 655335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mLoadIndices = new HashSet<Integer>(); 656335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 657335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 658335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 659335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void writeToParcel(Parcel dest, int flags) { 660335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mMaxCount); 661335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mMaxCountSlack); 662335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mPreloadLowerBound); 663335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(mPreloadUpperBound); 664335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mMetaData.writeToParcel(dest, 0); 665335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 666335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // We write the index data and cache 667335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen int count = mIndexMetaData.size(); 668335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(count); 669335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen for (Integer key: mIndexMetaData.keySet()) { 670335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(key); 671335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mIndexMetaData.get(key).writeToParcel(dest, flags); 672335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 673335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen count = mIndexRemoteViews.size(); 674335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(count); 675335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen for (Integer key: mIndexRemoteViews.keySet()) { 676335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen dest.writeInt(key); 677335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mIndexRemoteViews.get(key).writeToParcel(dest, flags); 678335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 679335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 680335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 681335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 682335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public int describeContents() { 683335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen return 0; 684335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 685335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 686b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public void insert(int position, RemoteViews v, long itemId, boolean isRequested) { 6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the count 6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.size() >= mMaxCount) { 6893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(position)); 690499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 691499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the available memory size constraints 693b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position; 694b90a91c633e99d4559095184af27d1416541d3c0Winson Chung while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) { 6953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: This is currently the most naive mechanism for deciding what to prune when 6963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we hit the memory limit. In the future, we may want to calculate which index to 6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // remove based on both its position as well as it's current memory usage, as well 6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // as whether it was directly requested vs. whether it was preloaded by our caching 6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // mechanism. 700b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition)); 701499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Update the metadata cache 7043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); 706b90a91c633e99d4559095184af27d1416541d3c0Winson Chung metaData.set(v, itemId, isRequested); 7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 708b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId, isRequested)); 709499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.put(position, v); 7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 712499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData getMetaData() { 7143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mMetaData; 7153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 71616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public RemoteViewsMetaData getTemporaryMetaData() { 71716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mTemporaryMetaData; 71816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 7193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViews getRemoteViewsAt(int position) { 7203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.containsKey(position)) { 7213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.get(position); 7223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 723499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 724499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData getMetaDataAt(int position) { 7263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 7273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.get(position); 728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return null; 7303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 731499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 73216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void commitTemporaryMetaData() { 73316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mTemporaryMetaData) { 73416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mMetaData) { 73516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMetaData.set(mTemporaryMetaData); 73616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 73716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 73816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 73916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 7403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getRemoteViewsBitmapMemoryUsage() { 7413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Calculate the memory usage of all the RemoteViews bitmaps being cached 7423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mem = 0; 7433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (Integer i : mIndexRemoteViews.keySet()) { 7443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews v = mIndexRemoteViews.get(i); 745aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung if (v != null) { 7465d20064651b9947a4573c9a0eefec90f66eb1b59Adam Cohen mem += v.estimateMemoryUsage(); 747aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung } 748499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mem; 750499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getFarthestPositionFrom(int pos) { 7523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Find the index farthest away and remove that 7533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDist = 0; 7543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDistIndex = -1; 755b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int maxDistNonRequested = 0; 756b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int maxDistIndexNonRequested = -1; 7573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i : mIndexRemoteViews.keySet()) { 7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int dist = Math.abs(i-pos); 759b90a91c633e99d4559095184af27d1416541d3c0Winson Chung if (dist > maxDistNonRequested && !mIndexMetaData.get(i).isRequested) { 760b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDistNonRequested/maxDistIndexNonRequested will store the index of the 761b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest non-requested position 762b90a91c633e99d4559095184af27d1416541d3c0Winson Chung maxDistIndexNonRequested = i; 763b90a91c633e99d4559095184af27d1416541d3c0Winson Chung maxDistNonRequested = dist; 764b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 76535fbe2a5923d45ebcdfb3ad74efd1089a05e8737Adam Cohen if (dist >= maxDist) { 766b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDist/maxDistIndex will store the index of the farthest position 767b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // regardless of whether it was directly requested or not 7683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDistIndex = i; 7693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDist = dist; 770c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 771c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 772b90a91c633e99d4559095184af27d1416541d3c0Winson Chung if (maxDistIndexNonRequested > -1) { 773b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return maxDistIndexNonRequested; 774b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 7753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return maxDistIndex; 776c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 777c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 7783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queueRequestedPositionToLoad(int position) { 779b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = position; 7803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.add(position); 7823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(position); 783499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 784499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 78516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) { 7863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Check if we need to preload any items 7873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { 7883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; 7893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (Math.abs(position - center) < mMaxCountSlack) { 79016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return false; 791499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 792499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 793499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = 0; 7953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mMetaData) { 7963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = mMetaData.count; 797499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 7983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 8003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the requested indices 8023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.addAll(mRequestedIndices); 8033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the preload indices 8053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int halfMaxCount = mMaxCount / 2; 8063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = position - halfMaxCount; 8073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = position + halfMaxCount; 8083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveLowerBound = Math.max(0, mPreloadLowerBound); 8093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); 8103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { 8113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(i); 8123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 813499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 8143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // But remove all the indices that have already been loaded and are cached 8153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.removeAll(mIndexRemoteViews.keySet()); 816499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 81716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return true; 818499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 819b90a91c633e99d4559095184af27d1416541d3c0Winson Chung /** Returns the next index to load, and whether that index was directly requested or not */ 820b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public int[] getNextIndexToLoad() { 8213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We try and prioritize items that have been requested directly, instead 8223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // of items that are loaded as a result of the caching mechanism 8233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 8243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Prioritize requested indices to be loaded first 8253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mRequestedIndices.isEmpty()) { 8263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mRequestedIndices.iterator().next(); 8273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.remove(i); 8283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 829b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 1}; 8303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 831499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 8323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, preload other indices as necessary 8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mLoadIndices.isEmpty()) { 8343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mLoadIndices.iterator().next(); 8353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 836b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 0}; 8373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 838499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 839b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{-1, 0}; 840c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 8413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 842c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 8433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsRemoteViewAt(int position) { 8443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.containsKey(position); 8453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsMetaDataAt(int position) { 8473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.containsKey(position); 8483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 849499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 8503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 85161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Note: We do not try and reset the meta data, since that information is still used by 85261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // collection views to validate it's own contents (and will be re-requested if the data 85361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // is invalidated through the notifyDataSetChanged() flow). 85461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 8553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 8563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 857b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 8583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.clear(); 8593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.clear(); 8603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 8613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.clear(); 8623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 863499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 864499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 865499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 866499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 867499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { 868499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mContext = context; 869499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mIntent = intent; 87081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); 871a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung mLayoutInflater = LayoutInflater.from(context); 8729b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung if (mIntent == null) { 8739b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung throw new IllegalArgumentException("Non-null Intent must be specified."); 8749b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 8753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews = new RemoteViewsFrameLayoutRefSet(); 876499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 87781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Strip the previously injected app widget id from service intent 87881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { 87981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); 88081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 88181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 88281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the worker thread 883499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); 884499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread.start(); 885499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerQueue = new Handler(mWorkerThread.getLooper()); 88681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue = new Handler(Looper.myLooper(), this); 887499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 888335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCacheRemovalThread == null) { 889335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner"); 890335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalThread.start(); 891335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper()); 892335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 893335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 89481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the cache and the service connection on startup 8953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); 8963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mServiceConnection = new RemoteViewsAdapterServiceConnection(this); 897335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 898335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, Integer> 899335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen (new Intent.FilterComparison(mIntent), mAppWidgetId); 900335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 901335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(sCachedRemoteViewsCaches) { 902335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCachedRemoteViewsCaches.containsKey(key)) { 903335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Parcel src = sCachedRemoteViewsCaches.get(key); 904335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen src.setDataPosition(0); 905335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mCache = new FixedSizeRemoteViewsCache(src); 906335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mDataReady = true; 907335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } else { 908335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); 909335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen requestBindService(); 910335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 911335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 912499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 913499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 914fc442bdea14289656ef1f537103578eb71faf473Jeff Brown @Override 915fc442bdea14289656ef1f537103578eb71faf473Jeff Brown protected void finalize() throws Throwable { 916fc442bdea14289656ef1f537103578eb71faf473Jeff Brown try { 917fc442bdea14289656ef1f537103578eb71faf473Jeff Brown if (mWorkerThread != null) { 918fc442bdea14289656ef1f537103578eb71faf473Jeff Brown mWorkerThread.quit(); 919fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 920fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } finally { 921fc442bdea14289656ef1f537103578eb71faf473Jeff Brown super.finalize(); 922fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 923fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 924fc442bdea14289656ef1f537103578eb71faf473Jeff Brown 925335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public boolean isDataReady() { 926335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen return mDataReady; 927335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 928335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 929335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void saveRemoteViewsCache() { 930335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen final Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, 931335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Integer> (new Intent.FilterComparison(mIntent), mAppWidgetId); 932335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 933335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(sCachedRemoteViewsCaches) { 934335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen // If we already have a remove runnable posted for this key, remove it. 935335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { 936335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key)); 937335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.remove(key); 938335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 939335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 940335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Parcel p = Parcel.obtain(); 941335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized(mCache) { 942335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen mCache.writeToParcel(p, 0); 943335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 944335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCachedRemoteViewsCaches.put(key, p); 945335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen Runnable r = new Runnable() { 946335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen @Override 947335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen public void run() { 948335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen synchronized (sCachedRemoteViewsCaches) { 949335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sCachedRemoteViewsCaches.containsKey(key)) { 950335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCachedRemoteViewsCaches.remove(key); 951335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 952335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { 953335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.remove(key); 954335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 955335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 956335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 957335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen }; 958335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sRemoteViewsCacheRemoveRunnables.put(key, r); 959335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION); 960335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 961335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen } 962335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen 9633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void loadNextIndexInBackground() { 9643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mWorkerQueue.post(new Runnable() { 9653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 9663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 96716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mServiceConnection.isConnected()) { 96816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Get the next index to load 96916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int position = -1; 970b90a91c633e99d4559095184af27d1416541d3c0Winson Chung boolean isRequested = false; 97116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 972b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int[] res = mCache.getNextIndexToLoad(); 973b90a91c633e99d4559095184af27d1416541d3c0Winson Chung position = res[0]; 974b90a91c633e99d4559095184af27d1416541d3c0Winson Chung isRequested = res[1] > 0; 97516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 97616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (position > -1) { 97716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item, and notify any existing RemoteViewsFrameLayouts 978b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen updateRemoteViews(position, isRequested, true); 9793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 98016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up for the next one to load 98116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 98216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 98316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // No more items to load, so queue unbind 98416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 98516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 9863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 9893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 99116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void processException(String method, Exception e) { 99216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage()); 9933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 99416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we encounter a crash when updating, we should reset the metadata & cache and trigger 99516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // a notifyDataSetChanged to update the widget accordingly 99616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 99716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 99816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung metaData.reset(); 99916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 100016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 100116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 100216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 100316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 100416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 100516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 100616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 10073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 100816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 100916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 101016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 101116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void updateTemporaryMetaData() { 101216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 101316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 101416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 101516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // get the properties/first view (so that we can use it to 101616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // measure our dummy views) 101716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasStableIds = factory.hasStableIds(); 101816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int viewTypeCount = factory.getViewTypeCount(); 101916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int count = factory.getCount(); 102016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews loadingView = factory.getLoadingView(); 102116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews firstView = null; 102216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if ((count > 0) && (loadingView == null)) { 102316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung firstView = factory.getViewAt(0); 102416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 102516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData(); 102616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (tmpMetaData) { 102716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.hasStableIds = hasStableIds; 102816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We +1 because the base view type is the loading view 102916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.viewTypeCount = viewTypeCount + 1; 103016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.count = count; 103116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.setLoadingViewTemplates(loadingView, firstView); 103216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 10332625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch(RemoteException e) { 103416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung processException("updateMetaData", e); 1035fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen } catch(RuntimeException e) { 1036fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen processException("updateMetaData", e); 10373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1040b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private void updateRemoteViews(final int position, boolean isRequested, boolean 1041b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen notifyWhenLoaded) { 104216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 104316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 104416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item information from the remote service 104516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews remoteViews = null; 104616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung long itemId = 0; 104716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 104816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung remoteViews = factory.getViewAt(position); 104916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung itemId = factory.getItemId(position); 10502625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 105116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 105216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 105316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent additional work in re-centering the view cache, and 105416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // swapping from the loading view 105516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 10562625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 10572625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 10582625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 105916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 10603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 106116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (remoteViews == null) { 106216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If a null view was returned, we break early to prevent it from getting 106316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // into our cache and causing problems later. The effect is that the child at this 106416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // position will remain as a loading view until it is updated. 106516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + 106616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung "returned from RemoteViewsFactory."); 106716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 106816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 1069a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 1070a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int layoutId = remoteViews.getLayoutId(); 1071a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen RemoteViewsMetaData metaData = mCache.getMetaData(); 1072a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen boolean viewTypeInRange; 1073a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (metaData) { 1074a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen viewTypeInRange = metaData.isViewTypeInRange(layoutId); 1075a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 107616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 1077a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (viewTypeInRange) { 1078a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Cache the RemoteViews we loaded 1079a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache.insert(position, remoteViews, itemId, isRequested); 1080a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 1081a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Notify all the views that we have previously returned for this index that 1082a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // there is new data for it. 1083a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen final RemoteViews rv = remoteViews; 1084a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (notifyWhenLoaded) { 1085a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mMainQueue.post(new Runnable() { 1086a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen @Override 1087a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void run() { 1088a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mRequestedViews.notifyOnRemoteViewsLoaded(position, rv); 1089a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 1090a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen }); 1091a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 1092a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 1093a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // We need to log an error here, as the the view type count specified by the 1094a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // factory is less than the number of view types returned. We don't return this 1095a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // view to the AdapterView, as this will cause an exception in the hosting process, 1096a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // which contains the associated AdapterView. 1097a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " + 1098a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen " indicated by getViewTypeCount() "); 1099b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 11003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1101499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 11039b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung public Intent getRemoteViewsServiceIntent() { 11049b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung return mIntent; 11059b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 11069b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 1107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getCount() { 11083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 11093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 11103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.count; 11113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1112499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1113499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1114499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public Object getItem(int position) { 11153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Disallow arbitrary object to be associated with an item for the time being 1116499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 1117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1118499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1119499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public long getItemId(int position) { 11203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 11213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 11223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mCache.getMetaDataAt(position).itemId; 11233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 11253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1126499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1127499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1128499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getItemViewType(int position) { 11293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = 0; 11303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 11313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 11323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = mCache.getMetaDataAt(position).typeId; 11333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 11343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 11353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 11383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 11393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 11403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.getMappedViewType(typeId); 11413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 11443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 11453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Returns the item type id for the specified convert view. Returns -1 if the convert view 11463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * is invalid. 11473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 11483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getConvertViewTypeId(View convertView) { 11493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = -1; 1150a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (convertView != null) { 1151a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId); 1152a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (tag != null) { 1153a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen typeId = (Integer) tag; 1154a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen } 11553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return typeId; 1157499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1158499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1159b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen /** 1160b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * This method allows an AdapterView using this Adapter to provide information about which 1161b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * views are currently being displayed. This allows for certain optimizations and preloading 1162b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * which wouldn't otherwise be possible. 1163b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen */ 1164b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen public void setVisibleRangeHint(int lowerBound, int upperBound) { 1165b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowLowerBound = lowerBound; 1166b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowUpperBound = upperBound; 1167b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1168b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 1169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public View getView(int position, View convertView, ViewGroup parent) { 117016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // "Request" an index so that we can queue it for loading, initiate subsequent 117116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // preloading, etc. 117216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 117316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isInCache = mCache.containsRemoteViewAt(position); 117416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isConnected = mServiceConnection.isConnected(); 117516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasNewItems = false; 117616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 11777ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung if (!isInCache && !isConnected) { 117816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Requesting bind service will trigger a super.notifyDataSetChanged(), which will 117916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // in turn trigger another request to getView() 118016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 118116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 11823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up other indices to be preloaded based on this position 118316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position); 118416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 118516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 118616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (isInCache) { 11873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View convertViewChild = null; 11883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int convertViewTypeId = 0; 1189181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen RemoteViewsFrameLayout layout = null; 1190181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen 1191181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (convertView instanceof RemoteViewsFrameLayout) { 1192181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen layout = (RemoteViewsFrameLayout) convertView; 11933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewChild = layout.getChildAt(0); 11943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewTypeId = getConvertViewTypeId(convertViewChild); 11953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 11973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Second, we try and retrieve the RemoteViews from the cache, returning a loading 11983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // view and queueing it to be loaded if it has not already been loaded. 119916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Context context = parent.getContext(); 120016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews rv = mCache.getRemoteViewsAt(position); 1201aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position); 1202aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen indexMetaData.isRequested = true; 1203aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen int typeId = indexMetaData.typeId; 120416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1205b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 1206b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Reuse the convert view where possible 1207b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (layout != null) { 1208b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (convertViewTypeId == typeId) { 1209b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen rv.reapply(context, convertViewChild); 1210b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1211b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1212b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.removeAllViews(); 1213b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } else { 1214b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout = new RemoteViewsFrameLayout(context); 12153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 12163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1217b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Otherwise, create a new view to be returned 1218b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View newView = rv.apply(context, parent); 1219b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen newView.setTagInternal(com.android.internal.R.id.rowTypeId, 1220b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(typeId)); 1221b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(newView); 1222b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1223b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1224b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e){ 1225b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // We have to make sure that we successfully inflated the RemoteViews, if not 1226b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // we return the loading view instead. 1227b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" + 1228b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "loading view instead" + e); 1229b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1230b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen RemoteViewsFrameLayout loadingView = null; 1231b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen final RemoteViewsMetaData metaData = mCache.getMetaData(); 1232b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen synchronized (metaData) { 1233a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1234a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache, mLayoutInflater); 1235b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1236b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return loadingView; 1237b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } finally { 1238b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (hasNewItems) loadNextIndexInBackground(); 1239b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 124016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 124116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If the cache does not have the RemoteViews at this position, then create a 124216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // loading view and queue the actual position to be loaded in the background 124316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViewsFrameLayout loadingView = null; 124416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 124516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 1246a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1247a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache, mLayoutInflater); 12483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 124916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 125016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mRequestedViews.add(position, loadingView); 125116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.queueRequestedPositionToLoad(position); 125216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 125316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 125416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return loadingView; 12553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 12563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1257499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getViewTypeCount() { 12603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 12613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 12623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.viewTypeCount; 12633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean hasStableIds() { 12673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 12683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 12693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.hasStableIds; 12703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1271499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1272499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1273499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isEmpty() { 1274499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return getCount() <= 0; 1275499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1276499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 127716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void onNotifyDataSetChanged() { 127816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Complete the actual notifyDataSetChanged() call initiated earlier 127916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 128016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 128116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 12822625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 128316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 128416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 128516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent from further being notified (since nothing has 128616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // changed) 128716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 12882625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 12892625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 12902625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 129116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 129216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 129316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Flush the cache so that we can reload new items from the service 129416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 129516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 129616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 129716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 129816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Re-request the new metadata (only after the notification to the factory) 129916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung updateTemporaryMetaData(); 13004c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen int newCount; 13014c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen synchronized(mCache.getTemporaryMetaData()) { 13024c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen newCount = mCache.getTemporaryMetaData().count; 13034c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 130416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1305b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // Pre-load (our best guess of) the views which are currently visible in the AdapterView. 1306b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // This mitigates flashing and flickering of loading views when a widget notifies that 1307b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // its data has changed. 1308b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen for (int i = mVisibleWindowLowerBound; i <= mVisibleWindowUpperBound; i++) { 13094c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // Because temporary meta data is only ever modified from this thread (ie. 13104c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // mWorkerThread), it is safe to assume that count is a valid representation. 13114c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen if (i < newCount) { 13124c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen updateRemoteViews(i, false, false); 13134c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 1314b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1315b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 131616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Propagate the notification back to the base adapter 131716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 13186364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 13196364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 13206364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung synchronized (mCache) { 132116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.commitTemporaryMetaData(); 13226364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 132361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 132416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 132516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 13263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 13273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 13286364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 132916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Reset the notify flagflag 133016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = false; 133116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 133216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 133316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void notifyDataSetChanged() { 133416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 133516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 133616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 133716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we are not connected, queue up the notifyDataSetChanged to be handled when we do 133816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // connect 133916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mServiceConnection.isConnected()) { 134016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mNotifyDataSetChangedAfterOnServiceConnected) { 134116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 134216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 134316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 134416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = true; 134516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 134616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 134716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 134816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 134916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mWorkerQueue.post(new Runnable() { 135016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 135116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 135216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung onNotifyDataSetChanged(); 135316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 135416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 13553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 13563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1357fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen void superNotifyDataSetChanged() { 1358499cb9f516062b654952d282f211bee44c31a3c2Winson Chung super.notifyDataSetChanged(); 1359499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1360499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 136181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung @Override 136281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public boolean handleMessage(Message msg) { 136381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung boolean result = false; 136481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung switch (msg.what) { 136581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung case sUnbindServiceMessageType: 136681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (mServiceConnection.isConnected()) { 136716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.unbind(mContext, mAppWidgetId, mIntent); 136881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 136981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung result = true; 137081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 137181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung default: 137281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 137381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 137481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung return result; 137581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 137681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 137781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private void enqueueDeferredUnbindServiceMessage() { 137881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Remove any existing deferred-unbind messages 137981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 138081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay); 138181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 138281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 1383499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean requestBindService() { 138481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Try binding the service (which will start it if it's not already running) 1385499cb9f516062b654952d282f211bee44c31a3c2Winson Chung if (!mServiceConnection.isConnected()) { 138616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.bind(mContext, mAppWidgetId, mIntent); 1387499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1388499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 138916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any existing deferred-unbind messages 139016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 1391499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mServiceConnection.isConnected(); 1392499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung} 1394