RemoteViewsAdapter.java revision a5a06873d152d16aa47ca9be8501417280b9c9cb
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; 322625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException; 33fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log; 34a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater; 35499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View; 36181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec; 3784bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup; 38499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection; 40499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory; 41499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** 43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews 44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views. 45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */ 4781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback { 48fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung private static final String TAG = "RemoteViewsAdapter"; 49499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The max number of items in the cache 51b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sDefaultCacheSize = 40; 5281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The delay (in millis) to wait until attempting to unbind from a service after a request. 5381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // This ensures that we don't stay continually bound to the service and that it can be destroyed 5481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // if we need the memory elsewhere in the system. 55b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sUnbindServiceDelay = 5000; 56b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 57b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Default height for the default loading view, in case we cannot get inflate the first view 58b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen private static final int sDefaultLoadingViewHeight = 50; 59b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 6081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Type defs for controlling different messages across the main and worker message queues 6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sDefaultMessageType = 0; 6281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceMessageType = 1; 6381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Context mContext; 6581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Intent mIntent; 6681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final int mAppWidgetId; 67a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung private LayoutInflater mLayoutInflater; 68499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private RemoteViewsAdapterServiceConnection mServiceConnection; 693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteAdapterConnectionCallback> mCallback; 703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private FixedSizeRemoteViewsCache mCache; 71b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowLowerBound; 72b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private int mVisibleWindowUpperBound; 733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 7416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // A flag to determine whether we should notify data set changed after we connect 7516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mNotifyDataSetChangedAfterOnServiceConnected = false; 7616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of requested views that are to be notified when the associated RemoteViews are 783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loaded. 793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayoutRefSet mRequestedViews; 80499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private HandlerThread mWorkerThread; 82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung // items may be interrupted within the normally processed queues 83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mWorkerQueue; 84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mMainQueue; 85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 87499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An interface for the RemoteAdapter to notify other classes when adapters 88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * are actually connected to/disconnected from their actual services. 89499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 90499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public interface RemoteAdapterConnectionCallback { 9116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung /** 9216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung * @return whether the adapter was set or not. 9316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung */ 9416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean onRemoteAdapterConnected(); 95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterDisconnected(); 972148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen 982148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen /** 992148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not 1002148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen * connected yet. 1012148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen */ 1022148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen public void deferNotifyDataSetChanged(); 103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 104499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * The service connection that gets populated when the RemoteViewsService is 1073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * bound. This must be a static inner class to ensure that no references to the outer 1083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being 1093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * garbage collected, and would cause us to leak activities due to the caching mechanism for 1103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * FrameLayouts in the adapter). 111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 11281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static class RemoteViewsAdapterServiceConnection extends 11381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung IRemoteViewsAdapterConnection.Stub { 11416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnected; 11516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnecting; 1163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteViewsAdapter> mAdapter; 117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private IRemoteViewsFactory mRemoteViewsFactory; 118499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { 1203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); 121499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 122499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 12316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void bind(Context context, int appWidgetId, Intent intent) { 12416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mIsConnecting) { 12516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 12616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 12716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.bindRemoteViewsService(appWidgetId, intent, asBinder()); 12816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = true; 12916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 13016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage()); 13116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 13216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 13316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 13416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 13516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 13616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 13716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void unbind(Context context, int appWidgetId, Intent intent) { 13816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 13916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 14016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.unbindRemoteViewsService(appWidgetId, intent); 14116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 14216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 14316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage()); 14416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 14516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 14616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 14716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 14816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceConnected(IBinder service) { 150499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); 151c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 15216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any deferred unbind messages 1533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 1543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up work that we need to do for the callback to run 1573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mWorkerQueue.post(new Runnable() { 158499cb9f516062b654952d282f211bee44c31a3c2Winson Chung @Override 159499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void run() { 16016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) { 16116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Handle queued notifyDataSetChanged() if necessary 16216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.onNotifyDataSetChanged(); 16316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 1643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = 1653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mServiceConnection.getRemoteViewsFactory(); 1663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 16716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!factory.isCreated()) { 16816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We only call onDataSetChanged() if this is the factory was just 16916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // create in response to this bind 17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 17116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 1722625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 1733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error notifying factory of data set changed in " + 1743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung "onServiceConnected(): " + e.getMessage()); 1753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent anything further from being notified 1773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // (effectively nothing has changed) 1783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 1792625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 1802625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error notifying factory of data set changed in " + 1812625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen "onServiceConnected(): " + e.getMessage()); 182499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Request meta data so that we have up to date data when calling back to 1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the remote adapter callback 18616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.updateTemporaryMetaData(); 1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 18816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Notify the host that we've connected 18961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung adapter.mMainQueue.post(new Runnable() { 1903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 1913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (adapter.mCache) { 19316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mCache.commitTemporaryMetaData(); 19416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 19516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = 1973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mCallback.get(); 1983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 1993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterConnected(); 2003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 2033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 20416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 20516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Enqueue unbind message 20616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.enqueueDeferredUnbindServiceMessage(); 20716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = true; 20816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 209499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 210499cb9f516062b654952d282f211bee44c31a3c2Winson Chung }); 211499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 212499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 21316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceDisconnected() { 21416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 21516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 2163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsFactory = null; 217499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 21816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Clear the main/worker queues 2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 221c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 22216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.post(new Runnable() { 22316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 22416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 22516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 22616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.removeMessages(sUnbindServiceMessageType); 2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 22816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); 22916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (callback != null) { 23016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung callback.onRemoteAdapterDisconnected(); 23116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 23216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 23316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 235499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 23616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized IRemoteViewsFactory getRemoteViewsFactory() { 237499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mRemoteViewsFactory; 238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 239499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 24016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized boolean isConnected() { 24116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mIsConnected; 242499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 243499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 244499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 245499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when 2473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * they are loaded. 248499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 249a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class RemoteViewsFrameLayout extends FrameLayout { 2503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayout(Context context) { 2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung super(context); 2523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Updates this RemoteViewsFrameLayout depending on the view that was loaded. 2563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded 2573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * successfully. 258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void onRemoteViewsLoaded(RemoteViews view) { 26061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung try { 26161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Remove all the children of this layout first 26261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung removeAllViews(); 26361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung addView(view.apply(getContext(), this)); 26461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } catch (Exception e) { 26561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung Log.e(TAG, "Failed to apply RemoteViews."); 26661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } 2673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the 2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * adapter that have not yet had their RemoteViews loaded. 2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayoutRefSet { 2753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; 276499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayoutRefSet() { 2783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); 279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 280499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 281499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. 283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void add(int position, RemoteViewsFrameLayout layout) { 2853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 2863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung LinkedList<RemoteViewsFrameLayout> refs; 2873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create the list if necessary 2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 2903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = mReferences.get(pos); 2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = new LinkedList<RemoteViewsFrameLayout>(); 2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.put(pos, refs); 294499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add the references to the list 2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.add(layout); 298499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that 3023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * the associated RemoteViews has loaded. 3033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 304a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) { 30561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung if (view == null) return; 30661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 3073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 3083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the references for that position of the newly loaded RemoteViews 3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); 3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (final RemoteViewsFrameLayout ref : refs) { 3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ref.onRemoteViewsLoaded(view); 3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.clear(); 315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Remove this set from the original mapping 3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.remove(pos); 318499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 319499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 320499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. 3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void clear() { 3253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We currently just clear the references, and leave all the previous layouts returned 3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // in their default state of the loading view. 3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.clear(); 328499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 330499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with the cache in it's current state. 3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 334a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class RemoteViewsMetaData { 3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count; 3363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount; 3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds; 3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Used to determine how to construct loading views. If a loading view is not specified 3403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by the user, then we try and load the first view, and use its height as the height for 3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the default loading view. 3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mUserLoadingView; 3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mFirstView; 3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mFirstViewHeight; 3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A mapping from type id to a set of unique type ids 34716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>(); 3483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData() { 3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung reset(); 351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 35316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void set(RemoteViewsMetaData d) { 35416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (d) { 35516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung count = d.count; 35616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung viewTypeCount = d.viewTypeCount; 35716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasStableIds = d.hasStableIds; 35816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView); 35916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 36016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 36116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = 0; 36416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by default there is at least one dummy view type 3663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung viewTypeCount = 1; 3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung hasStableIds = true; 3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = null; 3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = 0; 37116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTypeIdIndexMap.clear(); 372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 373499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { 3753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = loadingView; 3763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (firstView != null) { 3773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = firstView; 3783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = -1; 379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 380499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 381499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getMappedViewType(int typeId) { 3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mTypeIdIndexMap.containsKey(typeId)) { 3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mTypeIdIndexMap.get(typeId); 3853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We +1 because the loading view always has view type id of 0 3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int incrementalTypeId = mTypeIdIndexMap.size() + 1; 3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap.put(typeId, incrementalTypeId); 3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return incrementalTypeId; 3906394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3916394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3926394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 393a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public boolean isViewTypeInRange(int typeId) { 394a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int mappedType = getMappedViewType(typeId); 395a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (mappedType >= viewTypeCount) { 396a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return false; 397a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 398a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen return true; 399a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 400a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 401a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 4023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayout createLoadingView(int position, View convertView, 403a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen ViewGroup parent, Object lock, LayoutInflater layoutInflater) { 4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create and return a new FrameLayout, and setup the references for this position 4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Context context = parent.getContext(); 4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); 4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create a new loading view 409a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (lock) { 410b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen boolean customLoadingViewAvailable = false; 411b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 4123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mUserLoadingView != null) { 413b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Try to inflate user-specified loading view 414b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 415b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View loadingView = mUserLoadingView.apply(parent.getContext(), parent); 416b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, 417b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(0)); 418b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(loadingView); 419b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen customLoadingViewAvailable = true; 420b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e) { 421b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating custom loading view, using default loading" + 422b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "view instead", e); 423b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 424b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 425b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (!customLoadingViewAvailable) { 4263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A default loading view 4273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Use the size of the first row as a guide for the size of the loading view 4283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mFirstViewHeight < 0) { 429b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 430b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View firstView = mFirstView.apply(parent.getContext(), parent); 431b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen firstView.measure( 432b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 433b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 434b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstViewHeight = firstView.getMeasuredHeight(); 435b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstView = null; 436b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e) { 437a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen float density = context.getResources().getDisplayMetrics().density; 438b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstViewHeight = (int) 439b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Math.round(sDefaultLoadingViewHeight * density); 440b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen mFirstView = null; 441b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating first RemoteViews" + e); 442b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Compose the loading view text 446a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen TextView loadingTextView = (TextView) layoutInflater.inflate( 447fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen com.android.internal.R.layout.remote_views_adapter_default_loading_view, 448fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen layout, false); 449a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setHeight(mFirstViewHeight); 450a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setTag(new Integer(0)); 451a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung 452a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung layout.addView(loadingTextView); 453fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung } 4546394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4556394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return layout; 4576394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4596394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 4603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 4613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with a single item in the cache. 4623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 463a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class RemoteViewsIndexMetaData { 4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId; 4653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId; 466b90a91c633e99d4559095184af27d1416541d3c0Winson Chung boolean isRequested; 467499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 468b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) { 469b90a91c633e99d4559095184af27d1416541d3c0Winson Chung set(v, itemId, requested); 470499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 471499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 472b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public void set(RemoteViews v, long id, boolean requested) { 4733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = id; 474a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (v != null) { 4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = v.getLayoutId(); 476a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = 0; 478a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 479b90a91c633e99d4559095184af27d1416541d3c0Winson Chung isRequested = requested; 4803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 482499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 4843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * 4853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 486a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen private static class FixedSizeRemoteViewsCache { 4873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final String TAG = "FixedSizeRemoteViewsCache"; 4883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The meta data related to all the RemoteViews, ie. count, is stable, etc. 4904c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // The meta data objects are made final so that they can be locked on independently 4914c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in 4924c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // the order mTemporaryMetaData followed by mMetaData. 4934c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mMetaData; 4944c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen private final RemoteViewsMetaData mTemporaryMetaData; 4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be 4973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // greater than or equal to the set of RemoteViews. 4983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: The reason that we keep this separate from the RemoteViews cache below is that this 4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we still need to be able to access the mapping of position to meta data, without keeping 5003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. 5013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // memory and size, but this metadata cache will retain information until the data at the 5023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). 5033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; 5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses 5063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // too much memory. 5073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViews> mIndexRemoteViews; 5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices that have been explicitly requested by the collection view 5103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mRequestedIndices; 5113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 512b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // We keep a reference of the last requested index to determine which item to prune the 513b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest items from when we hit the memory limit 514b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private int mLastRequestedIndex; 515b90a91c633e99d4559095184af27d1416541d3c0Winson Chung 5163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices to load, including those explicitly requested, as well as those 5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // determined by the preloading algorithm to be prefetched 5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mLoadIndices; 5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The lower and upper bounds of the preloaded range 5213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadLowerBound; 5223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadUpperBound; 5233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The bounds of this fixed cache, we will try and fill as many items into the cache up to 5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the maxCount number of items, or the maxSize memory usage. 5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The maxCountSlack is used to determine if a new position in the cache to be loaded is 5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // sufficiently ouside the old set, prompting a shifting of the "window" of items to be 5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloaded. 5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCount; 5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCountSlack; 5313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final float sMaxCountSlackPercent = 0.75f; 532b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024; 5333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public FixedSizeRemoteViewsCache(int maxCacheSize) { 5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCount = maxCacheSize; 5363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); 5373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 5383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 5393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMetaData = new RemoteViewsMetaData(); 54016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTemporaryMetaData = new RemoteViewsMetaData(); 5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 5433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices = new HashSet<Integer>(); 544b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices = new HashSet<Integer>(); 5463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 547499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 548b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public void insert(int position, RemoteViews v, long itemId, boolean isRequested) { 5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the count 5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.size() >= mMaxCount) { 5513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(position)); 552499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 553499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the available memory size constraints 555b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position; 556b90a91c633e99d4559095184af27d1416541d3c0Winson Chung while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) { 5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: This is currently the most naive mechanism for deciding what to prune when 5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we hit the memory limit. In the future, we may want to calculate which index to 5593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // remove based on both its position as well as it's current memory usage, as well 5603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // as whether it was directly requested vs. whether it was preloaded by our caching 5613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // mechanism. 562b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition)); 563499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Update the metadata cache 5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 5673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); 568b90a91c633e99d4559095184af27d1416541d3c0Winson Chung metaData.set(v, itemId, isRequested); 5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 570b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId, isRequested)); 571499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.put(position, v); 5733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 574499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData getMetaData() { 5763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mMetaData; 5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 57816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public RemoteViewsMetaData getTemporaryMetaData() { 57916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mTemporaryMetaData; 58016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViews getRemoteViewsAt(int position) { 5823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.containsKey(position)) { 5833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.get(position); 5843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 585499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 586499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData getMetaDataAt(int position) { 5883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 5893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.get(position); 590499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return null; 5923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 593499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 59416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void commitTemporaryMetaData() { 59516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mTemporaryMetaData) { 59616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mMetaData) { 59716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMetaData.set(mTemporaryMetaData); 59816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 59916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 60016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 60116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 6023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getRemoteViewsBitmapMemoryUsage() { 6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Calculate the memory usage of all the RemoteViews bitmaps being cached 6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mem = 0; 6053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (Integer i : mIndexRemoteViews.keySet()) { 6063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews v = mIndexRemoteViews.get(i); 607aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung if (v != null) { 6085d20064651b9947a4573c9a0eefec90f66eb1b59Adam Cohen mem += v.estimateMemoryUsage(); 609aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung } 610499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mem; 612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getFarthestPositionFrom(int pos) { 6143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Find the index farthest away and remove that 6153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDist = 0; 6163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDistIndex = -1; 617b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int maxDistNonRequested = 0; 618b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int maxDistIndexNonRequested = -1; 6193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i : mIndexRemoteViews.keySet()) { 6203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int dist = Math.abs(i-pos); 621b90a91c633e99d4559095184af27d1416541d3c0Winson Chung if (dist > maxDistNonRequested && !mIndexMetaData.get(i).isRequested) { 622b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDistNonRequested/maxDistIndexNonRequested will store the index of the 623b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest non-requested position 624b90a91c633e99d4559095184af27d1416541d3c0Winson Chung maxDistIndexNonRequested = i; 625b90a91c633e99d4559095184af27d1416541d3c0Winson Chung maxDistNonRequested = dist; 626b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 62735fbe2a5923d45ebcdfb3ad74efd1089a05e8737Adam Cohen if (dist >= maxDist) { 628b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDist/maxDistIndex will store the index of the farthest position 629b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // regardless of whether it was directly requested or not 6303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDistIndex = i; 6313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDist = dist; 632c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 633c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 634b90a91c633e99d4559095184af27d1416541d3c0Winson Chung if (maxDistIndexNonRequested > -1) { 635b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return maxDistIndexNonRequested; 636b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return maxDistIndex; 638c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 639c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queueRequestedPositionToLoad(int position) { 641b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = position; 6423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.add(position); 6443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(position); 645499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 646499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 64716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) { 6483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Check if we need to preload any items 6493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { 6503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; 6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (Math.abs(position - center) < mMaxCountSlack) { 65216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return false; 653499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 654499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 655499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = 0; 6573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mMetaData) { 6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = mMetaData.count; 659499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 6623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the requested indices 6643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.addAll(mRequestedIndices); 6653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the preload indices 6673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int halfMaxCount = mMaxCount / 2; 6683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = position - halfMaxCount; 6693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = position + halfMaxCount; 6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveLowerBound = Math.max(0, mPreloadLowerBound); 6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); 6723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { 6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(i); 6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 675499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // But remove all the indices that have already been loaded and are cached 6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.removeAll(mIndexRemoteViews.keySet()); 678499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 67916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return true; 680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 681b90a91c633e99d4559095184af27d1416541d3c0Winson Chung /** Returns the next index to load, and whether that index was directly requested or not */ 682b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public int[] getNextIndexToLoad() { 6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We try and prioritize items that have been requested directly, instead 6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // of items that are loaded as a result of the caching mechanism 6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Prioritize requested indices to be loaded first 6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mRequestedIndices.isEmpty()) { 6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mRequestedIndices.iterator().next(); 6893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.remove(i); 6903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 691b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 1}; 6923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 693499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, preload other indices as necessary 6953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mLoadIndices.isEmpty()) { 6963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mLoadIndices.iterator().next(); 6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 698b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 0}; 6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 700499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 701b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{-1, 0}; 702c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 7033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 704c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsRemoteViewAt(int position) { 7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.containsKey(position); 7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsMetaDataAt(int position) { 7093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.containsKey(position); 7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 711499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 71361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Note: We do not try and reset the meta data, since that information is still used by 71461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // collection views to validate it's own contents (and will be re-requested if the data 71561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // is invalidated through the notifyDataSetChanged() flow). 71661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 7173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 7183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 719b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 7203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.clear(); 7213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.clear(); 7223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 7233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.clear(); 7243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 725499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 726499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 727499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 729499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { 730499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mContext = context; 731499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mIntent = intent; 73281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); 733a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung mLayoutInflater = LayoutInflater.from(context); 7349b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung if (mIntent == null) { 7359b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung throw new IllegalArgumentException("Non-null Intent must be specified."); 7369b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 7373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews = new RemoteViewsFrameLayoutRefSet(); 738499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 73981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Strip the previously injected app widget id from service intent 74081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { 74181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); 74281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 74381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 74481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the worker thread 745499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); 746499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread.start(); 747499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerQueue = new Handler(mWorkerThread.getLooper()); 74881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue = new Handler(Looper.myLooper(), this); 749499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 75081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the cache and the service connection on startup 75181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); 7523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); 7533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mServiceConnection = new RemoteViewsAdapterServiceConnection(this); 754499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 755499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 756499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 757fc442bdea14289656ef1f537103578eb71faf473Jeff Brown @Override 758fc442bdea14289656ef1f537103578eb71faf473Jeff Brown protected void finalize() throws Throwable { 759fc442bdea14289656ef1f537103578eb71faf473Jeff Brown try { 760fc442bdea14289656ef1f537103578eb71faf473Jeff Brown if (mWorkerThread != null) { 761fc442bdea14289656ef1f537103578eb71faf473Jeff Brown mWorkerThread.quit(); 762fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 763fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } finally { 764fc442bdea14289656ef1f537103578eb71faf473Jeff Brown super.finalize(); 765fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 766fc442bdea14289656ef1f537103578eb71faf473Jeff Brown } 767fc442bdea14289656ef1f537103578eb71faf473Jeff Brown 7683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void loadNextIndexInBackground() { 7693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mWorkerQueue.post(new Runnable() { 7703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 7713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 77216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mServiceConnection.isConnected()) { 77316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Get the next index to load 77416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int position = -1; 775b90a91c633e99d4559095184af27d1416541d3c0Winson Chung boolean isRequested = false; 77616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 777b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int[] res = mCache.getNextIndexToLoad(); 778b90a91c633e99d4559095184af27d1416541d3c0Winson Chung position = res[0]; 779b90a91c633e99d4559095184af27d1416541d3c0Winson Chung isRequested = res[1] > 0; 78016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 78116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (position > -1) { 78216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item, and notify any existing RemoteViewsFrameLayouts 783b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen updateRemoteViews(position, isRequested, true); 7843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 78516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up for the next one to load 78616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 78716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 78816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // No more items to load, so queue unbind 78916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 79016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 7913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 7943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 79616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void processException(String method, Exception e) { 79716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage()); 7983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 79916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we encounter a crash when updating, we should reset the metadata & cache and trigger 80016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // a notifyDataSetChanged to update the widget accordingly 80116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 80216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 80316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung metaData.reset(); 80416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 80516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 80616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 80716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 80816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 80916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 81016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 81116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 8123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 81316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 81416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 81516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 81616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void updateTemporaryMetaData() { 81716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 81816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 81916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 82016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // get the properties/first view (so that we can use it to 82116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // measure our dummy views) 82216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasStableIds = factory.hasStableIds(); 82316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int viewTypeCount = factory.getViewTypeCount(); 82416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int count = factory.getCount(); 82516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews loadingView = factory.getLoadingView(); 82616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews firstView = null; 82716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if ((count > 0) && (loadingView == null)) { 82816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung firstView = factory.getViewAt(0); 82916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 83016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData(); 83116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (tmpMetaData) { 83216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.hasStableIds = hasStableIds; 83316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We +1 because the base view type is the loading view 83416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.viewTypeCount = viewTypeCount + 1; 83516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.count = count; 83616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.setLoadingViewTemplates(loadingView, firstView); 83716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 8382625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch(RemoteException e) { 83916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung processException("updateMetaData", e); 840fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen } catch(RuntimeException e) { 841fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen processException("updateMetaData", e); 8423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 845b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen private void updateRemoteViews(final int position, boolean isRequested, boolean 846b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen notifyWhenLoaded) { 84716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 84816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 84916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item information from the remote service 85016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews remoteViews = null; 85116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung long itemId = 0; 85216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 85316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung remoteViews = factory.getViewAt(position); 85416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung itemId = factory.getItemId(position); 8552625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 85616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 85716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 85816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent additional work in re-centering the view cache, and 85916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // swapping from the loading view 86016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 8612625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 8622625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 8632625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 86416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 8653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 86616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (remoteViews == null) { 86716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If a null view was returned, we break early to prevent it from getting 86816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // into our cache and causing problems later. The effect is that the child at this 86916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // position will remain as a loading view until it is updated. 87016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + 87116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung "returned from RemoteViewsFactory."); 87216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 87316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 874a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 875a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen int layoutId = remoteViews.getLayoutId(); 876a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen RemoteViewsMetaData metaData = mCache.getMetaData(); 877a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen boolean viewTypeInRange; 878a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen synchronized (metaData) { 879a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen viewTypeInRange = metaData.isViewTypeInRange(layoutId); 880a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 88116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 882a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (viewTypeInRange) { 883a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Cache the RemoteViews we loaded 884a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache.insert(position, remoteViews, itemId, isRequested); 885a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen 886a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // Notify all the views that we have previously returned for this index that 887a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // there is new data for it. 888a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen final RemoteViews rv = remoteViews; 889a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen if (notifyWhenLoaded) { 890a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mMainQueue.post(new Runnable() { 891a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen @Override 892a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen public void run() { 893a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mRequestedViews.notifyOnRemoteViewsLoaded(position, rv); 894a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 895a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen }); 896a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } 897a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen } else { 898a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // We need to log an error here, as the the view type count specified by the 899a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // factory is less than the number of view types returned. We don't return this 900a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // view to the AdapterView, as this will cause an exception in the hosting process, 901a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen // which contains the associated AdapterView. 902a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " + 903a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen " indicated by getViewTypeCount() "); 904b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 9053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 906499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 907499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 9089b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung public Intent getRemoteViewsServiceIntent() { 9099b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung return mIntent; 9109b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 9119b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 912499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getCount() { 9133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 9143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 9153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.count; 9163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 917499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 918499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 919499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public Object getItem(int position) { 9203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Disallow arbitrary object to be associated with an item for the time being 921499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 922499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 923499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 924499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public long getItemId(int position) { 9253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 9263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 9273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mCache.getMetaDataAt(position).itemId; 9283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 9303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 931499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 932499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 933499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getItemViewType(int position) { 9343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = 0; 9353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 9363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 9373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = mCache.getMetaDataAt(position).typeId; 9383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 9393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 9403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 9433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 9443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 9453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.getMappedViewType(typeId); 9463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 9493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 9503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Returns the item type id for the specified convert view. Returns -1 if the convert view 9513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * is invalid. 9523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 9533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getConvertViewTypeId(View convertView) { 9543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = -1; 955a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (convertView != null) { 956a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId); 957a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (tag != null) { 958a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen typeId = (Integer) tag; 959a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen } 9603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return typeId; 962499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 963499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 964b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen /** 965b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * This method allows an AdapterView using this Adapter to provide information about which 966b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * views are currently being displayed. This allows for certain optimizations and preloading 967b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen * which wouldn't otherwise be possible. 968b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen */ 969b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen public void setVisibleRangeHint(int lowerBound, int upperBound) { 970b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowLowerBound = lowerBound; 971b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen mVisibleWindowUpperBound = upperBound; 972b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 973b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 974499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public View getView(int position, View convertView, ViewGroup parent) { 97516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // "Request" an index so that we can queue it for loading, initiate subsequent 97616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // preloading, etc. 97716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 97816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isInCache = mCache.containsRemoteViewAt(position); 97916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isConnected = mServiceConnection.isConnected(); 98016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasNewItems = false; 98116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 9827ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung if (!isInCache && !isConnected) { 98316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Requesting bind service will trigger a super.notifyDataSetChanged(), which will 98416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // in turn trigger another request to getView() 98516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 98616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 9873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up other indices to be preloaded based on this position 98816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position); 98916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 99016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 99116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (isInCache) { 9923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View convertViewChild = null; 9933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int convertViewTypeId = 0; 994181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen RemoteViewsFrameLayout layout = null; 995181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen 996181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (convertView instanceof RemoteViewsFrameLayout) { 997181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen layout = (RemoteViewsFrameLayout) convertView; 9983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewChild = layout.getChildAt(0); 9993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewTypeId = getConvertViewTypeId(convertViewChild); 10003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 10023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Second, we try and retrieve the RemoteViews from the cache, returning a loading 10033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // view and queueing it to be loaded if it has not already been loaded. 100416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Context context = parent.getContext(); 100516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews rv = mCache.getRemoteViewsAt(position); 1006aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position); 1007aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen indexMetaData.isRequested = true; 1008aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen int typeId = indexMetaData.typeId; 100916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1010b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen try { 1011b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Reuse the convert view where possible 1012b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (layout != null) { 1013b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (convertViewTypeId == typeId) { 1014b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen rv.reapply(context, convertViewChild); 1015b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1016b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1017b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.removeAllViews(); 1018b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } else { 1019b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout = new RemoteViewsFrameLayout(context); 10203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1022b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // Otherwise, create a new view to be returned 1023b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen View newView = rv.apply(context, parent); 1024b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen newView.setTagInternal(com.android.internal.R.id.rowTypeId, 1025b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen new Integer(typeId)); 1026b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen layout.addView(newView); 1027b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return layout; 1028b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1029b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } catch (Exception e){ 1030b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // We have to make sure that we successfully inflated the RemoteViews, if not 1031b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen // we return the loading view instead. 1032b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" + 1033b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen "loading view instead" + e); 1034b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen 1035b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen RemoteViewsFrameLayout loadingView = null; 1036b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen final RemoteViewsMetaData metaData = mCache.getMetaData(); 1037b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen synchronized (metaData) { 1038a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1039a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache, mLayoutInflater); 1040b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 1041b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen return loadingView; 1042b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } finally { 1043b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen if (hasNewItems) loadNextIndexInBackground(); 1044b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen } 104516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 104616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If the cache does not have the RemoteViews at this position, then create a 104716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // loading view and queue the actual position to be loaded in the background 104816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViewsFrameLayout loadingView = null; 104916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 105016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 1051a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen loadingView = metaData.createLoadingView(position, convertView, parent, 1052a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen mCache, mLayoutInflater); 10533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 105416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 105516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mRequestedViews.add(position, loadingView); 105616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.queueRequestedPositionToLoad(position); 105716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 105816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 105916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return loadingView; 10603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1062499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1063499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1064499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getViewTypeCount() { 10653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 10663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 10673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.viewTypeCount; 10683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1069499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1070499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1071499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean hasStableIds() { 10723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 10733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 10743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.hasStableIds; 10753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1076499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1077499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1078499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isEmpty() { 1079499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return getCount() <= 0; 1080499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1081499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 108216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void onNotifyDataSetChanged() { 108316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Complete the actual notifyDataSetChanged() call initiated earlier 108416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 108516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 108616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 10872625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RemoteException e) { 108816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 108916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 109016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent from further being notified (since nothing has 109116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // changed) 109216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 10932625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen } catch (RuntimeException e) { 10942625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 10952625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen return; 109616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 109716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 109816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Flush the cache so that we can reload new items from the service 109916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 110016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 110116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 110216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 110316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Re-request the new metadata (only after the notification to the factory) 110416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung updateTemporaryMetaData(); 11054c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen int newCount; 11064c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen synchronized(mCache.getTemporaryMetaData()) { 11074c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen newCount = mCache.getTemporaryMetaData().count; 11084c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 110916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1110b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // Pre-load (our best guess of) the views which are currently visible in the AdapterView. 1111b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // This mitigates flashing and flickering of loading views when a widget notifies that 1112b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen // its data has changed. 1113b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen for (int i = mVisibleWindowLowerBound; i <= mVisibleWindowUpperBound; i++) { 11144c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // Because temporary meta data is only ever modified from this thread (ie. 11154c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen // mWorkerThread), it is safe to assume that count is a valid representation. 11164c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen if (i < newCount) { 11174c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen updateRemoteViews(i, false, false); 11184c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen } 1119b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen } 1120b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen 112116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Propagate the notification back to the base adapter 112216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 11236364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 11246364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 11256364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung synchronized (mCache) { 112616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.commitTemporaryMetaData(); 11276364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 112861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 112916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 113016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 11313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 11336364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 113416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Reset the notify flagflag 113516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = false; 113616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 113716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 113816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void notifyDataSetChanged() { 113916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 114016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 114116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 114216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we are not connected, queue up the notifyDataSetChanged to be handled when we do 114316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // connect 114416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mServiceConnection.isConnected()) { 114516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mNotifyDataSetChangedAfterOnServiceConnected) { 114616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 114716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 114816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 114916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = true; 115016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 115116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 115216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 115316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 115416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mWorkerQueue.post(new Runnable() { 115516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 115616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 115716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung onNotifyDataSetChanged(); 115816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 115916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 11603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 11613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1162fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen void superNotifyDataSetChanged() { 1163499cb9f516062b654952d282f211bee44c31a3c2Winson Chung super.notifyDataSetChanged(); 1164499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1165499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 116681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung @Override 116781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public boolean handleMessage(Message msg) { 116881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung boolean result = false; 116981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung switch (msg.what) { 117081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung case sUnbindServiceMessageType: 117181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (mServiceConnection.isConnected()) { 117216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.unbind(mContext, mAppWidgetId, mIntent); 117381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 117481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung result = true; 117581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 117681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung default: 117781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 117881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 117981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung return result; 118081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 118181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 118281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private void enqueueDeferredUnbindServiceMessage() { 118381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Remove any existing deferred-unbind messages 118481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 118581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay); 118681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 118781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 1188499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean requestBindService() { 118981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Try binding the service (which will start it if it's not already running) 1190499cb9f516062b654952d282f211bee44c31a3c2Winson Chung if (!mServiceConnection.isConnected()) { 119116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.bind(mContext, mAppWidgetId, mIntent); 1192499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1193499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 119416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any existing deferred-unbind messages 119516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 1196499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mServiceConnection.isConnected(); 1197499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1198499cb9f516062b654952d282f211bee44c31a3c2Winson Chung} 1199