RemoteViewsAdapter.java revision 4a9df8dda5c826ae72a2b5370b9d786ef8d0efd0
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;
21499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap;
223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.util.HashSet;
23c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chungimport java.util.LinkedList;
2481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager;
25499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context;
26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent;
27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler;
28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread;
29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder;
30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper;
3181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message;
322625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException;
33fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log;
34335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohenimport android.util.Pair;
35a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater;
36499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View;
37181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec;
3884bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup;
39499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection;
41499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory;
42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/**
44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews
45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views.
46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
47499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */
4881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
49fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung    private static final String TAG = "RemoteViewsAdapter";
50499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
514a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    // The max number of items in the cache
52b90a91c633e99d4559095184af27d1416541d3c0Winson Chung    private static final int sDefaultCacheSize = 40;
5381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // The delay (in millis) to wait until attempting to unbind from a service after a request.
5481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // This ensures that we don't stay continually bound to the service and that it can be destroyed
5581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // if we need the memory elsewhere in the system.
56b90a91c633e99d4559095184af27d1416541d3c0Winson Chung    private static final int sUnbindServiceDelay = 5000;
57b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
58b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen    // Default height for the default loading view, in case we cannot get inflate the first view
59b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen    private static final int sDefaultLoadingViewHeight = 50;
60b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // Type defs for controlling different messages across the main and worker message queues
624a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static final int sDefaultMessageType = 0;
6381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sUnbindServiceMessageType = 1;
6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
6581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Context mContext;
6681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Intent mIntent;
6781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final int mAppWidgetId;
68a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung    private LayoutInflater mLayoutInflater;
69499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private RemoteViewsAdapterServiceConnection mServiceConnection;
703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private FixedSizeRemoteViewsCache mCache;
72b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private int mVisibleWindowLowerBound;
73b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private int mVisibleWindowUpperBound;
743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
7516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    // A flag to determine whether we should notify data set changed after we connect
7616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private boolean mNotifyDataSetChangedAfterOnServiceConnected = false;
7716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // The set of requested views that are to be notified when the associated RemoteViews are
793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // loaded.
803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private RemoteViewsFrameLayoutRefSet mRequestedViews;
81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private HandlerThread mWorkerThread;
83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    // items may be interrupted within the normally processed queues
84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mWorkerQueue;
85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mMainQueue;
86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
87335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data
88335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // structures;
894a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static final HashMap<Pair<Intent.FilterComparison, Integer>, FixedSizeRemoteViewsCache>
90335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCachedRemoteViewsCaches = new HashMap<Pair<Intent.FilterComparison, Integer>,
914a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                    FixedSizeRemoteViewsCache>();
92335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static final HashMap<Pair<Intent.FilterComparison, Integer>, Runnable>
93335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sRemoteViewsCacheRemoveRunnables = new HashMap<Pair<Intent.FilterComparison, Integer>,
94335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            Runnable>();
95335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static HandlerThread sCacheRemovalThread;
96335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static Handler sCacheRemovalQueue;
97335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
98335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // We keep the cache around for a duration after onSaveInstanceState for use on re-inflation.
99335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this
100335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // duration, the cache is dropped.
101335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private static final int REMOTE_VIEWS_CACHE_DURATION = 5000;
102335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
103335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // Used to indicate to the AdapterView that it can use this Adapter immediately after
104335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    // construction (happens when we have a cached FixedSizeRemoteViewsCache).
105335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    private boolean mDataReady = false;
106335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
107499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
108499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An interface for the RemoteAdapter to notify other classes when adapters
109499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * are actually connected to/disconnected from their actual services.
110499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public interface RemoteAdapterConnectionCallback {
11216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        /**
11316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung         * @return whether the adapter was set or not.
11416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung         */
11516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public boolean onRemoteAdapterConnected();
116499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterDisconnected();
1182148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen
1192148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen        /**
1202148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not
1212148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         * connected yet.
1222148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         */
1232148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen        public void deferNotifyDataSetChanged();
124499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
125499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
126499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
127499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * The service connection that gets populated when the RemoteViewsService is
1283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * bound.  This must be a static inner class to ensure that no references to the outer
1293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
1303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * garbage collected, and would cause us to leak activities due to the caching mechanism for
1313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * FrameLayouts in the adapter).
132499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
13381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static class RemoteViewsAdapterServiceConnection extends
13481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            IRemoteViewsAdapterConnection.Stub {
13516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private boolean mIsConnected;
13616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private boolean mIsConnecting;
1373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private WeakReference<RemoteViewsAdapter> mAdapter;
138499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private IRemoteViewsFactory mRemoteViewsFactory;
139499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
1413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
143499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
14416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void bind(Context context, int appWidgetId, Intent intent) {
14516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (!mIsConnecting) {
14616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                try {
14716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
14816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mgr.bindRemoteViewsService(appWidgetId, intent, asBinder());
14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = true;
15016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                } catch (Exception e) {
15116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage());
15216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = false;
15316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnected = false;
15416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
15716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
15816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void unbind(Context context, int appWidgetId, Intent intent) {
15916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            try {
16016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
16116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mgr.unbindRemoteViewsService(appWidgetId, intent);
16216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnecting = false;
16316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } catch (Exception e) {
16416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage());
16516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnecting = false;
16616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnected = false;
16716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
16816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
16916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void onServiceConnected(IBinder service) {
171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
172c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
17316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Remove any deferred unbind messages
1743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
1753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
17616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
17716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Queue up work that we need to do for the callback to run
1783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            adapter.mWorkerQueue.post(new Runnable() {
179499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
180499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
18116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) {
18216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Handle queued notifyDataSetChanged() if necessary
18316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        adapter.onNotifyDataSetChanged();
18416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    } else {
1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        IRemoteViewsFactory factory =
1863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            adapter.mServiceConnection.getRemoteViewsFactory();
1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        try {
18816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                            if (!factory.isCreated()) {
18916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                // We only call onDataSetChanged() if this is the factory was just
19016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                // create in response to this bind
19116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                factory.onDataSetChanged();
19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                            }
1932625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                        } catch (RemoteException e) {
1943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            Log.e(TAG, "Error notifying factory of data set changed in " +
1953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                        "onServiceConnected(): " + e.getMessage());
1963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // Return early to prevent anything further from being notified
1983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // (effectively nothing has changed)
1993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            return;
2002625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                        } catch (RuntimeException e) {
2012625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                            Log.e(TAG, "Error notifying factory of data set changed in " +
2022625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                                    "onServiceConnected(): " + e.getMessage());
203499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
2043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // Request meta data so that we have up to date data when calling back to
2063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // the remote adapter callback
20716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        adapter.updateTemporaryMetaData();
2083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
20916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Notify the host that we've connected
21061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                        adapter.mMainQueue.post(new Runnable() {
2113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            @Override
2123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            public void run() {
21316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                synchronized (adapter.mCache) {
21416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                    adapter.mCache.commitTemporaryMetaData();
21516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                }
21616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
2173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                final RemoteAdapterConnectionCallback callback =
2183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    adapter.mCallback.get();
2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                if (callback != null) {
2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    callback.onRemoteAdapterConnected();
2213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                }
2223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            }
2233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        });
2243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
22516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
22616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Enqueue unbind message
22716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    adapter.enqueueDeferredUnbindServiceMessage();
22816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnected = true;
22916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = false;
230499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
231499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
232499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
23416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void onServiceDisconnected() {
23516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mIsConnected = false;
23616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mIsConnecting = false;
2373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRemoteViewsFactory = null;
238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
23916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Clear the main/worker queues
2403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
2413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
242c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
24316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            adapter.mMainQueue.post(new Runnable() {
24416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                @Override
24516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                public void run() {
24616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Dequeue any unbind messages
24716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
2483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
24916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
25016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (callback != null) {
25116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        callback.onRemoteAdapterDisconnected();
25216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
25316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
25416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            });
255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
256499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
25716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized IRemoteViewsFactory getRemoteViewsFactory() {
258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mRemoteViewsFactory;
259499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
260499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
26116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized boolean isConnected() {
26216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return mIsConnected;
263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
264499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
266499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
2673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * they are loaded.
269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
270a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen    private static class RemoteViewsFrameLayout extends FrameLayout {
2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsFrameLayout(Context context) {
2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            super(context);
2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
274499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
275499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
2783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         *             successfully.
279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
2803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void onRemoteViewsLoaded(RemoteViews view) {
28161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            try {
28261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                // Remove all the children of this layout first
28361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                removeAllViews();
28461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                addView(view.apply(getContext(), this));
28561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            } catch (Exception e) {
28661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                Log.e(TAG, "Failed to apply RemoteViews.");
28761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            }
2883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
290499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * adapter that have not yet had their RemoteViews loaded.
2943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsFrameLayoutRefSet {
2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences;
297499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsFrameLayoutRefSet() {
2993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>();
300499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
301499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
302499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
3033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
304499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
3053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void add(int position, RemoteViewsFrameLayout layout) {
3063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Integer pos = position;
3073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            LinkedList<RemoteViewsFrameLayout> refs;
3083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create the list if necessary
3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mReferences.containsKey(pos)) {
3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = mReferences.get(pos);
3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = new LinkedList<RemoteViewsFrameLayout>();
3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mReferences.put(pos, refs);
315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Add the references to the list
3183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            refs.add(layout);
319499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
320499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * the associated RemoteViews has loaded.
3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
325a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view) {
32661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            if (view == null) return;
32761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Integer pos = position;
3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mReferences.containsKey(pos)) {
3303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Notify all the references for that position of the newly loaded RemoteViews
3313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos);
3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (final RemoteViewsFrameLayout ref : refs) {
3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    ref.onRemoteViewsLoaded(view);
3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs.clear();
336499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Remove this set from the original mapping
3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mReferences.remove(pos);
339499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
340499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
341499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void clear() {
3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We currently just clear the references, and leave all the previous layouts returned
3473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // in their default state of the loading view.
3483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences.clear();
349499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
3533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with the cache in it's current state.
3543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
3554a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static class RemoteViewsMetaData {
3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int count;
3573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int viewTypeCount;
3583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        boolean hasStableIds;
3593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Used to determine how to construct loading views.  If a loading view is not specified
3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // by the user, then we try and load the first view, and use its height as the height for
3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the default loading view.
3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mUserLoadingView;
3643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mFirstView;
3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int mFirstViewHeight;
3663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // A mapping from type id to a set of unique type ids
36816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>();
3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData() {
3713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            reset();
372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
373499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
37416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public void set(RemoteViewsMetaData d) {
37516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (d) {
37616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                count = d.count;
37716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                viewTypeCount = d.viewTypeCount;
37816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                hasStableIds = d.hasStableIds;
37916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView);
38016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
38116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
38216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            count = 0;
38516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // by default there is at least one dummy view type
3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            viewTypeCount = 1;
3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            hasStableIds = true;
3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = null;
3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstView = null;
3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstViewHeight = 0;
39216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mTypeIdIndexMap.clear();
393499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
394499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = loadingView;
3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (firstView != null) {
3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstView = firstView;
3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstViewHeight = -1;
400499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
401499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
402499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public int getMappedViewType(int typeId) {
4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mTypeIdIndexMap.containsKey(typeId)) {
4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mTypeIdIndexMap.get(typeId);
4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // We +1 because the loading view always has view type id of 0
4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int incrementalTypeId = mTypeIdIndexMap.size() + 1;
4093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mTypeIdIndexMap.put(typeId, incrementalTypeId);
4103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return incrementalTypeId;
4116394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
4126394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
4136394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
414a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        public boolean isViewTypeInRange(int typeId) {
415a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            int mappedType = getMappedViewType(typeId);
416a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            if (mappedType >= viewTypeCount) {
417a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                return false;
418a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            } else {
419a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                return true;
420a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            }
421a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        }
422a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen
4233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
424a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                ViewGroup parent, Object lock, LayoutInflater layoutInflater) {
4253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create and return a new FrameLayout, and setup the references for this position
4263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Context context = parent.getContext();
4273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
4283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create a new loading view
430a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            synchronized (lock) {
431b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                boolean customLoadingViewAvailable = false;
432b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (mUserLoadingView != null) {
434b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // Try to inflate user-specified loading view
435b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    try {
436b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
437b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        loadingView.setTagInternal(com.android.internal.R.id.rowTypeId,
438b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                new Integer(0));
439b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        layout.addView(loadingView);
440b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        customLoadingViewAvailable = true;
441b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    } catch (Exception e) {
442b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        Log.w(TAG, "Error inflating custom loading view, using default loading" +
443b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                "view instead", e);
444b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    }
445b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                }
446b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                if (!customLoadingViewAvailable) {
4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // A default loading view
4483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Use the size of the first row as a guide for the size of the loading view
4493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    if (mFirstViewHeight < 0) {
450b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        try {
451b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            View firstView = mFirstView.apply(parent.getContext(), parent);
452b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            firstView.measure(
453b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
454b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
455b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstViewHeight = firstView.getMeasuredHeight();
456b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstView = null;
457b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        } catch (Exception e) {
458a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                            float density = context.getResources().getDisplayMetrics().density;
459b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstViewHeight = (int)
460b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                    Math.round(sDefaultLoadingViewHeight * density);
461b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstView = null;
462b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            Log.w(TAG, "Error inflating first RemoteViews" + e);
463b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        }
4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
4653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Compose the loading view text
467a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                    TextView loadingTextView = (TextView) layoutInflater.inflate(
468fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen                            com.android.internal.R.layout.remote_views_adapter_default_loading_view,
469fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen                            layout, false);
470a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    loadingTextView.setHeight(mFirstViewHeight);
471a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    loadingTextView.setTag(new Integer(0));
472a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung
473a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    layout.addView(loadingTextView);
474fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung                }
4756394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
4766394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return layout;
4786394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
4793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
4806394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
4823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with a single item in the cache.
4833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
4844a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static class RemoteViewsIndexMetaData {
4853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId;
4863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        long itemId;
487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
488591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        public RemoteViewsIndexMetaData(RemoteViews v, long itemId) {
489591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            set(v, itemId);
490499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
491499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
492591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        public void set(RemoteViews v, long id) {
4933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            itemId = id;
494a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            if (v != null) {
4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = v.getLayoutId();
496a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            } else {
4973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = 0;
498a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            }
4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
5003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
501499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
5033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     *
5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
5054a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen    private static class FixedSizeRemoteViewsCache {
5063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final String TAG = "FixedSizeRemoteViewsCache";
5073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The meta data related to all the RemoteViews, ie. count, is stable, etc.
5094c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // The meta data objects are made final so that they can be locked on independently
5104c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in
5114c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // the order mTemporaryMetaData followed by mMetaData.
5124c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        private final RemoteViewsMetaData mMetaData;
5134c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        private final RemoteViewsMetaData mTemporaryMetaData;
5143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
5163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // greater than or equal to the set of RemoteViews.
5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Note: The reason that we keep this separate from the RemoteViews cache below is that this
5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // we still need to be able to access the mapping of position to meta data, without keeping
5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
5203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // memory and size, but this metadata cache will retain information until the data at the
5213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
5223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData;
5233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // too much memory.
5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, RemoteViews> mIndexRemoteViews;
5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The set of indices that have been explicitly requested by the collection view
5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashSet<Integer> mRequestedIndices;
5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
531b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        // We keep a reference of the last requested index to determine which item to prune the
532b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        // farthest items from when we hit the memory limit
533b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        private int mLastRequestedIndex;
534b90a91c633e99d4559095184af27d1416541d3c0Winson Chung
5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The set of indices to load, including those explicitly requested, as well as those
5363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // determined by the preloading algorithm to be prefetched
5373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashSet<Integer> mLoadIndices;
5383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The lower and upper bounds of the preloaded range
5403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadLowerBound;
5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadUpperBound;
5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The bounds of this fixed cache, we will try and fill as many items into the cache up to
5443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the maxCount number of items, or the maxSize memory usage.
5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The maxCountSlack is used to determine if a new position in the cache to be loaded is
5463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // preloaded.
5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mMaxCount;
5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mMaxCountSlack;
5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final float sMaxCountSlackPercent = 0.75f;
551b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024;
5523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public FixedSizeRemoteViewsCache(int maxCacheSize) {
5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCount = maxCacheSize;
5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMetaData = new RemoteViewsMetaData();
55916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mTemporaryMetaData = new RemoteViewsMetaData();
5603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
5613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRequestedIndices = new HashSet<Integer>();
563b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = -1;
5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mLoadIndices = new HashSet<Integer>();
5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
566499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
567591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        public void insert(int position, RemoteViews v, long itemId,
568591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                ArrayList<Integer> visibleWindow) {
5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the count
5703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.size() >= mMaxCount) {
571591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mIndexRemoteViews.remove(getFarthestPositionFrom(position, visibleWindow));
572499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
573499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the available memory size constraints
575b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position;
576b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) {
5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Note: This is currently the most naive mechanism for deciding what to prune when
5783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // we hit the memory limit.  In the future, we may want to calculate which index to
5793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // remove based on both its position as well as it's current memory usage, as well
5803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // as whether it was directly requested vs. whether it was preloaded by our caching
5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // mechanism.
582591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition, visibleWindow));
583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Update the metadata cache
5863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexMetaData.containsKey(position)) {
5873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
588591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                metaData.set(v, itemId);
5893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
590591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId));
591499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.put(position, v);
5933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
594499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData getMetaData() {
5963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mMetaData;
5973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
59816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public RemoteViewsMetaData getTemporaryMetaData() {
59916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return mTemporaryMetaData;
60016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
6013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViews getRemoteViewsAt(int position) {
6023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.containsKey(position)) {
6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mIndexRemoteViews.get(position);
6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return null;
606499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
6073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsIndexMetaData getMetaDataAt(int position) {
6083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexMetaData.containsKey(position)) {
6093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mIndexMetaData.get(position);
610499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return null;
6123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
61416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public void commitTemporaryMetaData() {
61516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (mTemporaryMetaData) {
61616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                synchronized (mMetaData) {
61716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mMetaData.set(mTemporaryMetaData);
61816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
61916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
62016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
62116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int getRemoteViewsBitmapMemoryUsage() {
6233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Calculate the memory usage of all the RemoteViews bitmaps being cached
6243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int mem = 0;
6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            for (Integer i : mIndexRemoteViews.keySet()) {
6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViews v = mIndexRemoteViews.get(i);
627aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                if (v != null) {
6285d20064651b9947a4573c9a0eefec90f66eb1b59Adam Cohen                    mem += v.estimateMemoryUsage();
629aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                }
630499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mem;
632499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
633591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen
634591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        private int getFarthestPositionFrom(int pos, ArrayList<Integer> visibleWindow) {
6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Find the index farthest away and remove that
6363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDist = 0;
6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDistIndex = -1;
638591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            int maxDistNotVisible = 0;
639591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            int maxDistIndexNotVisible = -1;
6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            for (int i : mIndexRemoteViews.keySet()) {
6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int dist = Math.abs(i-pos);
642591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                if (dist > maxDistNotVisible && !visibleWindow.contains(i)) {
643591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    // maxDistNotVisible/maxDistIndexNotVisible will store the index of the
644591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    // farthest non-visible position
645591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    maxDistIndexNotVisible = i;
646591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    maxDistNotVisible = dist;
647b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                }
64835fbe2a5923d45ebcdfb3ad74efd1089a05e8737Adam Cohen                if (dist >= maxDist) {
649b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    // maxDist/maxDistIndex will store the index of the farthest position
650591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    // regardless of whether it is visible or not
6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDistIndex = i;
6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDist = dist;
653c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
654c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
655591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            if (maxDistIndexNotVisible > -1) {
656591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                return maxDistIndexNotVisible;
657b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            }
6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return maxDistIndex;
659c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
660c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void queueRequestedPositionToLoad(int position) {
662b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = position;
6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
6643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mRequestedIndices.add(position);
6653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.add(position);
666499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
667499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
66816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) {
6693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Check if we need to preload any items
6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) {
6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int center = (mPreloadUpperBound + mPreloadLowerBound) / 2;
6723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (Math.abs(position - center) < mMaxCountSlack) {
67316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    return false;
674499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
675499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
676499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int count = 0;
6783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mMetaData) {
6793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                count = mMetaData.count;
680499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
6823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.clear();
6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the requested indices
6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.addAll(mRequestedIndices);
6863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the preload indices
6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int halfMaxCount = mMaxCount / 2;
6893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadLowerBound = position - halfMaxCount;
6903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadUpperBound = position + halfMaxCount;
6913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
6923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
6933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
6943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.add(i);
6953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
696499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // But remove all the indices that have already been loaded and are cached
6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.removeAll(mIndexRemoteViews.keySet());
699499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
70016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return true;
701499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
702b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        /** Returns the next index to load, and whether that index was directly requested or not */
703b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        public int[] getNextIndexToLoad() {
7043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We try and prioritize items that have been requested directly, instead
7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // of items that are loaded as a result of the caching mechanism
7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Prioritize requested indices to be loaded first
7083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (!mRequestedIndices.isEmpty()) {
7093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Integer i = mRequestedIndices.iterator().next();
7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mRequestedIndices.remove(i);
7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.remove(i);
712b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    return new int[]{i.intValue(), 1};
7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
714499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
7153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Otherwise, preload other indices as necessary
7163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (!mLoadIndices.isEmpty()) {
7173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Integer i = mLoadIndices.iterator().next();
7183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.remove(i);
719b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    return new int[]{i.intValue(), 0};
7203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
721499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
722b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                return new int[]{-1, 0};
723c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
7243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
725c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
7263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsRemoteViewAt(int position) {
7273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mIndexRemoteViews.containsKey(position);
7283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsMetaDataAt(int position) {
7303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mIndexMetaData.containsKey(position);
7313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
732499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
7333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
73461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // Note: We do not try and reset the meta data, since that information is still used by
73561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // collection views to validate it's own contents (and will be re-requested if the data
73661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // is invalidated through the notifyDataSetChanged() flow).
73761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
7383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
7393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
740b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = -1;
7413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.clear();
7423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData.clear();
7433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
7443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mRequestedIndices.clear();
7453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.clear();
746499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
747499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
748499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
749499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
750499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
751499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
752499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
75381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
754a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung        mLayoutInflater = LayoutInflater.from(context);
7559b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        if (mIntent == null) {
7569b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung            throw new IllegalArgumentException("Non-null Intent must be specified.");
7579b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        }
7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mRequestedViews = new RemoteViewsFrameLayoutRefSet();
759499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
76081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Strip the previously injected app widget id from service intent
76181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
76281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
76381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
76481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
76581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the worker thread
766499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
767499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
768499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
76981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue = new Handler(Looper.myLooper(), this);
770499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
771335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        if (sCacheRemovalThread == null) {
772335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner");
773335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalThread.start();
774335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper());
775335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        }
776335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
77781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the cache and the service connection on startup
7783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
7793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
780335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
781335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, Integer>
782335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                (new Intent.FilterComparison(mIntent), mAppWidgetId);
783335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
784335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        synchronized(sCachedRemoteViewsCaches) {
785335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            if (sCachedRemoteViewsCaches.containsKey(key)) {
7864a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                mCache = sCachedRemoteViewsCaches.get(key);
7874a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                synchronized (mCache.mMetaData) {
7884a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                    if (mCache.mMetaData.count > 0) {
7894a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                        // As a precautionary measure, we verify that the meta data indicates a
7904a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                        // non-zero count before declaring that data is ready.
7914a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                        mDataReady = true;
7924a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                    }
7934a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                }
794335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            } else {
795335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
7964a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            }
7974a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            if (!mDataReady) {
798335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                requestBindService();
799335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            }
800335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        }
801499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
802499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
803fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    @Override
804fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    protected void finalize() throws Throwable {
805fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        try {
806fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            if (mWorkerThread != null) {
807fc442bdea14289656ef1f537103578eb71faf473Jeff Brown                mWorkerThread.quit();
808fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            }
809fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        } finally {
810fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            super.finalize();
811fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        }
812fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    }
813fc442bdea14289656ef1f537103578eb71faf473Jeff Brown
814335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    public boolean isDataReady() {
815335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        return mDataReady;
816335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    }
817335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
818335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    public void saveRemoteViewsCache() {
819335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        final Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison,
820335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                Integer> (new Intent.FilterComparison(mIntent), mAppWidgetId);
821335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
822335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        synchronized(sCachedRemoteViewsCaches) {
823335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            // If we already have a remove runnable posted for this key, remove it.
824335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
825335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key));
826335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                sRemoteViewsCacheRemoveRunnables.remove(key);
827335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            }
828335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
8294a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            int metaDataCount = 0;
8304a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            int numRemoteViewsCached = 0;
8314a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            synchronized (mCache.mMetaData) {
8324a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                metaDataCount = mCache.mMetaData.count;
833335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            }
8344a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            synchronized (mCache) {
8354a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                numRemoteViewsCached = mCache.mIndexRemoteViews.size();
8364a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            }
8374a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            if (metaDataCount > 0 && numRemoteViewsCached > 0) {
8384a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen                sCachedRemoteViewsCaches.put(key, mCache);
8394a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            }
8404a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen
841335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            Runnable r = new Runnable() {
842335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                @Override
843335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                public void run() {
844335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                    synchronized (sCachedRemoteViewsCaches) {
845335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        if (sCachedRemoteViewsCaches.containsKey(key)) {
846335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                            sCachedRemoteViewsCaches.remove(key);
847335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        }
848335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) {
849335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                            sRemoteViewsCacheRemoveRunnables.remove(key);
850335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                        }
851335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                    }
852335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen                }
853335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            };
854335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sRemoteViewsCacheRemoveRunnables.put(key, r);
855335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen            sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION);
856335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen        }
857335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen    }
858335c3b681bf1c118d9bf22d1a508c87173632ec6Adam Cohen
8593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private void loadNextIndexInBackground() {
8603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mWorkerQueue.post(new Runnable() {
8613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            @Override
8623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            public void run() {
86316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                if (mServiceConnection.isConnected()) {
86416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Get the next index to load
86516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    int position = -1;
86616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    synchronized (mCache) {
867b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                        int[] res = mCache.getNextIndexToLoad();
868b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                        position = res[0];
86916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
87016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (position > -1) {
87116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Load the item, and notify any existing RemoteViewsFrameLayouts
872591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                        updateRemoteViews(position, true);
8733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
87416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Queue up for the next one to load
87516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        loadNextIndexInBackground();
87616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    } else {
87716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // No more items to load, so queue unbind
87816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        enqueueDeferredUnbindServiceMessage();
87916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
8803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
8813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
8823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
8833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
8843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
88516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void processException(String method, Exception e) {
88616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage());
8873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
88816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // If we encounter a crash when updating, we should reset the metadata & cache and trigger
88916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // a notifyDataSetChanged to update the widget accordingly
89016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
89116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (metaData) {
89216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            metaData.reset();
89316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
89416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
89516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mCache.reset();
89616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
89716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.post(new Runnable() {
89816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            @Override
89916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            public void run() {
90016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                superNotifyDataSetChanged();
9013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
90216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        });
90316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    }
90416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
90516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void updateTemporaryMetaData() {
90616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
90716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
90816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
90916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // get the properties/first view (so that we can use it to
91016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // measure our dummy views)
91116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean hasStableIds = factory.hasStableIds();
91216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            int viewTypeCount = factory.getViewTypeCount();
91316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            int count = factory.getCount();
91416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            RemoteViews loadingView = factory.getLoadingView();
91516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            RemoteViews firstView = null;
91616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if ((count > 0) && (loadingView == null)) {
91716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                firstView = factory.getViewAt(0);
91816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
91916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData();
92016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (tmpMetaData) {
92116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.hasStableIds = hasStableIds;
92216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // We +1 because the base view type is the loading view
92316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.viewTypeCount = viewTypeCount + 1;
92416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.count = count;
92516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.setLoadingViewTemplates(loadingView, firstView);
92616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
9272625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch(RemoteException e) {
92816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            processException("updateMetaData", e);
929fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen        } catch(RuntimeException e) {
930fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen            processException("updateMetaData", e);
9313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
9323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
9333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
934591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen    private void updateRemoteViews(final int position, boolean notifyWhenLoaded) {
93516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
93616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
93716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Load the item information from the remote service
93816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        RemoteViews remoteViews = null;
93916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        long itemId = 0;
94016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
94116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            remoteViews = factory.getViewAt(position);
94216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            itemId = factory.getItemId(position);
9432625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RemoteException e) {
94416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
94516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
94616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Return early to prevent additional work in re-centering the view cache, and
94716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // swapping from the loading view
94816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
9492625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RuntimeException e) {
9502625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
9512625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            return;
95216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
9533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
95416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        if (remoteViews == null) {
95516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // If a null view was returned, we break early to prevent it from getting
95616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // into our cache and causing problems later. The effect is that the child  at this
95716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // position will remain as a loading view until it is updated.
95816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " +
95916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    "returned from RemoteViewsFactory.");
96016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
96116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
962a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen
963a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        int layoutId = remoteViews.getLayoutId();
964a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        RemoteViewsMetaData metaData = mCache.getMetaData();
965a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        boolean viewTypeInRange;
966591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        int cacheCount;
967a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        synchronized (metaData) {
968a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            viewTypeInRange = metaData.isViewTypeInRange(layoutId);
969591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            cacheCount = mCache.mMetaData.count;
970a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen        }
97116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
972a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            if (viewTypeInRange) {
973591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                ArrayList<Integer> visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
974591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                        mVisibleWindowUpperBound, cacheCount);
975a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // Cache the RemoteViews we loaded
976591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                mCache.insert(position, remoteViews, itemId, visibleWindow);
977a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen
978a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // Notify all the views that we have previously returned for this index that
979a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // there is new data for it.
980a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                final RemoteViews rv = remoteViews;
981a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                if (notifyWhenLoaded) {
982a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                    mMainQueue.post(new Runnable() {
983a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        @Override
984a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        public void run() {
985a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                            mRequestedViews.notifyOnRemoteViewsLoaded(position, rv);
986a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        }
987a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                    });
988a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                }
989a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen            } else {
990a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // We need to log an error here, as the the view type count specified by the
991a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // factory is less than the number of view types returned. We don't return this
992a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // view to the AdapterView, as this will cause an exception in the hosting process,
993a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                // which contains the associated AdapterView.
994a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                Log.e(TAG, "Error: widget's RemoteViewsFactory returns more view types than " +
995a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        " indicated by getViewTypeCount() ");
996b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen            }
9973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
998499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
999499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
10009b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    public Intent getRemoteViewsServiceIntent() {
10019b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        return mIntent;
10029b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    }
10039b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung
1004499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
10053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
10063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
10073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.count;
10083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1009499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1010499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1011499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
10123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Disallow arbitrary object to be associated with an item for the time being
1013499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
1014499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1015499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1016499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
10173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
10183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
10193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mCache.getMetaDataAt(position).itemId;
10203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
10213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return 0;
10223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1023499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1024499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1025499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
10263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = 0;
10273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
10283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
10293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = mCache.getMetaDataAt(position).typeId;
10303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
10313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return 0;
10323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
10333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
10343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
10353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
10363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
10373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.getMappedViewType(typeId);
10383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
10393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
10403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
10413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
10423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Returns the item type id for the specified convert view.  Returns -1 if the convert view
10433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * is invalid.
10443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
10453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private int getConvertViewTypeId(View convertView) {
10463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = -1;
1047a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        if (convertView != null) {
1048a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId);
1049a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            if (tag != null) {
1050a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                typeId = (Integer) tag;
1051a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
10523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
10533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        return typeId;
1054499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1055499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1056b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    /**
1057b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * This method allows an AdapterView using this Adapter to provide information about which
1058b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * views are currently being displayed. This allows for certain optimizations and preloading
1059b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * which  wouldn't otherwise be possible.
1060b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     */
1061b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    public void setVisibleRangeHint(int lowerBound, int upperBound) {
1062b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        mVisibleWindowLowerBound = lowerBound;
1063b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        mVisibleWindowUpperBound = upperBound;
1064b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    }
1065b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen
1066499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
106716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // "Request" an index so that we can queue it for loading, initiate subsequent
106816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // preloading, etc.
106916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
107016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean isInCache = mCache.containsRemoteViewAt(position);
107116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean isConnected = mServiceConnection.isConnected();
107216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean hasNewItems = false;
107316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
10747ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung            if (!isInCache && !isConnected) {
107516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // Requesting bind service will trigger a super.notifyDataSetChanged(), which will
107616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // in turn trigger another request to getView()
107716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                requestBindService();
107816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } else {
10793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Queue up other indices to be preloaded based on this position
108016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
108116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
108216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
108316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (isInCache) {
10843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                View convertViewChild = null;
10853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int convertViewTypeId = 0;
1086181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                RemoteViewsFrameLayout layout = null;
1087181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen
1088181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                if (convertView instanceof RemoteViewsFrameLayout) {
1089181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                    layout = (RemoteViewsFrameLayout) convertView;
10903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    convertViewChild = layout.getChildAt(0);
10913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    convertViewTypeId = getConvertViewTypeId(convertViewChild);
10923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
10933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
10943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Second, we try and retrieve the RemoteViews from the cache, returning a loading
10953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // view and queueing it to be loaded if it has not already been loaded.
109616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                Context context = parent.getContext();
109716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                RemoteViews rv = mCache.getRemoteViewsAt(position);
1098aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen                RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position);
1099aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen                int typeId = indexMetaData.typeId;
110016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1101b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                try {
1102b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // Reuse the convert view where possible
1103b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    if (layout != null) {
1104b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        if (convertViewTypeId == typeId) {
1105b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            rv.reapply(context, convertViewChild);
1106b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            return layout;
1107b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        }
1108b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        layout.removeAllViews();
1109b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    } else {
1110b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        layout = new RemoteViewsFrameLayout(context);
11113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
11123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1113b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // Otherwise, create a new view to be returned
1114b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    View newView = rv.apply(context, parent);
1115b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    newView.setTagInternal(com.android.internal.R.id.rowTypeId,
1116b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            new Integer(typeId));
1117b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    layout.addView(newView);
1118b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    return layout;
1119b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
1120b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                } catch (Exception e){
1121b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // We have to make sure that we successfully inflated the RemoteViews, if not
1122b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // we return the loading view instead.
1123b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" +
1124b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            "loading view instead" + e);
1125b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
1126b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    RemoteViewsFrameLayout loadingView = null;
1127b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    final RemoteViewsMetaData metaData = mCache.getMetaData();
1128b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    synchronized (metaData) {
1129a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                        loadingView = metaData.createLoadingView(position, convertView, parent,
1130a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                                mCache, mLayoutInflater);
1131b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    }
1132b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    return loadingView;
1133b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                } finally {
1134b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    if (hasNewItems) loadNextIndexInBackground();
1135b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                }
113616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } else {
113716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // If the cache does not have the RemoteViews at this position, then create a
113816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // loading view and queue the actual position to be loaded in the background
113916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                RemoteViewsFrameLayout loadingView = null;
114016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                final RemoteViewsMetaData metaData = mCache.getMetaData();
114116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                synchronized (metaData) {
1142a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                    loadingView = metaData.createLoadingView(position, convertView, parent,
1143a5a06873d152d16aa47ca9be8501417280b9c9cbAdam Cohen                            mCache, mLayoutInflater);
11443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
114516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
114616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mRequestedViews.add(position, loadingView);
114716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mCache.queueRequestedPositionToLoad(position);
114816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                loadNextIndexInBackground();
114916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
115016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                return loadingView;
11513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
11523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1154499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1155499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
11563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
11573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
11583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.viewTypeCount;
11593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1160499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1161499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1162499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
11633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
11643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
11653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.hasStableIds;
11663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1167499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1168499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
1170499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
1171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1172499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
117316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void onNotifyDataSetChanged() {
117416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Complete the actual notifyDataSetChanged() call initiated earlier
117516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
117616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
117716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            factory.onDataSetChanged();
11782625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RemoteException e) {
117916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
118016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
118116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Return early to prevent from further being notified (since nothing has
118216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // changed)
118316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
11842625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RuntimeException e) {
11852625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
11862625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            return;
118716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
118816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
118916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Flush the cache so that we can reload new items from the service
119016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
119116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mCache.reset();
119216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
119316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
119416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Re-request the new metadata (only after the notification to the factory)
119516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        updateTemporaryMetaData();
11964c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        int newCount;
1197591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        ArrayList<Integer> visibleWindow;
11984c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        synchronized(mCache.getTemporaryMetaData()) {
11994c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            newCount = mCache.getTemporaryMetaData().count;
1200591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            visibleWindow = getVisibleWindow(mVisibleWindowLowerBound,
1201591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                    mVisibleWindowUpperBound, newCount);
12024c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        }
120316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1204b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // Pre-load (our best guess of) the views which are currently visible in the AdapterView.
1205b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // This mitigates flashing and flickering of loading views when a widget notifies that
1206b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // its data has changed.
1207591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        for (int i: visibleWindow) {
12084c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            // Because temporary meta data is only ever modified from this thread (ie.
12094c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            // mWorkerThread), it is safe to assume that count is a valid representation.
12104c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            if (i < newCount) {
1211591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                updateRemoteViews(i, false);
12124c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            }
1213b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        }
1214b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen
121516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Propagate the notification back to the base adapter
121616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.post(new Runnable() {
12176364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            @Override
12186364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            public void run() {
12196364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                synchronized (mCache) {
122016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mCache.commitTemporaryMetaData();
12216364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                }
122261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
122316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                superNotifyDataSetChanged();
122416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                enqueueDeferredUnbindServiceMessage();
12253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
12263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
12276364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung
122816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Reset the notify flagflag
122916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mNotifyDataSetChangedAfterOnServiceConnected = false;
123016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    }
123116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1232591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen    private ArrayList<Integer> getVisibleWindow(int lower, int upper, int count) {
1233591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        ArrayList<Integer> window = new ArrayList<Integer>();
12344a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen
12354a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen        // In the case that the window is invalid or uninitialized, return an empty window.
12364a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen        if ((lower == 0 && upper == 0) || lower < 0 || upper < 0) {
12374a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen            return window;
12384a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen        }
12394a9df8dda5c826ae72a2b5370b9d786ef8d0efd0Adam Cohen
1240591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        if (lower <= upper) {
1241591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            for (int i = lower;  i <= upper; i++){
1242591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                window.add(i);
1243591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            }
1244591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        } else {
1245591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            // If the upper bound is less than the lower bound it means that the visible window
1246591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            // wraps around.
1247591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            for (int i = lower; i < count; i++) {
1248591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                window.add(i);
1249591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            }
1250591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            for (int i = 0; i <= upper; i++) {
1251591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen                window.add(i);
1252591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen            }
1253591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        }
1254591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen        return window;
1255591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen    }
1256591ff978e5ca419fc1baad212419667b8a5e6dc7Adam Cohen
125716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    public void notifyDataSetChanged() {
125816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Dequeue any unbind messages
125916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
126016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
126116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // If we are not connected, queue up the notifyDataSetChanged to be handled when we do
126216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // connect
126316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        if (!mServiceConnection.isConnected()) {
126416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (mNotifyDataSetChangedAfterOnServiceConnected) {
126516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                return;
126616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
126716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
126816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mNotifyDataSetChangedAfterOnServiceConnected = true;
126916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            requestBindService();
127016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
127116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
127216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
127316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mWorkerQueue.post(new Runnable() {
127416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            @Override
127516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            public void run() {
127616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                onNotifyDataSetChanged();
127716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
127816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        });
12793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
12803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1281fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen    void superNotifyDataSetChanged() {
1282499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
1283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1284499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
128581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    @Override
128681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    public boolean handleMessage(Message msg) {
128781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        boolean result = false;
128881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        switch (msg.what) {
128981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        case sUnbindServiceMessageType:
129081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            if (mServiceConnection.isConnected()) {
129116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mServiceConnection.unbind(mContext, mAppWidgetId, mIntent);
129281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            }
129381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            result = true;
129481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
129581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        default:
129681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
129781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
129881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        return result;
129981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
130081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
130181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private void enqueueDeferredUnbindServiceMessage() {
130281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Remove any existing deferred-unbind messages
130381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
130481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
130581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
130681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
1307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
130881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Try binding the service (which will start it if it's not already running)
1309499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
131016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mServiceConnection.bind(mContext, mAppWidgetId, mIntent);
1311499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
1312499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
131316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Remove any existing deferred-unbind messages
131416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
1315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
1316499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1317499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
1318