1499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/*
2499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Copyright (C) 2007 The Android Open Source Project
3499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
4499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * you may not use this file except in compliance with the License.
6499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * You may obtain a copy of the License at
7499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
8499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
10499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Unless required by applicable law or agreed to in writing, software
11499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * See the License for the specific language governing permissions and
14499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * limitations under the License.
15499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
16499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
17499cb9f516062b654952d282f211bee44c31a3c2Winson Chungpackage android.widget;
18499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.lang.ref.WeakReference;
20591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohenimport java.util.ArrayList;
214ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyalimport java.util.Arrays;
22499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap;
233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.util.HashSet;
24c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chungimport java.util.LinkedList;
25ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani
263e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Millerimport android.Manifest;
27a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyalimport android.appwidget.AppWidgetHostView;
2881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager;
29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context;
30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent;
31499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler;
32499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread;
33499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder;
34499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper;
3581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message;
362625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException;
37fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log;
383e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Millerimport android.util.Slog;
394ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyalimport android.util.SparseArray;
404ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyalimport android.util.SparseBooleanArray;
414ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyalimport android.util.SparseIntArray;
42a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater;
43499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View;
44181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec;
4584bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup;
46a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohenimport android.widget.RemoteViews.OnClickHandler;
47499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection;
49499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory;
50499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
51499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/**
52499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews
53499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views.
54499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
55499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */
5681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
573e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller    private static final String MULTI_USER_PERM = Manifest.permission.INTERACT_ACROSS_USERS_FULL;
583e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller
59fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung    private static final String TAG = "RemoteViewsAdapter";
60499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
613e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller    // The max number of items in the cache
62b90a91c633e99d4559095184af27d1416541d3c0Winson Chung    private static final int sDefaultCacheSize = 40;
6381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // The delay (in millis) to wait until attempting to unbind from a service after a request.
6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // This ensures that we don't stay continually bound to the service and that it can be destroyed
6581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // if we need the memory elsewhere in the system.
66b90a91c633e99d4559095184af27d1416541d3c0Winson Chung    private static final int sUnbindServiceDelay = 5000;
67b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
68b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen    // Default height for the default loading view, in case we cannot get inflate the first view
69b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen    private static final int sDefaultLoadingViewHeight = 50;
70b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
7181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // Type defs for controlling different messages across the main and worker message queues
723e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller    private static final int sDefaultMessageType = 0;
7381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sUnbindServiceMessageType = 1;
7481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
7581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Context mContext;
7681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Intent mIntent;
7781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final int mAppWidgetId;
78a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung    private LayoutInflater mLayoutInflater;
79499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private RemoteViewsAdapterServiceConnection mServiceConnection;
803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
81a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen    private OnClickHandler mRemoteViewsOnClickHandler;
82a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal    private final FixedSizeRemoteViewsCache mCache;
83b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private int mVisibleWindowLowerBound;
84b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private int mVisibleWindowUpperBound;
853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    // A flag to determine whether we should notify data set changed after we connect
8716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private boolean mNotifyDataSetChangedAfterOnServiceConnected = false;
8816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // The set of requested views that are to be notified when the associated RemoteViews are
903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // loaded.
913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private RemoteViewsFrameLayoutRefSet mRequestedViews;
92499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private HandlerThread mWorkerThread;
94499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    // items may be interrupted within the normally processed queues
95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mWorkerQueue;
96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mMainQueue;
97499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
98335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
993e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller    // structures;
1004ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal    private static final HashMap<RemoteViewsCacheKey, FixedSizeRemoteViewsCache>
1014ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            sCachedRemoteViewsCaches = new HashMap<>();
102ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani    private static final HashMap<RemoteViewsCacheKey, Runnable>
1034ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            sRemoteViewsCacheRemoveRunnables = new HashMap<>();
104ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani
105335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static HandlerThread sCacheRemovalThread;
106335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static Handler sCacheRemovalQueue;
107335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
108335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // We keep the cache around for a duration after onSaveInstanceState for use on re-inflation.
109335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this
110335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // duration, the cache is dropped.
111335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static final int REMOTE_VIEWS_CACHE_DURATION = 5000;
112335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
113335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // Used to indicate to the AdapterView that it can use this Adapter immediately after
114335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // construction (happens when we have a cached FixedSizeRemoteViewsCache).
115335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private boolean mDataReady = false;
116335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
118499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An interface for the RemoteAdapter to notify other classes when adapters
119499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * are actually connected to/disconnected from their actual services.
120499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
121499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public interface RemoteAdapterConnectionCallback {
12216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        /**
12316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung         * @return whether the adapter was set or not.
12416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung         */
12516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public boolean onRemoteAdapterConnected();
126499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
127499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterDisconnected();
1282148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen
1292148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen        /**
1302148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not
1312148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         * connected yet.
1322148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         */
1332148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen        public void deferNotifyDataSetChanged();
134499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
135499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
136499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
137499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * The service connection that gets populated when the RemoteViewsService is
1383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * bound.  This must be a static inner class to ensure that no references to the outer
1393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * garbage collected, and would cause us to leak activities due to the caching mechanism for
1413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * FrameLayouts in the adapter).
142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
14381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static class RemoteViewsAdapterServiceConnection extends
14481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            IRemoteViewsAdapterConnection.Stub {
14516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private boolean mIsConnected;
14616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private boolean mIsConnecting;
1473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private WeakReference<RemoteViewsAdapter> mAdapter;
148499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private IRemoteViewsFactory mRemoteViewsFactory;
149499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
1513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
152499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
15416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void bind(Context context, int appWidgetId, Intent intent) {
15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (!mIsConnecting) {
15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                try {
157c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani                    RemoteViewsAdapter adapter;
15816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
1593e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller                    if ((adapter = mAdapter.get()) != null) {
1605771ad7bc7533f4a9bc72ac1ecabfdf6ca493b16Svet Ganov                        mgr.bindRemoteViewsService(context.getOpPackageName(), appWidgetId,
161976e8bd2017d0263216c62111454438cc0f130e3Svetoslav                                intent, asBinder());
162c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani                    } else {
1633e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller                        Slog.w(TAG, "bind: adapter was null");
164c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani                    }
16516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = true;
16616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                } catch (Exception e) {
16716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage());
16816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = false;
16916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnected = false;
17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
17116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
17216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
17316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
17416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void unbind(Context context, int appWidgetId, Intent intent) {
17516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            try {
176c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani                RemoteViewsAdapter adapter;
17716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
1783e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller                if ((adapter = mAdapter.get()) != null) {
1792635b7b0d33d166e081587922b2fe96bf294ac57Svetoslav                    mgr.unbindRemoteViewsService(context.getOpPackageName(), appWidgetId, intent);
180c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani                } else {
1813e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller                    Slog.w(TAG, "unbind: adapter was null");
182c566b43d02596cba437e9a2723e9f989297cca72Amith Yamasani                }
18316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnecting = false;
18416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } catch (Exception e) {
18516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage());
18616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnecting = false;
18716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnected = false;
18816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
18916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
19016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
19116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void onServiceConnected(IBinder service) {
192499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
193c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
19416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Remove any deferred unbind messages
1953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
1963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
19716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
19816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Queue up work that we need to do for the callback to run
1993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            adapter.mWorkerQueue.post(new Runnable() {
200499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
201499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
20216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) {
20316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Handle queued notifyDataSetChanged() if necessary
20416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        adapter.onNotifyDataSetChanged();
20516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    } else {
2063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        IRemoteViewsFactory factory =
2073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            adapter.mServiceConnection.getRemoteViewsFactory();
2083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        try {
20916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                            if (!factory.isCreated()) {
21016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                // We only call onDataSetChanged() if this is the factory was just
21116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                // create in response to this bind
21216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                factory.onDataSetChanged();
21316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                            }
2142625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                        } catch (RemoteException e) {
2153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            Log.e(TAG, "Error notifying factory of data set changed in " +
2163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                        "onServiceConnected(): " + e.getMessage());
2173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // Return early to prevent anything further from being notified
2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // (effectively nothing has changed)
2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            return;
2212625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                        } catch (RuntimeException e) {
2222625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                            Log.e(TAG, "Error notifying factory of data set changed in " +
2232625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                                    "onServiceConnected(): " + e.getMessage());
224499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
2253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // Request meta data so that we have up to date data when calling back to
2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // the remote adapter callback
22816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        adapter.updateTemporaryMetaData();
2293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
23016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Notify the host that we've connected
23161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                        adapter.mMainQueue.post(new Runnable() {
2323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            @Override
2333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            public void run() {
23416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                synchronized (adapter.mCache) {
23516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                    adapter.mCache.commitTemporaryMetaData();
23616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                }
23716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
2383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                final RemoteAdapterConnectionCallback callback =
2393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    adapter.mCallback.get();
2403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                if (callback != null) {
2413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    callback.onRemoteAdapterConnected();
2423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                }
2433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            }
2443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        });
2453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
24616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
24716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Enqueue unbind message
24816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    adapter.enqueueDeferredUnbindServiceMessage();
24916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnected = true;
25016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = false;
251499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
252499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
25516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void onServiceDisconnected() {
25616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mIsConnected = false;
25716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mIsConnecting = false;
2583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRemoteViewsFactory = null;
259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
26016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Clear the main/worker queues
2613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
2623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
2633e5105351da19e639bb9c2d0b62e012779d9a7a8Jim Miller
26416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            adapter.mMainQueue.post(new Runnable() {
26516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                @Override
26616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                public void run() {
26716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Dequeue any unbind messages
26816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
2693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
27016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
27116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (callback != null) {
27216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        callback.onRemoteAdapterDisconnected();
27316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
27416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
27516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            });
276499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
277499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
27816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized IRemoteViewsFactory getRemoteViewsFactory() {
279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mRemoteViewsFactory;
280499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
281499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
28216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized boolean isConnected() {
28316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return mIsConnected;
284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
285499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
286499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
287499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
2883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * they are loaded.
290499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
291b880d167a6ba6ce90c0c52c44bc775627150cdb8Sunny Goyal    static class RemoteViewsFrameLayout extends AppWidgetHostView {
292a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        private final FixedSizeRemoteViewsCache mCache;
293a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal
294a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        public RemoteViewsFrameLayout(Context context, FixedSizeRemoteViewsCache cache) {
2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            super(context);
296a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            mCache = cache;
2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
298499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
3003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
3023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         *             successfully.
303499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
304a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen        public void onRemoteViewsLoaded(RemoteViews view, OnClickHandler handler) {
305a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            setOnClickHandler(handler);
306a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            applyRemoteViews(view);
307a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        }
308a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal
309a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        @Override
310a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        protected View getDefaultView() {
311a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            return mCache.getMetaData().createDefaultLoadingView(this);
312a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        }
313a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal
314a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        @Override
315a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        protected Context getRemoteContext() {
316a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            return null;
317a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        }
318a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal
319a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        @Override
320a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        protected View getErrorView() {
321a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            // Use the default loading view as the error view.
322a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            return getDefaultView();
3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
325499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * adapter that have not yet had their RemoteViews loaded.
3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
3303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsFrameLayoutRefSet {
3314ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final SparseArray<LinkedList<RemoteViewsFrameLayout>> mReferences =
3324ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                new SparseArray<>();
3334ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>
3344ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mViewToLinkedList = new HashMap<>();
335499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
336499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
338499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
3393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void add(int position, RemoteViewsFrameLayout layout) {
3404ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(position);
3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create the list if necessary
3434ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            if (refs == null) {
3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = new LinkedList<RemoteViewsFrameLayout>();
3454ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mReferences.put(position, refs);
346499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
347ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen            mViewToLinkedList.put(layout, refs);
3483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Add the references to the list
3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            refs.add(layout);
351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
3543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
3553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * the associated RemoteViews has loaded.
3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
357a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) {
35861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            if (view == null) return;
35961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
3604ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(position);
3614ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            if (refs != null) {
3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Notify all the references for that position of the newly loaded RemoteViews
3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (final RemoteViewsFrameLayout ref : refs) {
364a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen                    ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler);
365ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen                    if (mViewToLinkedList.containsKey(ref)) {
366ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen                        mViewToLinkedList.remove(ref);
367ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen                    }
3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs.clear();
3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Remove this set from the original mapping
3714ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mReferences.remove(position);
372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
373499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
374499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
376ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen         * We need to remove views from this set if they have been recycled by the AdapterView.
377ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen         */
378ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen        public void removeView(RemoteViewsFrameLayout rvfl) {
379ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen            if (mViewToLinkedList.containsKey(rvfl)) {
380ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen                mViewToLinkedList.get(rvfl).remove(rvfl);
381ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen                mViewToLinkedList.remove(rvfl);
382ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen            }
383ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen        }
384ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen
385ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen        /**
3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void clear() {
3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We currently just clear the references, and leave all the previous layouts returned
3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // in their default state of the loading view.
3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences.clear();
392ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen            mViewToLinkedList.clear();
393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
3943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
395499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with the cache in it's current state.
3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
3994a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static class RemoteViewsMetaData {
4003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int count;
4013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int viewTypeCount;
4023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        boolean hasStableIds;
4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Used to determine how to construct loading views.  If a loading view is not specified
4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // by the user, then we try and load the first view, and use its height as the height for
4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the default loading view.
4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mUserLoadingView;
4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mFirstView;
4093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int mFirstViewHeight;
4103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // A mapping from type id to a set of unique type ids
4124ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final SparseIntArray mTypeIdIndexMap = new SparseIntArray();
4133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData() {
4153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            reset();
416499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
417499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
41816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public void set(RemoteViewsMetaData d) {
41916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (d) {
42016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                count = d.count;
42116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                viewTypeCount = d.viewTypeCount;
42216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                hasStableIds = d.hasStableIds;
42316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView);
42416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
42516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
42616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
4273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
4283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            count = 0;
42916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
4303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // by default there is at least one dummy view type
4313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            viewTypeCount = 1;
4323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            hasStableIds = true;
4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = null;
4343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstView = null;
4353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstViewHeight = 0;
43616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mTypeIdIndexMap.clear();
437499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
438499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
4403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = loadingView;
4413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (firstView != null) {
4423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstView = firstView;
4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstViewHeight = -1;
444499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
445499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
446499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public int getMappedViewType(int typeId) {
4484ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            int mappedTypeId = mTypeIdIndexMap.get(typeId, -1);
4494ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            if (mappedTypeId == -1) {
4503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // We +1 because the loading view always has view type id of 0
4514ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mappedTypeId = mTypeIdIndexMap.size() + 1;
4524ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mTypeIdIndexMap.put(typeId, mappedTypeId);
4536394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
4544ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return mappedTypeId;
4556394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
4566394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
457a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        public boolean isViewTypeInRange(int typeId) {
458a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            int mappedType = getMappedViewType(typeId);
4594ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return (mappedType < viewTypeCount);
460a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        }
461a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen
462a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        /**
463a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal         * Creates a default loading view. Uses the size of the first row as a guide for the
464a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal         * size of the loading view.
465a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal         */
466a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal        private synchronized View createDefaultLoadingView(ViewGroup parent) {
4673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Context context = parent.getContext();
468a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            if (mFirstViewHeight < 0) {
469a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                try {
470a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    View firstView = mFirstView.apply(parent.getContext(), parent);
471a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    firstView.measure(
472a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
473a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                            MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
474a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    mFirstViewHeight = firstView.getMeasuredHeight();
475a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                } catch (Exception e) {
476a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    float density = context.getResources().getDisplayMetrics().density;
477a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    mFirstViewHeight = Math.round(sDefaultLoadingViewHeight * density);
478a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    Log.w(TAG, "Error inflating first RemoteViews" + e);
479fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung                }
480a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                mFirstView = null;
4816394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
4826394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
483a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            // Compose the loading view text
484a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            TextView loadingTextView = (TextView) LayoutInflater.from(context).inflate(
485a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    com.android.internal.R.layout.remote_views_adapter_default_loading_view,
486a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    parent, false);
487a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            loadingTextView.setHeight(mFirstViewHeight);
488a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            return loadingTextView;
4896394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
4903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
4916394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
4923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
4933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with a single item in the cache.
4943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
4954a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static class RemoteViewsIndexMetaData {
4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId;
4973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        long itemId;
498499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
499591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        public RemoteViewsIndexMetaData(RemoteViews v, long itemId) {
500591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            set(v, itemId);
501499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
502499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
503591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        public void set(RemoteViews v, long id) {
5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            itemId = id;
505a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            if (v != null) {
5063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = v.getLayoutId();
507a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            } else {
5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = 0;
509a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            }
5103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
5113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
512499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
5143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     *
5153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
5164a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static class FixedSizeRemoteViewsCache {
5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final String TAG = "FixedSizeRemoteViewsCache";
5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The meta data related to all the RemoteViews, ie. count, is stable, etc.
5204c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // The meta data objects are made final so that they can be locked on independently
5214c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in
5224c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // the order mTemporaryMetaData followed by mMetaData.
5234ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final RemoteViewsMetaData mMetaData = new RemoteViewsMetaData();
5244ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final RemoteViewsMetaData mTemporaryMetaData = new RemoteViewsMetaData();
5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // greater than or equal to the set of RemoteViews.
5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Note: The reason that we keep this separate from the RemoteViews cache below is that this
5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // we still need to be able to access the mapping of position to meta data, without keeping
5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
5313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // memory and size, but this metadata cache will retain information until the data at the
5323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
5334ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final SparseArray<RemoteViewsIndexMetaData> mIndexMetaData = new SparseArray<>();
5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
5363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // too much memory.
5374ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final SparseArray<RemoteViews> mIndexRemoteViews = new SparseArray<>();
5383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5394ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        // An array of indices to load, Indices which are explicitely requested are set to true,
5404ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        // and those determined by the preloading algorithm to prefetch are set to false.
5414ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final SparseBooleanArray mIndicesToLoad = new SparseBooleanArray();
5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
543b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        // We keep a reference of the last requested index to determine which item to prune the
544b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        // farthest items from when we hit the memory limit
545b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        private int mLastRequestedIndex;
546b90a91c633e99d4559095184af27d1416541d3c0Winson Chung
5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The lower and upper bounds of the preloaded range
5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadLowerBound;
5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadUpperBound;
5513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The bounds of this fixed cache, we will try and fill as many items into the cache up to
5533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the maxCount number of items, or the maxSize memory usage.
5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The maxCountSlack is used to determine if a new position in the cache to be loaded is
5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // preloaded.
5574ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final int mMaxCount;
5584ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private final int mMaxCountSlack;
5593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final float sMaxCountSlackPercent = 0.75f;
560b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024;
5613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public FixedSizeRemoteViewsCache(int maxCacheSize) {
5633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCount = maxCacheSize;
5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
567b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = -1;
5683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
569499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5704ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        public void insert(int position, RemoteViews v, long itemId, int[] visibleWindow) {
5713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the count
5723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.size() >= mMaxCount) {
573591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mIndexRemoteViews.remove(getFarthestPositionFrom(position, visibleWindow));
574499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
575499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the available memory size constraints
577b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position;
578b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) {
5793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Note: This is currently the most naive mechanism for deciding what to prune when
5803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // we hit the memory limit.  In the future, we may want to calculate which index to
5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // remove based on both its position as well as it's current memory usage, as well
5823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // as whether it was directly requested vs. whether it was preloaded by our caching
5833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // mechanism.
5846d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                int trimIndex = getFarthestPositionFrom(pruneFromPosition, visibleWindow);
5856d06825ad8809353785eec2f337a6da65ec187faHenrik Engström
5866d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                // Need to check that this is a valid index, to cover the case where you have only
5876d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                // a single view in the cache, but it's larger than the max memory limit
5886d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                if (trimIndex < 0) {
5896d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                    break;
5906d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                }
5916d06825ad8809353785eec2f337a6da65ec187faHenrik Engström
5926d06825ad8809353785eec2f337a6da65ec187faHenrik Engström                mIndexRemoteViews.remove(trimIndex);
593499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Update the metadata cache
5964ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
5974ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            if (metaData != null) {
598591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                metaData.set(v, itemId);
5993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
600591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId));
601499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.put(position, v);
6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
604499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData getMetaData() {
6063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mMetaData;
6073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
60816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public RemoteViewsMetaData getTemporaryMetaData() {
60916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return mTemporaryMetaData;
61016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViews getRemoteViewsAt(int position) {
6124ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return mIndexRemoteViews.get(position);
613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
6143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsIndexMetaData getMetaDataAt(int position) {
6154ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return mIndexMetaData.get(position);
6163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
617499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
61816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public void commitTemporaryMetaData() {
61916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (mTemporaryMetaData) {
62016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                synchronized (mMetaData) {
62116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mMetaData.set(mTemporaryMetaData);
62216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
62316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
62416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
62516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int getRemoteViewsBitmapMemoryUsage() {
6273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Calculate the memory usage of all the RemoteViews bitmaps being cached
6283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int mem = 0;
6294ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            for (int i = mIndexRemoteViews.size() - 1; i >= 0; i--) {
6304ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                final RemoteViews v = mIndexRemoteViews.valueAt(i);
631aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                if (v != null) {
6325d20064651b9947a4573c9a0eefec90f66eb1b59Adam Cohen                    mem += v.estimateMemoryUsage();
633aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                }
634499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mem;
636499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
637591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen
6384ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        private int getFarthestPositionFrom(int pos, int[] visibleWindow) {
6393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Find the index farthest away and remove that
6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDist = 0;
6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDistIndex = -1;
642591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            int maxDistNotVisible = 0;
643591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            int maxDistIndexNotVisible = -1;
6444ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            for (int i = mIndexRemoteViews.size() - 1; i >= 0; i--) {
6454ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                int index = mIndexRemoteViews.keyAt(i);
6464ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                int dist = Math.abs(index-pos);
6474ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                if (dist > maxDistNotVisible && Arrays.binarySearch(visibleWindow, index) < 0) {
648591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    // maxDistNotVisible/maxDistIndexNotVisible will store the index of the
649591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    // farthest non-visible position
6504ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    maxDistIndexNotVisible = index;
651591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    maxDistNotVisible = dist;
652b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                }
65335fbe2a5923d45ebcdfb3ad74efd1089a05e8737Adam Cohen                if (dist >= maxDist) {
654b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    // maxDist/maxDistIndex will store the index of the farthest position
655591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    // regardless of whether it is visible or not
6564ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    maxDistIndex = index;
6573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDist = dist;
658c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
659c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
660591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            if (maxDistIndexNotVisible > -1) {
661591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                return maxDistIndexNotVisible;
662b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            }
6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return maxDistIndex;
664c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
665c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
6663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void queueRequestedPositionToLoad(int position) {
667b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = position;
6684ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            synchronized (mIndicesToLoad) {
6694ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mIndicesToLoad.put(position, true);
670499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
671499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
67216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) {
6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Check if we need to preload any items
6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) {
6753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int center = (mPreloadUpperBound + mPreloadLowerBound) / 2;
6763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (Math.abs(position - center) < mMaxCountSlack) {
67716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    return false;
678499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
679499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int count = 0;
6823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mMetaData) {
6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                count = mMetaData.count;
684499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6854ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            synchronized (mIndicesToLoad) {
6864ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                // Remove all indices which have not been previously requested.
6874ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                for (int i = mIndicesToLoad.size() - 1; i >= 0; i--) {
6884ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    if (!mIndicesToLoad.valueAt(i)) {
6894ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                        mIndicesToLoad.removeAt(i);
6904ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    }
6914ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                }
6923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the preload indices
6943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int halfMaxCount = mMaxCount / 2;
6953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadLowerBound = position - halfMaxCount;
6963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadUpperBound = position + halfMaxCount;
6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
7004ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    if (mIndexRemoteViews.indexOfKey(i) < 0 && !mIndicesToLoad.get(i)) {
7014ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                        // If the index has not been requested, and has not been loaded.
7024ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                        mIndicesToLoad.put(i, false);
7034ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    }
7043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
705499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
70616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return true;
707499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
7084ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        /** Returns the next index to load */
7094ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        public int getNextIndexToLoad() {
7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We try and prioritize items that have been requested directly, instead
7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // of items that are loaded as a result of the caching mechanism
7124ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            synchronized (mIndicesToLoad) {
7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Prioritize requested indices to be loaded first
7144ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                int index = mIndicesToLoad.indexOfValue(true);
7154ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                if (index < 0) {
7164ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    // Otherwise, preload other indices as necessary
7174ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    index = mIndicesToLoad.indexOfValue(false);
7183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
7194ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                if (index < 0) {
7204ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    return -1;
7214ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                } else {
7224ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    int key = mIndicesToLoad.keyAt(index);
7234ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    mIndicesToLoad.removeAt(index);
7244ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                    return key;
7253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
726c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
7273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
728c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsRemoteViewAt(int position) {
7304ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return mIndexRemoteViews.indexOfKey(position) >= 0;
7313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
7323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsMetaDataAt(int position) {
7334ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return mIndexMetaData.indexOfKey(position) >= 0;
7343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
735499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
7363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
73761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // Note: We do not try and reset the meta data, since that information is still used by
73861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // collection views to validate it's own contents (and will be re-requested if the data
73961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // is invalidated through the notifyDataSetChanged() flow).
74061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
7413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
7423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
743b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = -1;
7443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.clear();
7453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData.clear();
7464ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            synchronized (mIndicesToLoad) {
7474ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                mIndicesToLoad.clear();
748499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
749499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
750499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
751499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
752ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani    static class RemoteViewsCacheKey {
753ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        final Intent.FilterComparison filter;
754ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        final int widgetId;
755ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani
756976e8bd2017d0263216c62111454438cc0f130e3Svetoslav        RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId) {
757ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani            this.filter = filter;
758ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani            this.widgetId = widgetId;
759ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        }
760ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani
761ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        @Override
762ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        public boolean equals(Object o) {
763ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani            if (!(o instanceof RemoteViewsCacheKey)) {
764ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani                return false;
765ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani            }
766ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani            RemoteViewsCacheKey other = (RemoteViewsCacheKey) o;
767976e8bd2017d0263216c62111454438cc0f130e3Svetoslav            return other.filter.equals(filter) && other.widgetId == widgetId;
768ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        }
769ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani
770ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        @Override
771ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        public int hashCode() {
772976e8bd2017d0263216c62111454438cc0f130e3Svetoslav            return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2);
773ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        }
774ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani    }
775ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani
776976e8bd2017d0263216c62111454438cc0f130e3Svetoslav    public RemoteViewsAdapter(Context context, Intent intent,
777976e8bd2017d0263216c62111454438cc0f130e3Svetoslav            RemoteAdapterConnectionCallback callback) {
778499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
779499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
780976e8bd2017d0263216c62111454438cc0f130e3Svetoslav
7819b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        if (mIntent == null) {
7829b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung            throw new IllegalArgumentException("Non-null Intent must be specified.");
7839b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        }
78468d26be719072c029b37ae15f17b8137e8429e38Henrik Baard
78568d26be719072c029b37ae15f17b8137e8429e38Henrik Baard        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
78668d26be719072c029b37ae15f17b8137e8429e38Henrik Baard        mLayoutInflater = LayoutInflater.from(context);
7873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mRequestedViews = new RemoteViewsFrameLayoutRefSet();
788499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
78981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Strip the previously injected app widget id from service intent
79081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
79181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
79281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
79381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
79481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the worker thread
795499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
796499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
797499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
79881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue = new Handler(Looper.myLooper(), this);
799499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
800335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        if (sCacheRemovalThread == null) {
801335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
802335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalThread.start();
803335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper());
804335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        }
805335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
80681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the cache and the service connection on startup
8073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
8083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
809335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
810ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent),
811976e8bd2017d0263216c62111454438cc0f130e3Svetoslav                mAppWidgetId);
812335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
813335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        synchronized(sCachedRemoteViewsCaches) {
814335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            if (sCachedRemoteViewsCaches.containsKey(key)) {
8154a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                mCache = sCachedRemoteViewsCaches.get(key);
8164a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                synchronized (mCache.mMetaData) {
8174a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                    if (mCache.mMetaData.count > 0) {
8184a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                        // As a precautionary measure, we verify that the meta data indicates a
8194a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                        // non-zero count before declaring that data is ready.
8204a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                        mDataReady = true;
8214a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                    }
8224a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                }
823335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            } else {
824335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
8254a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            }
8264a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            if (!mDataReady) {
827335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                requestBindService();
828335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            }
829335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        }
830499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
831499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
832fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    @Override
833fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    protected void finalize() throws Throwable {
834fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        try {
835fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            if (mWorkerThread != null) {
836fc442bdea14289656ef1f537103578eb71faf473Jeff Brown                mWorkerThread.quit();
837fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            }
838fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        } finally {
839fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            super.finalize();
840fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        }
841fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    }
842fc442bdea14289656ef1f537103578eb71faf473Jeff Brown
843335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    public boolean isDataReady() {
844335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        return mDataReady;
845335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    }
846335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
847a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen    public void setRemoteViewsOnClickHandler(OnClickHandler handler) {
848a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen        mRemoteViewsOnClickHandler = handler;
849a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen    }
850a6a4cbc18f7e5a3831d787d3f398e02c5eae6512Adam Cohen
851335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    public void saveRemoteViewsCache() {
852ac2e6dd590b42c374aaeaf75f2531ab167cbdd30Amith Yamasani        final RemoteViewsCacheKey key = new RemoteViewsCacheKey(
853976e8bd2017d0263216c62111454438cc0f130e3Svetoslav                new Intent.FilterComparison(mIntent), mAppWidgetId);
854335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
855335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        synchronized(sCachedRemoteViewsCaches) {
856335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            // If we already have a remove runnable posted for this key, remove it.
857335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
858335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key));
859335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                sRemoteViewsCacheRemoveRunnables.remove(key);
860335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            }
861335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
8624a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            int metaDataCount = 0;
8634a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            int numRemoteViewsCached = 0;
8644a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            synchronized (mCache.mMetaData) {
8654a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                metaDataCount = mCache.mMetaData.count;
866335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            }
8674a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            synchronized (mCache) {
8684a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                numRemoteViewsCached = mCache.mIndexRemoteViews.size();
8694a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            }
8704a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            if (metaDataCount > 0 && numRemoteViewsCached > 0) {
8714a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                sCachedRemoteViewsCaches.put(key, mCache);
8724a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            }
8734a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen
874335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            Runnable r = new Runnable() {
875335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                @Override
876335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                public void run() {
877335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                    synchronized (sCachedRemoteViewsCaches) {
878335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        if (sCachedRemoteViewsCaches.containsKey(key)) {
879335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                            sCachedRemoteViewsCaches.remove(key);
880335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        }
881335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
882335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                            sRemoteViewsCacheRemoveRunnables.remove(key);
883335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        }
884335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                    }
885335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                }
886335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            };
887335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sRemoteViewsCacheRemoveRunnables.put(key, r);
888335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION);
889335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        }
890335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    }
891335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
8923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private void loadNextIndexInBackground() {
8933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mWorkerQueue.post(new Runnable() {
8943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            @Override
8953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            public void run() {
89616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                if (mServiceConnection.isConnected()) {
89716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Get the next index to load
89816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    int position = -1;
89916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    synchronized (mCache) {
9004ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                        position = mCache.getNextIndexToLoad();
90116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
90216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (position > -1) {
90316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Load the item, and notify any existing RemoteViewsFrameLayouts
904591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                        updateRemoteViews(position, true);
9053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
90616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Queue up for the next one to load
90716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        loadNextIndexInBackground();
90816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    } else {
90916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // No more items to load, so queue unbind
91016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        enqueueDeferredUnbindServiceMessage();
91116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
9123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
9133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
9143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
9153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
9163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
91716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void processException(String method, Exception e) {
91816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage());
9193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
92016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // If we encounter a crash when updating, we should reset the metadata & cache and trigger
92116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // a notifyDataSetChanged to update the widget accordingly
92216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
92316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (metaData) {
92416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            metaData.reset();
92516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
92616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
92716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mCache.reset();
92816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
92916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.post(new Runnable() {
93016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            @Override
93116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            public void run() {
93216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                superNotifyDataSetChanged();
9333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
93416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        });
93516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    }
93616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
93716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void updateTemporaryMetaData() {
93816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
93916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
94016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
94116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // get the properties/first view (so that we can use it to
94216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // measure our dummy views)
94316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean hasStableIds = factory.hasStableIds();
94416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            int viewTypeCount = factory.getViewTypeCount();
94516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            int count = factory.getCount();
94616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            RemoteViews loadingView = factory.getLoadingView();
94716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            RemoteViews firstView = null;
94816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if ((count > 0) && (loadingView == null)) {
94916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                firstView = factory.getViewAt(0);
95016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
95116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData();
95216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (tmpMetaData) {
95316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.hasStableIds = hasStableIds;
95416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // We +1 because the base view type is the loading view
95516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.viewTypeCount = viewTypeCount + 1;
95616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.count = count;
95716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.setLoadingViewTemplates(loadingView, firstView);
95816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
9592625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch(RemoteException e) {
96016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            processException("updateMetaData", e);
961fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen        } catch(RuntimeException e) {
962fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen            processException("updateMetaData", e);
9633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
9643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
9653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
966591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen    private void updateRemoteViews(final int position, boolean notifyWhenLoaded) {
96716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
96816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
96916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Load the item information from the remote service
97016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        RemoteViews remoteViews = null;
97116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        long itemId = 0;
97216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
97316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            remoteViews = factory.getViewAt(position);
97416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            itemId = factory.getItemId(position);
9752625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RemoteException e) {
97616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
97716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
97816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Return early to prevent additional work in re-centering the view cache, and
97916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // swapping from the loading view
98016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
9812625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RuntimeException e) {
9822625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
9832625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            return;
98416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
9853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
98616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        if (remoteViews == null) {
98716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // If a null view was returned, we break early to prevent it from getting
98816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // into our cache and causing problems later. The effect is that the child  at this
98916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // position will remain as a loading view until it is updated.
99016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " +
99116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    "returned from RemoteViewsFactory.");
99216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
99316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
994a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen
995a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        int layoutId = remoteViews.getLayoutId();
996a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        RemoteViewsMetaData metaData = mCache.getMetaData();
997a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        boolean viewTypeInRange;
998591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        int cacheCount;
999a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        synchronized (metaData) {
1000a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            viewTypeInRange = metaData.isViewTypeInRange(layoutId);
1001591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            cacheCount = mCache.mMetaData.count;
1002a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        }
100316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
1004a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            if (viewTypeInRange) {
10054ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                int[] visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
1006591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                        mVisibleWindowUpperBound, cacheCount);
1007a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // Cache the RemoteViews we loaded
1008591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mCache.insert(position, remoteViews, itemId, visibleWindow);
1009a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen
1010a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // Notify all the views that we have previously returned for this index that
1011a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // there is new data for it.
1012a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                final RemoteViews rv = remoteViews;
1013a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                if (notifyWhenLoaded) {
1014a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                    mMainQueue.post(new Runnable() {
1015a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        @Override
1016a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        public void run() {
1017a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                            mRequestedViews.notifyOnRemoteViewsLoaded(position, rv);
1018a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        }
1019a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                    });
1020a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                }
1021a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            } else {
1022a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // We need to log an error here, as the the view type count specified by the
1023a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // factory is less than the number of view types returned. We don't return this
1024a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // view to the AdapterView, as this will cause an exception in the hosting process,
1025a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // which contains the associated AdapterView.
1026a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " +
1027a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        " indicated by getViewTypeCount() ");
1028b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen            }
10293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1030499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1031499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
10329b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    public Intent getRemoteViewsServiceIntent() {
10339b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        return mIntent;
10349b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    }
10359b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung
1036499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
10373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
10383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
10393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.count;
10403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1041499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1042499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1043499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
10443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Disallow arbitrary object to be associated with an item for the time being
1045499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
1046499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1047499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1048499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
10493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
10503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
10513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mCache.getMetaDataAt(position).itemId;
10523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
10533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return 0;
10543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1055499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1056499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1057499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
10583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = 0;
10593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
10603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
10613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = mCache.getMetaDataAt(position).typeId;
10623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
10633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return 0;
10643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
10653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
10663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
10673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
10683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
10693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.getMappedViewType(typeId);
10703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
10713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
10723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
10733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
1074b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * This method allows an AdapterView using this Adapter to provide information about which
1075b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * views are currently being displayed. This allows for certain optimizations and preloading
1076b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * which  wouldn't otherwise be possible.
1077b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     */
1078b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    public void setVisibleRangeHint(int lowerBound, int upperBound) {
1079b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        mVisibleWindowLowerBound = lowerBound;
1080b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        mVisibleWindowUpperBound = upperBound;
1081b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    }
1082b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen
1083499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
108416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // "Request" an index so that we can queue it for loading, initiate subsequent
108516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // preloading, etc.
108616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
1087a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            RemoteViews rv = mCache.getRemoteViewsAt(position);
1088a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            boolean isInCache = (rv != null);
108916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean isConnected = mServiceConnection.isConnected();
109016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean hasNewItems = false;
109116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1092ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen            if (convertView != null && convertView instanceof RemoteViewsFrameLayout) {
1093ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen                mRequestedViews.removeView((RemoteViewsFrameLayout) convertView);
1094ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen            }
1095ff067190770281b221398e4ac9992a5e4bb10b27Adam Cohen
10967ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung            if (!isInCache && !isConnected) {
109716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // Requesting bind service will trigger a super.notifyDataSetChanged(), which will
109816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // in turn trigger another request to getView()
109916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                requestBindService();
110016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } else {
11013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Queue up other indices to be preloaded based on this position
110216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
110316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
110416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1105a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            final RemoteViewsFrameLayout layout =
1106a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                    (convertView instanceof RemoteViewsFrameLayout)
1107a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                            ? (RemoteViewsFrameLayout) convertView
1108a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                            : new RemoteViewsFrameLayout(parent.getContext(), mCache);
110916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (isInCache) {
1110a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                layout.onRemoteViewsLoaded(rv, mRemoteViewsOnClickHandler);
1111a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                if (hasNewItems) loadNextIndexInBackground();
111216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } else {
1113a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                // If the views is not loaded, apply the loading view. If the loading view doesn't
1114a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                // exist, the layout will create a default view based on the firstView height.
1115a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                layout.onRemoteViewsLoaded(mCache.getMetaData().mUserLoadingView,
1116a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                        mRemoteViewsOnClickHandler);
1117a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal                mRequestedViews.add(position, layout);
111816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mCache.queueRequestedPositionToLoad(position);
111916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                loadNextIndexInBackground();
11203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
1121a086f0436ad600915db1f8e0dc284d653d152e8dSunny Goyal            return layout;
11223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1123499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1124499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1125499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
11263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
11273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
11283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.viewTypeCount;
11293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1130499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1131499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1132499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
11333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
11343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
11353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.hasStableIds;
11363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1137499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1138499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1139499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
1140499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
1141499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
114316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void onNotifyDataSetChanged() {
114416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Complete the actual notifyDataSetChanged() call initiated earlier
114516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
114616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
114716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            factory.onDataSetChanged();
11482625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RemoteException e) {
114916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
115016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
115116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Return early to prevent from further being notified (since nothing has
115216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // changed)
115316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
11542625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RuntimeException e) {
11552625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
11562625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            return;
115716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
115816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
115916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Flush the cache so that we can reload new items from the service
116016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
116116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mCache.reset();
116216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
116316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
116416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Re-request the new metadata (only after the notification to the factory)
116516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        updateTemporaryMetaData();
11664c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        int newCount;
11674ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        int[] visibleWindow;
11684c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        synchronized(mCache.getTemporaryMetaData()) {
11694c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            newCount = mCache.getTemporaryMetaData().count;
1170591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
1171591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    mVisibleWindowUpperBound, newCount);
11724c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        }
117316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1174b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // Pre-load (our best guess of) the views which are currently visible in the AdapterView.
1175b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // This mitigates flashing and flickering of loading views when a widget notifies that
1176b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // its data has changed.
1177591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        for (int i: visibleWindow) {
11784c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            // Because temporary meta data is only ever modified from this thread (ie.
11794c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            // mWorkerThread), it is safe to assume that count is a valid representation.
11804c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            if (i < newCount) {
1181591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                updateRemoteViews(i, false);
11824c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            }
1183b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        }
1184b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen
118516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Propagate the notification back to the base adapter
118616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.post(new Runnable() {
11876364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            @Override
11886364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            public void run() {
11896364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                synchronized (mCache) {
119016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mCache.commitTemporaryMetaData();
11916364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                }
119261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
119316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                superNotifyDataSetChanged();
119416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                enqueueDeferredUnbindServiceMessage();
11953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
11963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
11976364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung
119816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Reset the notify flagflag
119916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mNotifyDataSetChangedAfterOnServiceConnected = false;
120016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    }
120116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
12024ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal    /**
12034ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal     * Returns a sorted array of all integers between lower and upper.
12044ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal     */
12054ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal    private int[] getVisibleWindow(int lower, int upper, int count) {
12064a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen        // In the case that the window is invalid or uninitialized, return an empty window.
12074a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen        if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) {
12084ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            return new int[0];
12094a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen        }
12104a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen
12114ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal        int[] window;
1212591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        if (lower <= upper) {
12134ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            window = new int[upper + 1 - lower];
12144ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            for (int i = lower, j = 0;  i <= upper; i++, j++){
12154ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                window[j] = i;
1216591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            }
1217591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        } else {
1218591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            // If the upper bound is less than the lower bound it means that the visible window
1219591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            // wraps around.
12204ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            count = Math.max(count, lower);
12214ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            window = new int[count - lower + upper + 1];
12224ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            int j = 0;
12234ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            // Add the entries in sorted order
12244ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            for (int i = 0; i <= upper; i++, j++) {
12254ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                window[j] = i;
1226591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            }
12274ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal            for (int i = lower; i < count; i++, j++) {
12284ea548426666c736587a7fe5ad24ab03a3466cadSunny Goyal                window[j] = i;
1229591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            }
1230591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        }
1231591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        return window;
1232591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen    }
1233591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen
123416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    public void notifyDataSetChanged() {
123516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Dequeue any unbind messages
123616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
123716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
123816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // If we are not connected, queue up the notifyDataSetChanged to be handled when we do
123916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // connect
124016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        if (!mServiceConnection.isConnected()) {
124116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mNotifyDataSetChangedAfterOnServiceConnected = true;
124216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            requestBindService();
124316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
124416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
124516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
124616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mWorkerQueue.post(new Runnable() {
124716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            @Override
124816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            public void run() {
124916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                onNotifyDataSetChanged();
125016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
125116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        });
12523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
12533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1254fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen    void superNotifyDataSetChanged() {
1255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
1256499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1257499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
125881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    @Override
125981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    public boolean handleMessage(Message msg) {
126081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        boolean result = false;
126181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        switch (msg.what) {
126281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        case sUnbindServiceMessageType:
126381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            if (mServiceConnection.isConnected()) {
126416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mServiceConnection.unbind(mContext, mAppWidgetId, mIntent);
126581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            }
126681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            result = true;
126781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
126881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        default:
126981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
127081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
127181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        return result;
127281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
127381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
127481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private void enqueueDeferredUnbindServiceMessage() {
127581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Remove any existing deferred-unbind messages
127681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
127781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
127881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
127981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
1280499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
128181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Try binding the service (which will start it if it's not already running)
1282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
128316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mServiceConnection.bind(mContext, mAppWidgetId, mIntent);
1284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
1285499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
128616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Remove any existing deferred-unbind messages
128716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
1288499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
1289499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1290499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
1291