RemoteViewsAdapter.java revision 4c994986202b5b668a6377a9ad47aa5345046db9
1499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/*
2499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Copyright (C) 2007 The Android Open Source Project
3499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
4499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Licensed under the Apache License, Version 2.0 (the "License");
5499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * you may not use this file except in compliance with the License.
6499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * You may obtain a copy of the License at
7499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
8499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *      http://www.apache.org/licenses/LICENSE-2.0
9499cb9f516062b654952d282f211bee44c31a3c2Winson Chung *
10499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * Unless required by applicable law or agreed to in writing, software
11499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * distributed under the License is distributed on an "AS IS" BASIS,
12499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * See the License for the specific language governing permissions and
14499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * limitations under the License.
15499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
16499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
17499cb9f516062b654952d282f211bee44c31a3c2Winson Chungpackage android.widget;
18499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.lang.ref.WeakReference;
20499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport java.util.HashMap;
213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport java.util.HashSet;
22c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chungimport java.util.LinkedList;
23499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager;
25499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context;
26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent;
27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler;
28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread;
29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder;
30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper;
3181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message;
322625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohenimport android.os.RemoteException;
33fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log;
34a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater;
35499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View;
36181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec;
3784bbb020217adcdfe0694c44ccab57e208ffde16Winson Chungimport android.view.ViewGroup;
38499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport com.android.internal.widget.IRemoteViewsAdapterConnection;
40499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport com.android.internal.widget.IRemoteViewsFactory;
41499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
42499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/**
43499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * An adapter to a RemoteViewsService which fetches and caches RemoteViews
44499cb9f516062b654952d282f211bee44c31a3c2Winson Chung * to be later inflated as child views.
45499cb9f516062b654952d282f211bee44c31a3c2Winson Chung */
46499cb9f516062b654952d282f211bee44c31a3c2Winson Chung/** @hide */
4781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungpublic class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
48fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung    private static final String TAG = "RemoteViewsAdapter";
49499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // The max number of items in the cache
51b90a91c633e99d4559095184af27d1416541d3c0Winson Chung    private static final int sDefaultCacheSize = 40;
5281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // The delay (in millis) to wait until attempting to unbind from a service after a request.
5381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // This ensures that we don't stay continually bound to the service and that it can be destroyed
5481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // if we need the memory elsewhere in the system.
55b90a91c633e99d4559095184af27d1416541d3c0Winson Chung    private static final int sUnbindServiceDelay = 5000;
56b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
57b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen    // Default height for the default loading view, in case we cannot get inflate the first view
58b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen    private static final int sDefaultLoadingViewHeight = 50;
59b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
6081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // Type defs for controlling different messages across the main and worker message queues
6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sDefaultMessageType = 0;
6281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sUnbindServiceMessageType = 1;
6381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
6481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Context mContext;
6581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Intent mIntent;
6681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final int mAppWidgetId;
67a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung    private LayoutInflater mLayoutInflater;
68499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private RemoteViewsAdapterServiceConnection mServiceConnection;
693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private FixedSizeRemoteViewsCache mCache;
71b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private int mVisibleWindowLowerBound;
72b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private int mVisibleWindowUpperBound;
733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
7416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    // A flag to determine whether we should notify data set changed after we connect
7516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private boolean mNotifyDataSetChangedAfterOnServiceConnected = false;
7616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // The set of requested views that are to be notified when the associated RemoteViews are
783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // loaded.
793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private RemoteViewsFrameLayoutRefSet mRequestedViews;
80499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private HandlerThread mWorkerThread;
82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    // items may be interrupted within the normally processed queues
83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mWorkerQueue;
84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mMainQueue;
85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
87499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An interface for the RemoteAdapter to notify other classes when adapters
88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * are actually connected to/disconnected from their actual services.
89499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
90499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public interface RemoteAdapterConnectionCallback {
9116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        /**
9216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung         * @return whether the adapter was set or not.
9316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung         */
9416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public boolean onRemoteAdapterConnected();
95499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterDisconnected();
972148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen
982148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen        /**
992148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         * This defers a notifyDataSetChanged on the pending RemoteViewsAdapter if it has not
1002148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         * connected yet.
1012148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen         */
1022148d43eb23c702e834c93ae427f822f32d280a2Adam Cohen        public void deferNotifyDataSetChanged();
103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
104499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * The service connection that gets populated when the RemoteViewsService is
1073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * bound.  This must be a static inner class to ensure that no references to the outer
1083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
1093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * garbage collected, and would cause us to leak activities due to the caching mechanism for
1103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * FrameLayouts in the adapter).
111499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
11281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static class RemoteViewsAdapterServiceConnection extends
11381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            IRemoteViewsAdapterConnection.Stub {
11416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private boolean mIsConnected;
11516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private boolean mIsConnecting;
1163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private WeakReference<RemoteViewsAdapter> mAdapter;
117499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private IRemoteViewsFactory mRemoteViewsFactory;
118499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
1203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
121499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
122499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
12316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void bind(Context context, int appWidgetId, Intent intent) {
12416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (!mIsConnecting) {
12516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                try {
12616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
12716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mgr.bindRemoteViewsService(appWidgetId, intent, asBinder());
12816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = true;
12916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                } catch (Exception e) {
13016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage());
13116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = false;
13216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnected = false;
13316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
13416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
13516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
13616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
13716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void unbind(Context context, int appWidgetId, Intent intent) {
13816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            try {
13916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
14016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mgr.unbindRemoteViewsService(appWidgetId, intent);
14116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnecting = false;
14216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } catch (Exception e) {
14316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage());
14416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnecting = false;
14516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mIsConnected = false;
14616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
14716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
14816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
14916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void onServiceConnected(IBinder service) {
150499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
151c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
15216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Remove any deferred unbind messages
1533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
1543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
15516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
15616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Queue up work that we need to do for the callback to run
1573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            adapter.mWorkerQueue.post(new Runnable() {
158499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
159499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
16016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (adapter.mNotifyDataSetChangedAfterOnServiceConnected) {
16116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Handle queued notifyDataSetChanged() if necessary
16216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        adapter.onNotifyDataSetChanged();
16316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    } else {
1643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        IRemoteViewsFactory factory =
1653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            adapter.mServiceConnection.getRemoteViewsFactory();
1663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        try {
16716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                            if (!factory.isCreated()) {
16816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                // We only call onDataSetChanged() if this is the factory was just
16916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                // create in response to this bind
17016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                factory.onDataSetChanged();
17116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                            }
1722625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                        } catch (RemoteException e) {
1733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            Log.e(TAG, "Error notifying factory of data set changed in " +
1743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                        "onServiceConnected(): " + e.getMessage());
1753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // Return early to prevent anything further from being notified
1773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // (effectively nothing has changed)
1783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            return;
1792625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                        } catch (RuntimeException e) {
1802625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                            Log.e(TAG, "Error notifying factory of data set changed in " +
1812625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen                                    "onServiceConnected(): " + e.getMessage());
182499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
1833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // Request meta data so that we have up to date data when calling back to
1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // the remote adapter callback
18616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        adapter.updateTemporaryMetaData();
1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
18816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Notify the host that we've connected
18961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                        adapter.mMainQueue.post(new Runnable() {
1903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            @Override
1913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            public void run() {
19216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                synchronized (adapter.mCache) {
19316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                    adapter.mCache.commitTemporaryMetaData();
19416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                                }
19516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                final RemoteAdapterConnectionCallback callback =
1973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    adapter.mCallback.get();
1983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                if (callback != null) {
1993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    callback.onRemoteAdapterConnected();
2003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                }
2013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            }
2023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        });
2033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
20416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
20516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Enqueue unbind message
20616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    adapter.enqueueDeferredUnbindServiceMessage();
20716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnected = true;
20816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mIsConnecting = false;
209499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
210499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
211499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
212499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
21316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized void onServiceDisconnected() {
21416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mIsConnected = false;
21516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mIsConnecting = false;
2163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRemoteViewsFactory = null;
217499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
21816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Clear the main/worker queues
2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
221c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
22216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            adapter.mMainQueue.post(new Runnable() {
22316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                @Override
22416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                public void run() {
22516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Dequeue any unbind messages
22616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
22816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
22916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (callback != null) {
23016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        callback.onRemoteAdapterDisconnected();
23116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
23216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
23316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            });
234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
235499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
23616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized IRemoteViewsFactory getRemoteViewsFactory() {
237499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mRemoteViewsFactory;
238499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
239499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
24016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public synchronized boolean isConnected() {
24116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return mIsConnected;
242499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
243499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
244499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
245499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
2463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
2473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * they are loaded.
248499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
2493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsFrameLayout extends FrameLayout {
2503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsFrameLayout(Context context) {
2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            super(context);
2523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
2553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
2563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
2573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         *             successfully.
258499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
2593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void onRemoteViewsLoaded(RemoteViews view) {
26061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            try {
26161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                // Remove all the children of this layout first
26261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                removeAllViews();
26361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                addView(view.apply(getContext(), this));
26461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            } catch (Exception e) {
26561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                Log.e(TAG, "Failed to apply RemoteViews.");
26661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            }
2673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
269499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * adapter that have not yet had their RemoteViews loaded.
2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
2743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsFrameLayoutRefSet {
2753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences;
276499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsFrameLayoutRefSet() {
2783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>();
279499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
280499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
281499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
2823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
283499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
2843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void add(int position, RemoteViewsFrameLayout layout) {
2853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Integer pos = position;
2863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            LinkedList<RemoteViewsFrameLayout> refs;
2873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create the list if necessary
2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mReferences.containsKey(pos)) {
2903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = mReferences.get(pos);
2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = new LinkedList<RemoteViewsFrameLayout>();
2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mReferences.put(pos, refs);
294499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Add the references to the list
2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            refs.add(layout);
298499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
3023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * the associated RemoteViews has loaded.
3033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
3043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
30561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            if (view == null) return;
30661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
3073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Integer pos = position;
3083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mReferences.containsKey(pos)) {
3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Notify all the references for that position of the newly loaded RemoteViews
3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos);
3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (final RemoteViewsFrameLayout ref : refs) {
3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    ref.onRemoteViewsLoaded(view);
3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs.clear();
315499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Remove this set from the original mapping
3173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mReferences.remove(pos);
318499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
319499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
320499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void clear() {
3253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We currently just clear the references, and leave all the previous layouts returned
3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // in their default state of the loading view.
3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences.clear();
328499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
330499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with the cache in it's current state.
3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsMetaData {
3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int count;
3363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int viewTypeCount;
3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        boolean hasStableIds;
3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Used to determine how to construct loading views.  If a loading view is not specified
3403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // by the user, then we try and load the first view, and use its height as the height for
3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the default loading view.
3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mUserLoadingView;
3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mFirstView;
3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int mFirstViewHeight;
3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // A mapping from type id to a set of unique type ids
34716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        private final HashMap<Integer, Integer> mTypeIdIndexMap = new HashMap<Integer, Integer>();
3483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData() {
3503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            reset();
351499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
352499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
35316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public void set(RemoteViewsMetaData d) {
35416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (d) {
35516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                count = d.count;
35616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                viewTypeCount = d.viewTypeCount;
35716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                hasStableIds = d.hasStableIds;
35816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                setLoadingViewTemplates(d.mUserLoadingView, d.mFirstView);
35916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
36016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
36116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            count = 0;
36416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // by default there is at least one dummy view type
3663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            viewTypeCount = 1;
3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            hasStableIds = true;
3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = null;
3693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstView = null;
3703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstViewHeight = 0;
37116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mTypeIdIndexMap.clear();
372499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
373499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
3753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = loadingView;
3763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (firstView != null) {
3773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstView = firstView;
3783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstViewHeight = -1;
379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
380499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
381499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public int getMappedViewType(int typeId) {
3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mTypeIdIndexMap.containsKey(typeId)) {
3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mTypeIdIndexMap.get(typeId);
3853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // We +1 because the loading view always has view type id of 0
3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int incrementalTypeId = mTypeIdIndexMap.size() + 1;
3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mTypeIdIndexMap.put(typeId, incrementalTypeId);
3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return incrementalTypeId;
3906394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
3916394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
3926394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
3943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                ViewGroup parent) {
3953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create and return a new FrameLayout, and setup the references for this position
3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Context context = parent.getContext();
3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create a new loading view
4003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mCache) {
401b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                boolean customLoadingViewAvailable = false;
402b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (mUserLoadingView != null) {
404b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // Try to inflate user-specified loading view
405b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    try {
406b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
407b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        loadingView.setTagInternal(com.android.internal.R.id.rowTypeId,
408b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                new Integer(0));
409b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        layout.addView(loadingView);
410b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        customLoadingViewAvailable = true;
411b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    } catch (Exception e) {
412b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        Log.w(TAG, "Error inflating custom loading view, using default loading" +
413b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                "view instead", e);
414b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    }
415b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                }
416b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                if (!customLoadingViewAvailable) {
4173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // A default loading view
4183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Use the size of the first row as a guide for the size of the loading view
4193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    if (mFirstViewHeight < 0) {
420b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        try {
421b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            View firstView = mFirstView.apply(parent.getContext(), parent);
422b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            firstView.measure(
423b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
424b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
425b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstViewHeight = firstView.getMeasuredHeight();
426b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstView = null;
427b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        } catch (Exception e) {
428b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            float density = mContext.getResources().getDisplayMetrics().density;
429b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstViewHeight = (int)
430b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                                    Math.round(sDefaultLoadingViewHeight * density);
431b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            mFirstView = null;
432b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            Log.w(TAG, "Error inflating first RemoteViews" + e);
433b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        }
4343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
4353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Compose the loading view text
437a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    TextView loadingTextView = (TextView) mLayoutInflater.inflate(
438fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen                            com.android.internal.R.layout.remote_views_adapter_default_loading_view,
439fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen                            layout, false);
440a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    loadingTextView.setHeight(mFirstViewHeight);
441a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    loadingTextView.setTag(new Integer(0));
442a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung
443a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    layout.addView(loadingTextView);
444fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung                }
4456394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
4466394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return layout;
4486394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
4493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
4506394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
4513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
4523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with a single item in the cache.
4533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
4543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsIndexMetaData {
4553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId;
4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        long itemId;
457b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        boolean isRequested;
458499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
459b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        public RemoteViewsIndexMetaData(RemoteViews v, long itemId, boolean requested) {
460b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            set(v, itemId, requested);
461499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
462499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
463b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        public void set(RemoteViews v, long id, boolean requested) {
4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            itemId = id;
4653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (v != null)
4663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = v.getLayoutId();
4673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            else
4683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = 0;
469b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            isRequested = requested;
4703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
4713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
472499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
4743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     *
4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
4763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class FixedSizeRemoteViewsCache {
4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final String TAG = "FixedSizeRemoteViewsCache";
4783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The meta data related to all the RemoteViews, ie. count, is stable, etc.
4804c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // The meta data objects are made final so that they can be locked on independently
4814c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // of the FixedSizeRemoteViewsCache. If we ever lock on both meta data objects, it is in
4824c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        // the order mTemporaryMetaData followed by mMetaData.
4834c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        private final RemoteViewsMetaData mMetaData;
4844c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        private final RemoteViewsMetaData mTemporaryMetaData;
4853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
4873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // greater than or equal to the set of RemoteViews.
4883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Note: The reason that we keep this separate from the RemoteViews cache below is that this
4893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // we still need to be able to access the mapping of position to meta data, without keeping
4903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
4913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // memory and size, but this metadata cache will retain information until the data at the
4923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
4933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData;
4943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // too much memory.
4973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, RemoteViews> mIndexRemoteViews;
4983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The set of indices that have been explicitly requested by the collection view
5003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashSet<Integer> mRequestedIndices;
5013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
502b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        // We keep a reference of the last requested index to determine which item to prune the
503b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        // farthest items from when we hit the memory limit
504b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        private int mLastRequestedIndex;
505b90a91c633e99d4559095184af27d1416541d3c0Winson Chung
5063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The set of indices to load, including those explicitly requested, as well as those
5073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // determined by the preloading algorithm to be prefetched
5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashSet<Integer> mLoadIndices;
5093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The lower and upper bounds of the preloaded range
5113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadLowerBound;
5123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadUpperBound;
5133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The bounds of this fixed cache, we will try and fill as many items into the cache up to
5153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the maxCount number of items, or the maxSize memory usage.
5163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The maxCountSlack is used to determine if a new position in the cache to be loaded is
5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // preloaded.
5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mMaxCount;
5203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mMaxCountSlack;
5213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final float sMaxCountSlackPercent = 0.75f;
522b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        private static final int sMaxMemoryLimitInBytes = 2 * 1024 * 1024;
5233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public FixedSizeRemoteViewsCache(int maxCacheSize) {
5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCount = maxCacheSize;
5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMetaData = new RemoteViewsMetaData();
53016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mTemporaryMetaData = new RemoteViewsMetaData();
5313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
5323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
5333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRequestedIndices = new HashSet<Integer>();
534b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = -1;
5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mLoadIndices = new HashSet<Integer>();
5363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
537499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
538b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        public void insert(int position, RemoteViews v, long itemId, boolean isRequested) {
5393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the count
5403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.size() >= mMaxCount) {
5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
542499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
543499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the available memory size constraints
545b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            int pruneFromPosition = (mLastRequestedIndex > -1) ? mLastRequestedIndex : position;
546b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryLimitInBytes) {
5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Note: This is currently the most naive mechanism for deciding what to prune when
5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // we hit the memory limit.  In the future, we may want to calculate which index to
5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // remove based on both its position as well as it's current memory usage, as well
5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // as whether it was directly requested vs. whether it was preloaded by our caching
5513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // mechanism.
552b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition));
553499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Update the metadata cache
5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexMetaData.containsKey(position)) {
5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
558b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                metaData.set(v, itemId, isRequested);
5593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
560b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId, isRequested));
561499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.put(position, v);
5633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
564499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData getMetaData() {
5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mMetaData;
5673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
56816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public RemoteViewsMetaData getTemporaryMetaData() {
56916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return mTemporaryMetaData;
57016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
5713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViews getRemoteViewsAt(int position) {
5723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.containsKey(position)) {
5733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mIndexRemoteViews.get(position);
5743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
575499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return null;
576499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsIndexMetaData getMetaDataAt(int position) {
5783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexMetaData.containsKey(position)) {
5793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mIndexMetaData.get(position);
580499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return null;
5823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
58416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public void commitTemporaryMetaData() {
58516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (mTemporaryMetaData) {
58616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                synchronized (mMetaData) {
58716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mMetaData.set(mTemporaryMetaData);
58816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                }
58916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
59016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
59116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
5923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int getRemoteViewsBitmapMemoryUsage() {
5933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Calculate the memory usage of all the RemoteViews bitmaps being cached
5943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int mem = 0;
5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            for (Integer i : mIndexRemoteViews.keySet()) {
5963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViews v = mIndexRemoteViews.get(i);
597aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                if (v != null) {
598aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                    mem += v.estimateBitmapMemoryUsage();
599aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                }
600499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mem;
602499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
6033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int getFarthestPositionFrom(int pos) {
6043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Find the index farthest away and remove that
6053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDist = 0;
6063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDistIndex = -1;
607b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            int maxDistNonRequested = 0;
608b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            int maxDistIndexNonRequested = -1;
6093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            for (int i : mIndexRemoteViews.keySet()) {
6103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int dist = Math.abs(i-pos);
611b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                if (dist > maxDistNonRequested && !mIndexMetaData.get(i).isRequested) {
612b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    // maxDistNonRequested/maxDistIndexNonRequested will store the index of the
613b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    // farthest non-requested position
614b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    maxDistIndexNonRequested = i;
615b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    maxDistNonRequested = dist;
616b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                }
6173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (dist > maxDist) {
618b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    // maxDist/maxDistIndex will store the index of the farthest position
619b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    // regardless of whether it was directly requested or not
6203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDistIndex = i;
6213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDist = dist;
622c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
623c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
624b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            if (maxDistIndexNonRequested > -1) {
625b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                return maxDistIndexNonRequested;
626b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            }
6273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return maxDistIndex;
628c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
629c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
6303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void queueRequestedPositionToLoad(int position) {
631b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = position;
6323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
6333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mRequestedIndices.add(position);
6343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.add(position);
635499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
636499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
63716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        public boolean queuePositionsToBePreloadedFromRequestedPosition(int position) {
6383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Check if we need to preload any items
6393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) {
6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int center = (mPreloadUpperBound + mPreloadLowerBound) / 2;
6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (Math.abs(position - center) < mMaxCountSlack) {
64216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    return false;
643499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
644499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
645499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int count = 0;
6473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mMetaData) {
6483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                count = mMetaData.count;
649499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
6503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.clear();
6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the requested indices
6543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.addAll(mRequestedIndices);
6553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the preload indices
6573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int halfMaxCount = mMaxCount / 2;
6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadLowerBound = position - halfMaxCount;
6593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadUpperBound = position + halfMaxCount;
6603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
6623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.add(i);
6643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
665499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // But remove all the indices that have already been loaded and are cached
6673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.removeAll(mIndexRemoteViews.keySet());
668499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
66916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return true;
670499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
671b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        /** Returns the next index to load, and whether that index was directly requested or not */
672b90a91c633e99d4559095184af27d1416541d3c0Winson Chung        public int[] getNextIndexToLoad() {
6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We try and prioritize items that have been requested directly, instead
6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // of items that are loaded as a result of the caching mechanism
6753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
6763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Prioritize requested indices to be loaded first
6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (!mRequestedIndices.isEmpty()) {
6783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Integer i = mRequestedIndices.iterator().next();
6793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mRequestedIndices.remove(i);
6803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.remove(i);
681b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    return new int[]{i.intValue(), 1};
6823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
683499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Otherwise, preload other indices as necessary
6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (!mLoadIndices.isEmpty()) {
6863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Integer i = mLoadIndices.iterator().next();
6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.remove(i);
688b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    return new int[]{i.intValue(), 0};
6893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
690499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
691b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                return new int[]{-1, 0};
692c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
6933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
694c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
6953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsRemoteViewAt(int position) {
6963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mIndexRemoteViews.containsKey(position);
6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsMetaDataAt(int position) {
6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mIndexMetaData.containsKey(position);
7003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
701499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
7023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
70361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // Note: We do not try and reset the meta data, since that information is still used by
70461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // collection views to validate it's own contents (and will be re-requested if the data
70561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // is invalidated through the notifyDataSetChanged() flow).
70661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
7083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
709b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mLastRequestedIndex = -1;
7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.clear();
7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData.clear();
7123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mRequestedIndices.clear();
7143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.clear();
715499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
716499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
717499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
718499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
719499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
720499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
721499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
72281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
723a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung        mLayoutInflater = LayoutInflater.from(context);
7249b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        if (mIntent == null) {
7259b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung            throw new IllegalArgumentException("Non-null Intent must be specified.");
7269b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        }
7273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mRequestedViews = new RemoteViewsFrameLayoutRefSet();
728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
72981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Strip the previously injected app widget id from service intent
73081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
73181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
73281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
73381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
73481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the worker thread
735499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
736499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
737499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
73881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue = new Handler(Looper.myLooper(), this);
739499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
74081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the cache and the service connection on startup
74181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
7423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
7433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
744499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
745499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
746499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
747fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    @Override
748fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    protected void finalize() throws Throwable {
749fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        try {
750fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            if (mWorkerThread != null) {
751fc442bdea14289656ef1f537103578eb71faf473Jeff Brown                mWorkerThread.quit();
752fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            }
753fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        } finally {
754fc442bdea14289656ef1f537103578eb71faf473Jeff Brown            super.finalize();
755fc442bdea14289656ef1f537103578eb71faf473Jeff Brown        }
756fc442bdea14289656ef1f537103578eb71faf473Jeff Brown    }
757fc442bdea14289656ef1f537103578eb71faf473Jeff Brown
7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private void loadNextIndexInBackground() {
7593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mWorkerQueue.post(new Runnable() {
7603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            @Override
7613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            public void run() {
76216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                if (mServiceConnection.isConnected()) {
76316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    // Get the next index to load
76416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    int position = -1;
765b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                    boolean isRequested = false;
76616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    synchronized (mCache) {
767b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                        int[] res = mCache.getNextIndexToLoad();
768b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                        position = res[0];
769b90a91c633e99d4559095184af27d1416541d3c0Winson Chung                        isRequested = res[1] > 0;
77016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
77116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    if (position > -1) {
77216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Load the item, and notify any existing RemoteViewsFrameLayouts
773b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                        updateRemoteViews(position, isRequested, true);
7743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
77516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // Queue up for the next one to load
77616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        loadNextIndexInBackground();
77716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    } else {
77816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        // No more items to load, so queue unbind
77916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                        enqueueDeferredUnbindServiceMessage();
78016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    }
7813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
7823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
7833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
7843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
7853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
78616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void processException(String method, Exception e) {
78716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        Log.e("RemoteViewsAdapter", "Error in " + method + ": " + e.getMessage());
7883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
78916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // If we encounter a crash when updating, we should reset the metadata & cache and trigger
79016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // a notifyDataSetChanged to update the widget accordingly
79116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
79216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (metaData) {
79316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            metaData.reset();
79416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
79516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
79616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mCache.reset();
79716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
79816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.post(new Runnable() {
79916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            @Override
80016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            public void run() {
80116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                superNotifyDataSetChanged();
8023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
80316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        });
80416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    }
80516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
80616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void updateTemporaryMetaData() {
80716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
80816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
80916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
81016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // get the properties/first view (so that we can use it to
81116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // measure our dummy views)
81216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean hasStableIds = factory.hasStableIds();
81316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            int viewTypeCount = factory.getViewTypeCount();
81416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            int count = factory.getCount();
81516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            RemoteViews loadingView = factory.getLoadingView();
81616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            RemoteViews firstView = null;
81716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if ((count > 0) && (loadingView == null)) {
81816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                firstView = factory.getViewAt(0);
81916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
82016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            final RemoteViewsMetaData tmpMetaData = mCache.getTemporaryMetaData();
82116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            synchronized (tmpMetaData) {
82216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.hasStableIds = hasStableIds;
82316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // We +1 because the base view type is the loading view
82416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.viewTypeCount = viewTypeCount + 1;
82516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.count = count;
82616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                tmpMetaData.setLoadingViewTemplates(loadingView, firstView);
82716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
8282625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch(RemoteException e) {
82916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            processException("updateMetaData", e);
830fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen        } catch(RuntimeException e) {
831fa2e3ff3d33b2cbb452d22439e98b59e07f70f3dAdam Cohen            processException("updateMetaData", e);
8323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
8343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
835b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    private void updateRemoteViews(final int position, boolean isRequested, boolean
836b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen            notifyWhenLoaded) {
83716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
83816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
83916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Load the item information from the remote service
84016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        RemoteViews remoteViews = null;
84116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        long itemId = 0;
84216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
84316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            remoteViews = factory.getViewAt(position);
84416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            itemId = factory.getItemId(position);
8452625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RemoteException e) {
84616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
84716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
84816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Return early to prevent additional work in re-centering the view cache, and
84916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // swapping from the loading view
85016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
8512625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RuntimeException e) {
8522625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
8532625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            return;
85416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
8553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
85616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        if (remoteViews == null) {
85716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // If a null view was returned, we break early to prevent it from getting
85816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // into our cache and causing problems later. The effect is that the child  at this
85916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // position will remain as a loading view until it is updated.
86016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " +
86116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    "returned from RemoteViewsFactory.");
86216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
86316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
86416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
86516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Cache the RemoteViews we loaded
866b90a91c633e99d4559095184af27d1416541d3c0Winson Chung            mCache.insert(position, remoteViews, itemId, isRequested);
86716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
86816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Notify all the views that we have previously returned for this index that
86916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // there is new data for it.
87016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            final RemoteViews rv = remoteViews;
87116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            final int typeId = mCache.getMetaDataAt(position).typeId;
872b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen            if (notifyWhenLoaded) {
873b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                mMainQueue.post(new Runnable() {
874b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                    @Override
875b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                    public void run() {
876b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                        mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
877b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                    }
878b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen                });
879b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen            }
8803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
881499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
882499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
8839b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    public Intent getRemoteViewsServiceIntent() {
8849b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        return mIntent;
8859b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    }
8869b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung
887499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
8883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
8893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
8903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.count;
8913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
892499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
893499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
894499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
8953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Disallow arbitrary object to be associated with an item for the time being
896499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
897499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
898499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
899499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
9003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
9013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
9023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mCache.getMetaDataAt(position).itemId;
9033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
9043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return 0;
9053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
906499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
907499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
908499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
9093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = 0;
9103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
9113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
9123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = mCache.getMetaDataAt(position).typeId;
9133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
9143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return 0;
9153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
9163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
9173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
9183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
9193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
9203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.getMappedViewType(typeId);
9213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
9223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
9233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
9243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
9253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Returns the item type id for the specified convert view.  Returns -1 if the convert view
9263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * is invalid.
9273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
9283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private int getConvertViewTypeId(View convertView) {
9293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = -1;
930a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        if (convertView != null) {
931a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId);
932a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            if (tag != null) {
933a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                typeId = (Integer) tag;
934a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
9353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
9363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        return typeId;
937499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
938499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
939b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    /**
940b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * This method allows an AdapterView using this Adapter to provide information about which
941b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * views are currently being displayed. This allows for certain optimizations and preloading
942b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     * which  wouldn't otherwise be possible.
943b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen     */
944b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    public void setVisibleRangeHint(int lowerBound, int upperBound) {
9454c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        if (lowerBound < 0 || upperBound < 0) {
9464c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            throw new RuntimeException("Attempted to set invalid range: lowerBound="+lowerBound +
9474c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen                    "," + "upperBound="+upperBound);
9484c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        }
949b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        mVisibleWindowLowerBound = lowerBound;
950b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        mVisibleWindowUpperBound = upperBound;
951b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen    }
952b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen
953499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
95416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // "Request" an index so that we can queue it for loading, initiate subsequent
95516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // preloading, etc.
95616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
95716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean isInCache = mCache.containsRemoteViewAt(position);
95816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean isConnected = mServiceConnection.isConnected();
95916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            boolean hasNewItems = false;
96016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
9617ab73e757ac6b66b0066c8ff41c2d589adacd248Winson Chung            if (!isInCache && !isConnected) {
96216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // Requesting bind service will trigger a super.notifyDataSetChanged(), which will
96316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // in turn trigger another request to getView()
96416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                requestBindService();
96516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } else {
9663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Queue up other indices to be preloaded based on this position
96716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                hasNewItems = mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
96816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
96916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
97016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (isInCache) {
9713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                View convertViewChild = null;
9723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int convertViewTypeId = 0;
973181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                RemoteViewsFrameLayout layout = null;
974181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen
975181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                if (convertView instanceof RemoteViewsFrameLayout) {
976181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                    layout = (RemoteViewsFrameLayout) convertView;
9773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    convertViewChild = layout.getChildAt(0);
9783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    convertViewTypeId = getConvertViewTypeId(convertViewChild);
9793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
9803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
9813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Second, we try and retrieve the RemoteViews from the cache, returning a loading
9823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // view and queueing it to be loaded if it has not already been loaded.
98316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                Context context = parent.getContext();
98416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                RemoteViews rv = mCache.getRemoteViewsAt(position);
985aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen                RemoteViewsIndexMetaData indexMetaData = mCache.getMetaDataAt(position);
986aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen                indexMetaData.isRequested = true;
987aeb66ca473a194d0b9148234a710b26ce88c4807Adam Cohen                int typeId = indexMetaData.typeId;
98816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
989b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                try {
990b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // Reuse the convert view where possible
991b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    if (layout != null) {
992b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        if (convertViewTypeId == typeId) {
993b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            rv.reapply(context, convertViewChild);
994b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            return layout;
995b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        }
996b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        layout.removeAllViews();
997b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    } else {
998b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        layout = new RemoteViewsFrameLayout(context);
9993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
10003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1001b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // Otherwise, create a new view to be returned
1002b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    View newView = rv.apply(context, parent);
1003b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    newView.setTagInternal(com.android.internal.R.id.rowTypeId,
1004b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            new Integer(typeId));
1005b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    layout.addView(newView);
1006b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    return layout;
1007b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
1008b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                } catch (Exception e){
1009b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // We have to make sure that we successfully inflated the RemoteViews, if not
1010b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    // we return the loading view instead.
1011b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    Log.w(TAG, "Error inflating RemoteViews at position: " + position + ", using" +
1012b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                            "loading view instead" + e);
1013b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen
1014b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    RemoteViewsFrameLayout loadingView = null;
1015b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    final RemoteViewsMetaData metaData = mCache.getMetaData();
1016b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    synchronized (metaData) {
1017b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                        loadingView = metaData.createLoadingView(position, convertView, parent);
1018b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    }
1019b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    return loadingView;
1020b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                } finally {
1021b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                    if (hasNewItems) loadNextIndexInBackground();
1022b7ffea697292e48c9d764c5677b69af484e045c8Adam Cohen                }
102316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            } else {
102416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // If the cache does not have the RemoteViews at this position, then create a
102516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                // loading view and queue the actual position to be loaded in the background
102616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                RemoteViewsFrameLayout loadingView = null;
102716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                final RemoteViewsMetaData metaData = mCache.getMetaData();
102816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                synchronized (metaData) {
102916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    loadingView = metaData.createLoadingView(position, convertView, parent);
10303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
103116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
103216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mRequestedViews.add(position, loadingView);
103316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mCache.queueRequestedPositionToLoad(position);
103416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                loadNextIndexInBackground();
103516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
103616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                return loadingView;
10373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
10383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1039499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1040499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1041499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
10423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
10433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
10443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.viewTypeCount;
10453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1046499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1047499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1048499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
10493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
10503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
10513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.hasStableIds;
10523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
1053499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1054499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1055499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
1056499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
1057499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1058499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
105916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    private void onNotifyDataSetChanged() {
106016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Complete the actual notifyDataSetChanged() call initiated earlier
106116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
106216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        try {
106316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            factory.onDataSetChanged();
10642625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RemoteException e) {
106516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
106616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
106716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // Return early to prevent from further being notified (since nothing has
106816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            // changed)
106916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
10702625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen        } catch (RuntimeException e) {
10712625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
10722625feae79ab418355c2a4dafe8b162bba3cc1cfAdam Cohen            return;
107316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
107416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
107516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Flush the cache so that we can reload new items from the service
107616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        synchronized (mCache) {
107716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mCache.reset();
107816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
107916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
108016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Re-request the new metadata (only after the notification to the factory)
108116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        updateTemporaryMetaData();
10824c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        int newCount;
10834c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        synchronized(mCache.getTemporaryMetaData()) {
10844c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            newCount = mCache.getTemporaryMetaData().count;
10854c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen        }
108616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
1087b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // Pre-load (our best guess of) the views which are currently visible in the AdapterView.
1088b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // This mitigates flashing and flickering of loading views when a widget notifies that
1089b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        // its data has changed.
1090b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        for (int i = mVisibleWindowLowerBound; i <= mVisibleWindowUpperBound; i++) {
10914c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            // Because temporary meta data is only ever modified from this thread (ie.
10924c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            // mWorkerThread), it is safe to assume that count is a valid representation.
10934c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            if (i < newCount) {
10944c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen                updateRemoteViews(i, false, false);
10954c994986202b5b668a6377a9ad47aa5345046db9Adam Cohen            }
1096b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen        }
1097b967392e0170af8cfd8053fd43fcdf8c46f703e9Adam Cohen
109816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Propagate the notification back to the base adapter
109916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.post(new Runnable() {
11006364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            @Override
11016364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            public void run() {
11026364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                synchronized (mCache) {
110316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                    mCache.commitTemporaryMetaData();
11046364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                }
110561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
110616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                superNotifyDataSetChanged();
110716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                enqueueDeferredUnbindServiceMessage();
11083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
11093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
11106364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung
111116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Reset the notify flagflag
111216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mNotifyDataSetChangedAfterOnServiceConnected = false;
111316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    }
111416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
111516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung    public void notifyDataSetChanged() {
111616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Dequeue any unbind messages
111716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
111816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
111916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // If we are not connected, queue up the notifyDataSetChanged to be handled when we do
112016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // connect
112116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        if (!mServiceConnection.isConnected()) {
112216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            if (mNotifyDataSetChangedAfterOnServiceConnected) {
112316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                return;
112416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
112516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
112616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mNotifyDataSetChangedAfterOnServiceConnected = true;
112716c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            requestBindService();
112816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            return;
112916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        }
113016c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung
113116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mWorkerQueue.post(new Runnable() {
113216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            @Override
113316c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            public void run() {
113416c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                onNotifyDataSetChanged();
113516c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            }
113616c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        });
11373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
11383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1139fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen    void superNotifyDataSetChanged() {
1140499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
1141499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1142499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
114381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    @Override
114481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    public boolean handleMessage(Message msg) {
114581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        boolean result = false;
114681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        switch (msg.what) {
114781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        case sUnbindServiceMessageType:
114881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            if (mServiceConnection.isConnected()) {
114916c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung                mServiceConnection.unbind(mContext, mAppWidgetId, mIntent);
115081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            }
115181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            result = true;
115281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
115381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        default:
115481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
115581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
115681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        return result;
115781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
115881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
115981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private void enqueueDeferredUnbindServiceMessage() {
116081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Remove any existing deferred-unbind messages
116181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
116281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
116381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
116481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
1165499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
116681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Try binding the service (which will start it if it's not already running)
1167499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
116816c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung            mServiceConnection.bind(mContext, mAppWidgetId, mIntent);
1169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
1170499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
117116c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        // Remove any existing deferred-unbind messages
117216c8d8a558f94ec14ef52bb5ac11044e2d0d902cWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
1173499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
1174499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
1175499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
1176