RemoteViewsAdapter.java revision 84bbb020217adcdfe0694c44ccab57e208ffde16
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; 32fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log; 33a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater; 34499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View; 35181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec; 3684bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup; 37499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection; 39499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory; 40499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 41499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** 42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews 43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views. 44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */ 4681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback { 47fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung private static final String TAG = "RemoteViewsAdapter"; 48499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The max number of items in the cache 50b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sDefaultCacheSize = 40; 5181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // The delay (in millis) to wait until attempting to unbind from a service after a request. 5281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // This ensures that we don't stay continually bound to the service and that it can be destroyed 5381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // if we need the memory elsewhere in the system. 54b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sUnbindServiceDelay = 5000; 5581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Type defs for controlling different messages across the main and worker message queues 5681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sDefaultMessageType = 0; 5781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static final int sUnbindServiceMessageType = 1; 5881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 5981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Context mContext; 6081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final Intent mIntent; 6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private final int mAppWidgetId; 62a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung private LayoutInflater mLayoutInflater; 63499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private RemoteViewsAdapterServiceConnection mServiceConnection; 643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteAdapterConnectionCallback> mCallback; 653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private FixedSizeRemoteViewsCache mCache; 663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // A flag to determine whether we should notify data set changed after we connect 6816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mNotifyDataSetChangedAfterOnServiceConnected = false; 6916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of requested views that are to be notified when the associated RemoteViews are 713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // loaded. 723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayoutRefSet mRequestedViews; 73499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 74499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private HandlerThread mWorkerThread; 75499cb9f516062b654952d282f211bee44c31a3c2Winson Chung // items may be interrupted within the normally processed queues 76499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mWorkerQueue; 77499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private Handler mMainQueue; 78499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 79499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 80499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An interface for the RemoteAdapter to notify other classes when adapters 81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * are actually connected to/disconnected from their actual services. 82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public interface RemoteAdapterConnectionCallback { 8416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung /** 8516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung * @return whether the adapter was set or not. 8616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung */ 8716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean onRemoteAdapterConnected(); 88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 89499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void onRemoteAdapterDisconnected(); 90499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 91499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 92499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * The service connection that gets populated when the RemoteViewsService is 943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * bound. This must be a static inner class to ensure that no references to the outer 953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being 963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * garbage collected, and would cause us to leak activities due to the caching mechanism for 973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * FrameLayouts in the adapter). 98499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 9981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private static class RemoteViewsAdapterServiceConnection extends 10081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung IRemoteViewsAdapterConnection.Stub { 10116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnected; 10216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private boolean mIsConnecting; 1033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private WeakReference<RemoteViewsAdapter> mAdapter; 104499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private IRemoteViewsFactory mRemoteViewsFactory; 105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 1063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { 1073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); 108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 109499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 11016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void bind(Context context, int appWidgetId, Intent intent) { 11116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mIsConnecting) { 11216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 11316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 11416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.bindRemoteViewsService(appWidgetId, intent, asBinder()); 11516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = true; 11616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 11716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage()); 11816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 11916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 12016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 12116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 12216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 12316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 12416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void unbind(Context context, int appWidgetId, Intent intent) { 12516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 12616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final AppWidgetManager mgr = AppWidgetManager.getInstance(context); 12716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mgr.unbindRemoteViewsService(appWidgetId, intent); 12816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 12916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 13016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage()); 13116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 13216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 13316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 13416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 13516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 13616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceConnected(IBinder service) { 137499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); 138c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 13916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any deferred unbind messages 1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 1413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 14216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 14316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up work that we need to do for the callback to run 1443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mWorkerQueue.post(new Runnable() { 145499cb9f516062b654952d282f211bee44c31a3c2Winson Chung @Override 146499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public void run() { 14716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) { 14816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Handle queued notifyDataSetChanged() if necessary 14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.onNotifyDataSetChanged(); 15016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 1513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung IRemoteViewsFactory factory = 1523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mServiceConnection.getRemoteViewsFactory(); 1533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung try { 15416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!factory.isCreated()) { 15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We only call onDataSetChanged() if this is the factory was just 15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // create in response to this bind 15716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 15816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 1593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } catch (Exception e) { 1603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Log.e(TAG, "Error notifying factory of data set changed in " + 1613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung "onServiceConnected(): " + e.getMessage()); 1623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Return early to prevent anything further from being notified 1643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // (effectively nothing has changed) 1653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return; 166499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Request meta data so that we have up to date data when calling back to 1693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the remote adapter callback 17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.updateTemporaryMetaData(); 1713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 17216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Notify the host that we've connected 17361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung adapter.mMainQueue.post(new Runnable() { 1743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 1753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 17616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (adapter.mCache) { 17716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mCache.commitTemporaryMetaData(); 17816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 17916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 1803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteAdapterConnectionCallback callback = 1813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung adapter.mCallback.get(); 1823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (callback != null) { 1833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung callback.onRemoteAdapterConnected(); 1843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 1863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 18816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 18916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Enqueue unbind message 19016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.enqueueDeferredUnbindServiceMessage(); 19116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = true; 19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 193499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 194499cb9f516062b654952d282f211bee44c31a3c2Winson Chung }); 195499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 196499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 19716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized void onServiceDisconnected() { 19816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnected = false; 19916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mIsConnecting = false; 2003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRemoteViewsFactory = null; 201499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 20216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Clear the main/worker queues 2033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsAdapter adapter = mAdapter.get(); 2043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (adapter == null) return; 205c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 20616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.post(new Runnable() { 20716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 20816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 20916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 21016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung adapter.mMainQueue.removeMessages(sUnbindServiceMessageType); 2113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 21216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); 21316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (callback != null) { 21416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung callback.onRemoteAdapterDisconnected(); 21516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 21616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 21716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 218499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 219499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 22016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized IRemoteViewsFactory getRemoteViewsFactory() { 221499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mRemoteViewsFactory; 222499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 223499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 22416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public synchronized boolean isConnected() { 22516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mIsConnected; 226499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 227499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 228499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 229499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when 2313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * they are loaded. 232499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayout extends FrameLayout { 2343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayout(Context context) { 2353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung super(context); 2363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 237499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Updates this RemoteViewsFrameLayout depending on the view that was loaded. 2403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded 2413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * successfully. 242499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void onRemoteViewsLoaded(RemoteViews view) { 24461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung try { 24561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Remove all the children of this layout first 24661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung removeAllViews(); 24761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung addView(view.apply(getContext(), this)); 24861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } catch (Exception e) { 24961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung Log.e(TAG, "Failed to apply RemoteViews."); 25061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung } 2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the 2563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * adapter that have not yet had their RemoteViews loaded. 2573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsFrameLayoutRefSet { 2593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; 260499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsFrameLayoutRefSet() { 2623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); 263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung /** 2663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. 267499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */ 2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void add(int position, RemoteViewsFrameLayout layout) { 2693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 2703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung LinkedList<RemoteViewsFrameLayout> refs; 2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create the list if necessary 2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 2743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = mReferences.get(pos); 2753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs = new LinkedList<RemoteViewsFrameLayout>(); 2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.put(pos, refs); 278499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 2793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 2803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add the references to the list 2813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.add(layout); 282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 2843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 2853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that 2863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * the associated RemoteViews has loaded. 2873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 2883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) { 28961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung if (view == null) return; 29061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Integer pos = position; 2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mReferences.containsKey(pos)) { 2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Notify all the references for that position of the newly loaded RemoteViews 2943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); 2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (final RemoteViewsFrameLayout ref : refs) { 2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ref.onRemoteViewsLoaded(view); 2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 2983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung refs.clear(); 299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Remove this set from the original mapping 3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.remove(pos); 302499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 303499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 304499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. 3073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void clear() { 3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We currently just clear the references, and leave all the previous layouts returned 3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // in their default state of the loading view. 3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mReferences.clear(); 312499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 314499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with the cache in it's current state. 3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 3183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsMetaData { 3193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count; 3203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int viewTypeCount; 3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung boolean hasStableIds; 3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Used to determine how to construct loading views. If a loading view is not specified 3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by the user, then we try and load the first view, and use its height as the height for 3253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the default loading view. 3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mUserLoadingView; 3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViews mFirstView; 3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mFirstViewHeight; 3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A mapping from type id to a set of unique type ids 33116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>(); 3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData() { 3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung reset(); 335499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 336499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 33716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void set(RemoteViewsMetaData d) { 33816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (d) { 33916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung count = d.count; 34016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung viewTypeCount = d.viewTypeCount; 34116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasStableIds = d.hasStableIds; 34216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView); 34316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 34416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 34516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 3473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = 0; 34816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 3493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // by default there is at least one dummy view type 3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung viewTypeCount = 1; 3513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung hasStableIds = true; 3523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = null; 3533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 3543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = 0; 35516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTypeIdIndexMap.clear(); 356499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 357499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { 3593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mUserLoadingView = loadingView; 3603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (firstView != null) { 3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = firstView; 3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = -1; 363499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 364499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 365499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 3663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public int getMappedViewType(int typeId) { 3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mTypeIdIndexMap.containsKey(typeId)) { 3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mTypeIdIndexMap.get(typeId); 3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We +1 because the loading view always has view type id of 0 3713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int incrementalTypeId = mTypeIdIndexMap.size() + 1; 3723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mTypeIdIndexMap.put(typeId, incrementalTypeId); 3733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return incrementalTypeId; 3746394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3756394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 3766394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 3773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsFrameLayout createLoadingView(int position, View convertView, 3783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung ViewGroup parent) { 3793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create and return a new FrameLayout, and setup the references for this position 3803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final Context context = parent.getContext(); 3813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); 3823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Create a new loading view 3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 3853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mUserLoadingView != null) { 3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A user-specified loading view 3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View loadingView = mUserLoadingView.apply(parent.getContext(), parent); 388a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0)); 3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung layout.addView(loadingView); 3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // A default loading view 3923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Use the size of the first row as a guide for the size of the loading view 3933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mFirstViewHeight < 0) { 3943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View firstView = mFirstView.apply(parent.getContext(), parent); 3953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung firstView.measure( 3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstViewHeight = firstView.getMeasuredHeight(); 3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mFirstView = null; 4003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Compose the loading view text 403a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung TextView loadingTextView = (TextView) mLayoutInflater.inflate( 404fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen com.android.internal.R.layout.remote_views_adapter_default_loading_view, 405fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen layout, false); 406a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setHeight(mFirstViewHeight); 407a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung loadingTextView.setTag(new Integer(0)); 408a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung 409a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung layout.addView(loadingTextView); 410fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung } 4116394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4126394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 4133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return layout; 4146394c0e52cf641d93f678fd052499aa952e3595dWinson Chung } 4153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4166394c0e52cf641d93f678fd052499aa952e3595dWinson Chung 4173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 4183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * The meta-data associated with a single item in the cache. 4193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 4203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class RemoteViewsIndexMetaData { 4213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId; 4223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung long itemId; 423b90a91c633e99d4559095184af27d1416541d3c0Winson Chung boolean isRequested; 424499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 425b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) { 426b90a91c633e99d4559095184af27d1416541d3c0Winson Chung set(v, itemId, requested); 427499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 428499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 429b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public void set(RemoteViews v, long id, boolean requested) { 4303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung itemId = id; 4313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (v != null) 4323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = v.getLayoutId(); 4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung else 4343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = 0; 435b90a91c633e99d4559095184af27d1416541d3c0Winson Chung isRequested = requested; 4363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 4373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 438499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 4393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 4403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * 4413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 4423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private class FixedSizeRemoteViewsCache { 4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final String TAG = "FixedSizeRemoteViewsCache"; 4443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The meta data related to all the RemoteViews, ie. count, is stable, etc. 4463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private RemoteViewsMetaData mMetaData; 44716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private RemoteViewsMetaData mTemporaryMetaData; 4483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be 4503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // greater than or equal to the set of RemoteViews. 4513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: The reason that we keep this separate from the RemoteViews cache below is that this 4523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we still need to be able to access the mapping of position to meta data, without keeping 4533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. 4543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // memory and size, but this metadata cache will retain information until the data at the 4553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). 4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; 4573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses 4593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // too much memory. 4603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashMap<Integer, RemoteViews> mIndexRemoteViews; 4613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices that have been explicitly requested by the collection view 4633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mRequestedIndices; 4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 465b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // We keep a reference of the last requested index to determine which item to prune the 466b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest items from when we hit the memory limit 467b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private int mLastRequestedIndex; 468b90a91c633e99d4559095184af27d1416541d3c0Winson Chung 4693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The set of indices to load, including those explicitly requested, as well as those 4703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // determined by the preloading algorithm to be prefetched 4713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private HashSet<Integer> mLoadIndices; 4723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The lower and upper bounds of the preloaded range 4743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadLowerBound; 4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mPreloadUpperBound; 4763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The bounds of this fixed cache, we will try and fill as many items into the cache up to 4783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // the maxCount number of items, or the maxSize memory usage. 4793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // The maxCountSlack is used to determine if a new position in the cache to be loaded is 4803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // sufficiently ouside the old set, prompting a shifting of the "window" of items to be 4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // preloaded. 4823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCount; 4833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int mMaxCountSlack; 4843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private static final float sMaxCountSlackPercent = 0.75f; 485b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024; 4863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 4873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public FixedSizeRemoteViewsCache(int maxCacheSize) { 4883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCount = maxCacheSize; 4893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); 4903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 4913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 4923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mMetaData = new RemoteViewsMetaData(); 49316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mTemporaryMetaData = new RemoteViewsMetaData(); 4943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); 4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); 4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices = new HashSet<Integer>(); 497b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 4983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices = new HashSet<Integer>(); 4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 500499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 501b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public void insert(int position, RemoteViews v, long itemId, boolean isRequested) { 5023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the count 5033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.size() >= mMaxCount) { 5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(position)); 505499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 506499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Trim the cache if we go beyond the available memory size constraints 508b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position; 509b90a91c633e99d4559095184af27d1416541d3c0Winson Chung while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) { 5103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Note: This is currently the most naive mechanism for deciding what to prune when 5113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // we hit the memory limit. In the future, we may want to calculate which index to 5123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // remove based on both its position as well as it's current memory usage, as well 5133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // as whether it was directly requested vs. whether it was preloaded by our caching 5143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // mechanism. 515b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition)); 516499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Update the metadata cache 5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 5203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); 521b90a91c633e99d4559095184af27d1416541d3c0Winson Chung metaData.set(v, itemId, isRequested); 5223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 523b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId, isRequested)); 524499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.put(position, v); 5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 527499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsMetaData getMetaData() { 5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mMetaData; 5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 53116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public RemoteViewsMetaData getTemporaryMetaData() { 53216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return mTemporaryMetaData; 53316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViews getRemoteViewsAt(int position) { 5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexRemoteViews.containsKey(position)) { 5363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.get(position); 5373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 538499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public RemoteViewsIndexMetaData getMetaDataAt(int position) { 5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mIndexMetaData.containsKey(position)) { 5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.get(position); 543499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return null; 5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 546499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 54716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void commitTemporaryMetaData() { 54816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mTemporaryMetaData) { 54916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mMetaData) { 55016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMetaData.set(mTemporaryMetaData); 55116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 55216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 55316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 55416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getRemoteViewsBitmapMemoryUsage() { 5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Calculate the memory usage of all the RemoteViews bitmaps being cached 5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int mem = 0; 5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (Integer i : mIndexRemoteViews.keySet()) { 5593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViews v = mIndexRemoteViews.get(i); 560aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung if (v != null) { 561aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung mem += v.estimateBitmapMemoryUsage(); 562aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung } 563499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mem; 565499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getFarthestPositionFrom(int pos) { 5673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Find the index farthest away and remove that 5683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDist = 0; 5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int maxDistIndex = -1; 570b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int maxDistNonRequested = 0; 571b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int maxDistIndexNonRequested = -1; 5723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i : mIndexRemoteViews.keySet()) { 5733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int dist = Math.abs(i-pos); 574b90a91c633e99d4559095184af27d1416541d3c0Winson Chung if (dist > maxDistNonRequested && !mIndexMetaData.get(i).isRequested) { 575b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDistNonRequested/maxDistIndexNonRequested will store the index of the 576b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // farthest non-requested position 577b90a91c633e99d4559095184af27d1416541d3c0Winson Chung maxDistIndexNonRequested = i; 578b90a91c633e99d4559095184af27d1416541d3c0Winson Chung maxDistNonRequested = dist; 579b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 5803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (dist > maxDist) { 581b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // maxDist/maxDistIndex will store the index of the farthest position 582b90a91c633e99d4559095184af27d1416541d3c0Winson Chung // regardless of whether it was directly requested or not 5833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDistIndex = i; 5843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung maxDist = dist; 585c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 586c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 587b90a91c633e99d4559095184af27d1416541d3c0Winson Chung if (maxDistIndexNonRequested > -1) { 588b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return maxDistIndexNonRequested; 589b90a91c633e99d4559095184af27d1416541d3c0Winson Chung } 5903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return maxDistIndex; 591c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 592c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 5933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void queueRequestedPositionToLoad(int position) { 594b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = position; 5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 5963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.add(position); 5973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(position); 598499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 599499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 60016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) { 6013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Check if we need to preload any items 6023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { 6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; 6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (Math.abs(position - center) < mMaxCountSlack) { 60516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return false; 606499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 607499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 608499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int count = 0; 6103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mMetaData) { 6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung count = mMetaData.count; 612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 6133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 6153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the requested indices 6173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.addAll(mRequestedIndices); 6183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 6193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Add all the preload indices 6203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int halfMaxCount = mMaxCount / 2; 6213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = position - halfMaxCount; 6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = position + halfMaxCount; 6233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveLowerBound = Math.max(0, mPreloadLowerBound); 6243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); 6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { 6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.add(i); 6273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 628499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // But remove all the indices that have already been loaded and are cached 6303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.removeAll(mIndexRemoteViews.keySet()); 631499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 63216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return true; 633499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 634b90a91c633e99d4559095184af27d1416541d3c0Winson Chung /** Returns the next index to load, and whether that index was directly requested or not */ 635b90a91c633e99d4559095184af27d1416541d3c0Winson Chung public int[] getNextIndexToLoad() { 6363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // We try and prioritize items that have been requested directly, instead 6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // of items that are loaded as a result of the caching mechanism 6383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Prioritize requested indices to be loaded first 6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mRequestedIndices.isEmpty()) { 6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mRequestedIndices.iterator().next(); 6423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.remove(i); 6433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 644b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 1}; 6453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 646499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Otherwise, preload other indices as necessary 6483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (!mLoadIndices.isEmpty()) { 6493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung Integer i = mLoadIndices.iterator().next(); 6503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.remove(i); 651b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{i.intValue(), 0}; 6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 653499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 654b90a91c633e99d4559095184af27d1416541d3c0Winson Chung return new int[]{-1, 0}; 655c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung } 6563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 657c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung 6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsRemoteViewAt(int position) { 6593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexRemoteViews.containsKey(position); 6603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public boolean containsMetaDataAt(int position) { 6623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mIndexMetaData.containsKey(position); 6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 664499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 6653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void reset() { 66661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // Note: We do not try and reset the meta data, since that information is still used by 66761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // collection views to validate it's own contents (and will be re-requested if the data 66861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung // is invalidated through the notifyDataSetChanged() flow). 66961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadLowerBound = 0; 6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mPreloadUpperBound = -1; 672b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mLastRequestedIndex = -1; 6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexRemoteViews.clear(); 6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mIndexMetaData.clear(); 6753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mLoadIndices) { 6763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedIndices.clear(); 6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mLoadIndices.clear(); 678499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 679499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 681499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 682499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { 683499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mContext = context; 684499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mIntent = intent; 68581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1); 686a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung mLayoutInflater = LayoutInflater.from(context); 6879b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung if (mIntent == null) { 6889b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung throw new IllegalArgumentException("Non-null Intent must be specified."); 6899b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 6903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mRequestedViews = new RemoteViewsFrameLayoutRefSet(); 691499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 69281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Strip the previously injected app widget id from service intent 69381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { 69481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); 69581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 69681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 69781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the worker thread 698499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); 699499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerThread.start(); 700499cb9f516062b654952d282f211bee44c31a3c2Winson Chung mWorkerQueue = new Handler(mWorkerThread.getLooper()); 70181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue = new Handler(Looper.myLooper(), this); 702499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 70381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Initialize the cache and the service connection on startup 70481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); 7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); 7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mServiceConnection = new RemoteViewsAdapterServiceConnection(this); 707499cb9f516062b654952d282f211bee44c31a3c2Winson Chung requestBindService(); 708499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 709499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private void loadNextIndexInBackground() { 7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung mWorkerQueue.post(new Runnable() { 7123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung @Override 7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung public void run() { 71416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mServiceConnection.isConnected()) { 71516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Get the next index to load 71616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int position = -1; 717b90a91c633e99d4559095184af27d1416541d3c0Winson Chung boolean isRequested = false; 71816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 719b90a91c633e99d4559095184af27d1416541d3c0Winson Chung int[] res = mCache.getNextIndexToLoad(); 720b90a91c633e99d4559095184af27d1416541d3c0Winson Chung position = res[0]; 721b90a91c633e99d4559095184af27d1416541d3c0Winson Chung isRequested = res[1] > 0; 72216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 72316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (position > -1) { 72416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item, and notify any existing RemoteViewsFrameLayouts 725b90a91c633e99d4559095184af27d1416541d3c0Winson Chung updateRemoteViews(position, isRequested); 7263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 72716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Queue up for the next one to load 72816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 72916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 73016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // No more items to load, so queue unbind 73116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 73216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 7333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 7363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 73816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void processException(String method, Exception e) { 73916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage()); 7403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 74116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we encounter a crash when updating, we should reset the metadata & cache and trigger 74216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // a notifyDataSetChanged to update the widget accordingly 74316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 74416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 74516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung metaData.reset(); 74616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 74716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 74816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 74916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 75016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 75116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 75216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 75316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 7543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 75516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 75616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 75716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 75816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void updateTemporaryMetaData() { 75916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 76016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 76116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 76216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // get the properties/first view (so that we can use it to 76316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // measure our dummy views) 76416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasStableIds = factory.hasStableIds(); 76516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int viewTypeCount = factory.getViewTypeCount(); 76616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung int count = factory.getCount(); 76716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews loadingView = factory.getLoadingView(); 76816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews firstView = null; 76916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if ((count > 0) && (loadingView == null)) { 77016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung firstView = factory.getViewAt(0); 77116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 77216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData(); 77316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (tmpMetaData) { 77416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.hasStableIds = hasStableIds; 77516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // We +1 because the base view type is the loading view 77616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.viewTypeCount = viewTypeCount + 1; 77716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.count = count; 77816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung tmpMetaData.setLoadingViewTemplates(loadingView, firstView); 77916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 78016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 78116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung processException("updateMetaData", e); 7823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 7843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 785b90a91c633e99d4559095184af27d1416541d3c0Winson Chung private void updateRemoteViews(final int position, boolean isRequested) { 78616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mServiceConnection.isConnected()) return; 78716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 78816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 78916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Load the item information from the remote service 79016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews remoteViews = null; 79116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung long itemId = 0; 79216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 79316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung remoteViews = factory.getViewAt(position); 79416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung itemId = factory.getItemId(position); 79516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 79616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); 79716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 79816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent additional work in re-centering the view cache, and 79916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // swapping from the loading view 80016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 80116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 8023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 80316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (remoteViews == null) { 80416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If a null view was returned, we break early to prevent it from getting 80516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // into our cache and causing problems later. The effect is that the child at this 80616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // position will remain as a loading view until it is updated. 80716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " + 80816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung "returned from RemoteViewsFactory."); 80916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 81016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 81116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 81216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Cache the RemoteViews we loaded 813b90a91c633e99d4559095184af27d1416541d3c0Winson Chung mCache.insert(position, remoteViews, itemId, isRequested); 81416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 81516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Notify all the views that we have previously returned for this index that 81616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // there is new data for it. 81716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViews rv = remoteViews; 81816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final int typeId = mCache.getMetaDataAt(position).typeId; 81916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 82016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 82116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 82216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId); 82316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 82416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 8253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 826499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 827499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 8289b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung public Intent getRemoteViewsServiceIntent() { 8299b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung return mIntent; 8309b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung } 8319b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung 832499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getCount() { 8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 8343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 8353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.count; 8363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 837499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 838499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 839499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public Object getItem(int position) { 8403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Disallow arbitrary object to be associated with an item for the time being 841499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return null; 842499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 843499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 844499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public long getItemId(int position) { 8453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 8463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 8473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return mCache.getMetaDataAt(position).itemId; 8483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 8503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 851499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 852499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 853499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getItemViewType(int position) { 8543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = 0; 8553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (mCache) { 8563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung if (mCache.containsMetaDataAt(position)) { 8573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung typeId = mCache.getMetaDataAt(position).typeId; 8583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 8593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return 0; 8603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 8643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 8653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.getMappedViewType(typeId); 8663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 8693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung /** 8703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * Returns the item type id for the specified convert view. Returns -1 if the convert view 8713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung * is invalid. 8723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung */ 8733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung private int getConvertViewTypeId(View convertView) { 8743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int typeId = -1; 875a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (convertView != null) { 876a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId); 877a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen if (tag != null) { 878a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen typeId = (Integer) tag; 879a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen } 8803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 8813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return typeId; 882499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 883499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 884499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public View getView(int position, View convertView, ViewGroup parent) { 88516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // "Request" an index so that we can queue it for loading, initiate subsequent 88616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // preloading, etc. 88716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 88816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isInCache = mCache.containsRemoteViewAt(position); 88916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean isConnected = mServiceConnection.isConnected(); 89016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung boolean hasNewItems = false; 89116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 89216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!isConnected) { 89316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Requesting bind service will trigger a super.notifyDataSetChanged(), which will 89416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // in turn trigger another request to getView() 89516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 89616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 8973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Queue up other indices to be preloaded based on this position 89816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position); 89916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 90016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 90116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (isInCache) { 9023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung View convertViewChild = null; 9033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung int convertViewTypeId = 0; 904181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen RemoteViewsFrameLayout layout = null; 905181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen 906181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen if (convertView instanceof RemoteViewsFrameLayout) { 907181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen layout = (RemoteViewsFrameLayout) convertView; 9083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewChild = layout.getChildAt(0); 9093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung convertViewTypeId = getConvertViewTypeId(convertViewChild); 9103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 9123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // Second, we try and retrieve the RemoteViews from the cache, returning a loading 9133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung // view and queueing it to be loaded if it has not already been loaded. 91416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Context context = parent.getContext(); 91516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViews rv = mCache.getRemoteViewsAt(position); 916aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position); 917aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen indexMetaData.isRequested = true; 918aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen int typeId = indexMetaData.typeId; 91916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 92016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Reuse the convert view where possible 92116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (layout != null) { 92216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (convertViewTypeId == typeId) { 92316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung rv.reapply(context, convertViewChild); 92416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return layout; 9253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 92616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung layout.removeAllViews(); 9273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } else { 92816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung layout = new RemoteViewsFrameLayout(context); 92916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 9303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 93116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Otherwise, create a new view to be returned 93216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung View newView = rv.apply(context, parent); 93316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId)); 93416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung layout.addView(newView); 93516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (hasNewItems) loadNextIndexInBackground(); 9363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 93716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return layout; 93816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } else { 93916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If the cache does not have the RemoteViews at this position, then create a 94016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // loading view and queue the actual position to be loaded in the background 94116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung RemoteViewsFrameLayout loadingView = null; 94216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 94316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (metaData) { 94416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadingView = metaData.createLoadingView(position, convertView, parent); 9453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 94616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 94716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mRequestedViews.add(position, loadingView); 94816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.queueRequestedPositionToLoad(position); 94916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung loadNextIndexInBackground(); 95016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 95116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return loadingView; 9523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 9533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 954499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 955499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 956499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public int getViewTypeCount() { 9573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 9583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 9593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.viewTypeCount; 9603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 961499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 962499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 963499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean hasStableIds() { 9643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung final RemoteViewsMetaData metaData = mCache.getMetaData(); 9653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung synchronized (metaData) { 9663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung return metaData.hasStableIds; 9673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 968499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 969499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 970499cb9f516062b654952d282f211bee44c31a3c2Winson Chung public boolean isEmpty() { 971499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return getCount() <= 0; 972499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 973499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 97416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 97516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung private void onNotifyDataSetChanged() { 97616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Complete the actual notifyDataSetChanged() call initiated earlier 97716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); 97816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung try { 97916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung factory.onDataSetChanged(); 98016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } catch (Exception e) { 98116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); 98216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 98316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Return early to prevent from further being notified (since nothing has 98416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // changed) 98516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 98616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 98716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 98816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Flush the cache so that we can reload new items from the service 98916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung synchronized (mCache) { 99016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.reset(); 99116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 99216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 99316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Re-request the new metadata (only after the notification to the factory) 99416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung updateTemporaryMetaData(); 99516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 99616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Propagate the notification back to the base adapter 99716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.post(new Runnable() { 9986364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung @Override 9996364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung public void run() { 10006364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung synchronized (mCache) { 100116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mCache.commitTemporaryMetaData(); 10026364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung } 100361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung 100416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung superNotifyDataSetChanged(); 100516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung enqueueDeferredUnbindServiceMessage(); 10063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung }); 10086364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung 100916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Reset the notify flagflag 101016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = false; 101116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 101216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 101316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void notifyDataSetChanged() { 101416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Dequeue any unbind messages 101516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 101616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 101716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // If we are not connected, queue up the notifyDataSetChanged to be handled when we do 101816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // connect 101916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (!mServiceConnection.isConnected()) { 102016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung if (mNotifyDataSetChangedAfterOnServiceConnected) { 102116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 102216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 102316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 102416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mNotifyDataSetChangedAfterOnServiceConnected = true; 102516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung requestBindService(); 102616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung return; 102716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 102816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung 102916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mWorkerQueue.post(new Runnable() { 103016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung @Override 103116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung public void run() { 103216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung onNotifyDataSetChanged(); 103316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung } 103416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung }); 10353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung } 10363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung 1037fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen void superNotifyDataSetChanged() { 1038499cb9f516062b654952d282f211bee44c31a3c2Winson Chung super.notifyDataSetChanged(); 1039499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1040499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 104181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung @Override 104281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung public boolean handleMessage(Message msg) { 104381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung boolean result = false; 104481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung switch (msg.what) { 104581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung case sUnbindServiceMessageType: 104681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung if (mServiceConnection.isConnected()) { 104716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.unbind(mContext, mAppWidgetId, mIntent); 104881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 104981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung result = true; 105081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 105181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung default: 105281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung break; 105381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 105481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung return result; 105581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 105681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 105781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung private void enqueueDeferredUnbindServiceMessage() { 105881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Remove any existing deferred-unbind messages 105981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 106081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay); 106181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung } 106281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung 1063499cb9f516062b654952d282f211bee44c31a3c2Winson Chung private boolean requestBindService() { 106481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung // Try binding the service (which will start it if it's not already running) 1065499cb9f516062b654952d282f211bee44c31a3c2Winson Chung if (!mServiceConnection.isConnected()) { 106616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mServiceConnection.bind(mContext, mAppWidgetId, mIntent); 1067499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1068499cb9f516062b654952d282f211bee44c31a3c2Winson Chung 106916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung // Remove any existing deferred-unbind messages 107016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung mMainQueue.removeMessages(sUnbindServiceMessageType); 1071499cb9f516062b654952d282f211bee44c31a3c2Winson Chung return mServiceConnection.isConnected(); 1072499cb9f516062b654952d282f211bee44c31a3c2Winson Chung } 1073499cb9f516062b654952d282f211bee44c31a3c2Winson Chung} 1074