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