RemoteViewsAdapter.java revision 81f39eb6e76d0be1dd341af835e8002a0f80524e
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 Chungimport java.util.Map;
24499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.appwidget.AppWidgetManager;
26499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Context;
27499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.content.Intent;
28499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Handler;
29499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.HandlerThread;
30499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.IBinder;
31499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.os.Looper;
3281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chungimport android.os.Message;
33fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chungimport android.util.Log;
34a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chungimport android.view.LayoutInflater;
35499cb9f516062b654952d282f211bee44c31a3c2Winson Chungimport android.view.View;
363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chungimport android.view.ViewGroup;
37181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohenimport android.view.View.MeasureSpec;
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
5181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sDefaultCacheSize = 36;
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.
5581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sUnbindServiceDelay = 5000;
5681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    // Type defs for controlling different messages across the main and worker message queues
5781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sDefaultMessageType = 0;
5881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static final int sUnbindServiceMessageType = 1;
5981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
6081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Context mContext;
6181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final Intent mIntent;
6281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private final int mAppWidgetId;
63a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung    private LayoutInflater mLayoutInflater;
64499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private RemoteViewsAdapterServiceConnection mServiceConnection;
653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private WeakReference<RemoteAdapterConnectionCallback> mCallback;
663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private FixedSizeRemoteViewsCache mCache;
673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // The set of requested views that are to be notified when the associated RemoteViews are
693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    // loaded.
703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private RemoteViewsFrameLayoutRefSet mRequestedViews;
71499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
72499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private HandlerThread mWorkerThread;
73499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    // items may be interrupted within the normally processed queues
74499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mWorkerQueue;
75499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private Handler mMainQueue;
76499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
77499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
78499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * An interface for the RemoteAdapter to notify other classes when adapters
79499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * are actually connected to/disconnected from their actual services.
80499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
81499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public interface RemoteAdapterConnectionCallback {
82499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterConnected();
83499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
84499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public void onRemoteAdapterDisconnected();
85499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
86499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
87499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
88499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     * The service connection that gets populated when the RemoteViewsService is
893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * bound.  This must be a static inner class to ensure that no references to the outer
903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being
913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * garbage collected, and would cause us to leak activities due to the caching mechanism for
923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * FrameLayouts in the adapter).
93499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
9481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private static class RemoteViewsAdapterServiceConnection extends
9581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            IRemoteViewsAdapterConnection.Stub {
96499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private boolean mConnected;
973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private WeakReference<RemoteViewsAdapter> mAdapter;
98499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        private IRemoteViewsFactory mRemoteViewsFactory;
99499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) {
1013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
102499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
103499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
10481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        public void onServiceConnected(IBinder service) {
105499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
106499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mConnected = true;
107c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
1083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Queue up work that we need to do for the callback to run
1093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
1103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
1113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            adapter.mWorkerQueue.post(new Runnable() {
112499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                @Override
113499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                public void run() {
1143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Call back to the service to notify that the data set changed
1153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    if (adapter.mServiceConnection.isConnected()) {
1163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        IRemoteViewsFactory factory =
1173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            adapter.mServiceConnection.getRemoteViewsFactory();
1183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        try {
1193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // call back to the factory
1203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            factory.onDataSetChanged();
1213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        } catch (Exception e) {
1223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            Log.e(TAG, "Error notifying factory of data set changed in " +
1233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                        "onServiceConnected(): " + e.getMessage());
1243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            e.printStackTrace();
1253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // Return early to prevent anything further from being notified
1273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            // (effectively nothing has changed)
1283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            return;
129499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                        }
1303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // Request meta data so that we have up to date data when calling back to
1323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // the remote adapter callback
1333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        adapter.updateMetaData();
1343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // Post a runnable to call back to the view to notify it that we have
1363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        // connected
13761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                        adapter.mMainQueue.post(new Runnable() {
1383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            @Override
1393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            public void run() {
1403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                final RemoteAdapterConnectionCallback callback =
1413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    adapter.mCallback.get();
1423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                if (callback != null) {
1433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                    callback.onRemoteAdapterConnected();
1443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                }
1453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            }
1463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        });
1473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
148499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
149499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            });
150499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
151499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
15281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        public void onServiceDisconnected() {
153499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            mConnected = false;
1543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRemoteViewsFactory = null;
155499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
1563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteViewsAdapter adapter = mAdapter.get();
1573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (adapter == null) return;
158c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
1593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Clear the main/worker queues
16081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
16181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            adapter.mMainQueue.removeMessages(sDefaultMessageType);
16281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            adapter.mWorkerQueue.removeMessages(sDefaultMessageType);
1633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
1643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
1653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (callback != null) {
1663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                callback.onRemoteAdapterDisconnected();
1673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
168ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy            adapter.mCache.reset();
169499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
170499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
171499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public IRemoteViewsFactory getRemoteViewsFactory() {
172499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mRemoteViewsFactory;
173499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
174499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
175499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        public boolean isConnected() {
176499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return mConnected;
177499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
178499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
179499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
180499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    /**
1813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
1823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * they are loaded.
183499cb9f516062b654952d282f211bee44c31a3c2Winson Chung     */
1843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsFrameLayout extends FrameLayout {
1853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsFrameLayout(Context context) {
1863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            super(context);
1873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
188499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
189499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
1903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Updates this RemoteViewsFrameLayout depending on the view that was loaded.
1913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded
1923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         *             successfully.
193499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
1943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void onRemoteViewsLoaded(RemoteViews view) {
19561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            try {
19661ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                // Remove all the children of this layout first
19761ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                removeAllViews();
19861ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                addView(view.apply(getContext(), this));
19961ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            } catch (Exception e) {
20061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung                Log.e(TAG, "Failed to apply RemoteViews.");
20161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            }
2023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
2033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
204499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
2063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the
2073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * adapter that have not yet had their RemoteViews loaded.
2083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
2093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsFrameLayoutRefSet {
2103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences;
211499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsFrameLayoutRefSet() {
2133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>();
214499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
215499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
216499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        /**
2173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter.
218499cb9f516062b654952d282f211bee44c31a3c2Winson Chung         */
2193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void add(int position, RemoteViewsFrameLayout layout) {
2203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Integer pos = position;
2213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            LinkedList<RemoteViewsFrameLayout> refs;
2223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create the list if necessary
2243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mReferences.containsKey(pos)) {
2253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = mReferences.get(pos);
2263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
2273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs = new LinkedList<RemoteViewsFrameLayout>();
2283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mReferences.put(pos, refs);
229499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
2303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Add the references to the list
2323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            refs.add(layout);
233499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
234499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
2363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that
2373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * the associated RemoteViews has loaded.
2383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
2393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) {
24061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            if (view == null) return;
24161ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
2423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Integer pos = position;
2433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mReferences.containsKey(pos)) {
2443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Notify all the references for that position of the newly loaded RemoteViews
2453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos);
2463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (final RemoteViewsFrameLayout ref : refs) {
2473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    ref.onRemoteViewsLoaded(view);
2483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
2493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                refs.clear();
250499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Remove this set from the original mapping
2523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mReferences.remove(pos);
253499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
254499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
255499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        /**
2573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         * Removes all references to all RemoteViewsFrameLayouts returned by the adapter.
2583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung         */
2593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void clear() {
2603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We currently just clear the references, and leave all the previous layouts returned
2613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // in their default state of the loading view.
2623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mReferences.clear();
263499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
2643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
265499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
2673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with the cache in it's current state.
2683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
2693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsMetaData {
2703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int count;
2713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int viewTypeCount;
2723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        boolean hasStableIds;
2733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        boolean isDataDirty;
2743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Used to determine how to construct loading views.  If a loading view is not specified
2763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // by the user, then we try and load the first view, and use its height as the height for
2773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the default loading view.
2783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mUserLoadingView;
2793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        RemoteViews mFirstView;
2803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int mFirstViewHeight;
2813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // A mapping from type id to a set of unique type ids
2833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private Map<Integer, Integer> mTypeIdIndexMap;
2843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
2853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData() {
2863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            reset();
287499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
288499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
2893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
2903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            count = 0;
2913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // by default there is at least one dummy view type
2923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            viewTypeCount = 1;
2933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            hasStableIds = true;
2943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            isDataDirty = false;
2953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = null;
2963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstView = null;
2973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mFirstViewHeight = 0;
2983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mTypeIdIndexMap = new HashMap<Integer, Integer>();
299499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
300499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) {
3023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mUserLoadingView = loadingView;
3033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (firstView != null) {
3043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstView = firstView;
3053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mFirstViewHeight = -1;
306499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
307499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
308499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public int getMappedViewType(int typeId) {
3103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mTypeIdIndexMap.containsKey(typeId)) {
3113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mTypeIdIndexMap.get(typeId);
3123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
3133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // We +1 because the loading view always has view type id of 0
3143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int incrementalTypeId = mTypeIdIndexMap.size() + 1;
3153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mTypeIdIndexMap.put(typeId, incrementalTypeId);
3163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return incrementalTypeId;
3176394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
3186394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
3196394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private RemoteViewsFrameLayout createLoadingView(int position, View convertView,
3213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                ViewGroup parent) {
3223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create and return a new FrameLayout, and setup the references for this position
3233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            final Context context = parent.getContext();
3243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context);
3253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Create a new loading view
3273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mCache) {
3283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (mUserLoadingView != null) {
3293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // A user-specified loading view
3303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    View loadingView = mUserLoadingView.apply(parent.getContext(), parent);
331a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    loadingView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(0));
3323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    layout.addView(loadingView);
3333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                } else {
3343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // A default loading view
3353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Use the size of the first row as a guide for the size of the loading view
3363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    if (mFirstViewHeight < 0) {
3373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        View firstView = mFirstView.apply(parent.getContext(), parent);
3383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        firstView.measure(
3393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
3403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
3413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        mFirstViewHeight = firstView.getMeasuredHeight();
3423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        mFirstView = null;
3433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
3443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Compose the loading view text
346a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    TextView loadingTextView = (TextView) mLayoutInflater.inflate(
347fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen                            com.android.internal.R.layout.remote_views_adapter_default_loading_view,
348fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen                            layout, false);
349a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    loadingTextView.setHeight(mFirstViewHeight);
350a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    loadingTextView.setTag(new Integer(0));
351a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung
352a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung                    layout.addView(loadingTextView);
353fbc3590f40436e372df198c2d6b27877ae8952f1Winson Chung                }
3546394c0e52cf641d93f678fd052499aa952e3595dWinson Chung            }
3556394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return layout;
3576394c0e52cf641d93f678fd052499aa952e3595dWinson Chung        }
3583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
3596394c0e52cf641d93f678fd052499aa952e3595dWinson Chung
3603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
3613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * The meta-data associated with a single item in the cache.
3623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
3633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class RemoteViewsIndexMetaData {
3643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId;
3653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        long itemId;
366499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsIndexMetaData(RemoteViews v, long itemId) {
3683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            set(v, itemId);
369499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
370499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void set(RemoteViews v, long id) {
3723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            itemId = id;
3733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (v != null)
3743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = v.getLayoutId();
3753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            else
3763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = 0;
3773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
3783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
379499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
3803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
3813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     *
3823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
3833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private class FixedSizeRemoteViewsCache {
3843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final String TAG = "FixedSizeRemoteViewsCache";
3853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The meta data related to all the RemoteViews, ie. count, is stable, etc.
3873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private RemoteViewsMetaData mMetaData;
3883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache/mapping of position to RemoteViewsMetaData.  This set is guaranteed to be
3903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // greater than or equal to the set of RemoteViews.
3913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Note: The reason that we keep this separate from the RemoteViews cache below is that this
3923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // we still need to be able to access the mapping of position to meta data, without keeping
3933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the heavy RemoteViews around.  The RemoteViews cache is trimmed to fixed constraints wrt.
3943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // memory and size, but this metadata cache will retain information until the data at the
3953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged).
3963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData;
3973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
3983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses
3993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // too much memory.
4003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashMap<Integer, RemoteViews> mIndexRemoteViews;
4013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The set of indices that have been explicitly requested by the collection view
4033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashSet<Integer> mRequestedIndices;
4043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The set of indices to load, including those explicitly requested, as well as those
4063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // determined by the preloading algorithm to be prefetched
4073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private HashSet<Integer> mLoadIndices;
4083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The lower and upper bounds of the preloaded range
4103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadLowerBound;
4113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mPreloadUpperBound;
4123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The bounds of this fixed cache, we will try and fill as many items into the cache up to
4143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // the maxCount number of items, or the maxSize memory usage.
4153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // The maxCountSlack is used to determine if a new position in the cache to be loaded is
4163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // sufficiently ouside the old set, prompting a shifting of the "window" of items to be
4173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // preloaded.
4183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mMaxCount;
4193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int mMaxCountSlack;
4203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final float sMaxCountSlackPercent = 0.75f;
4213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private static final int sMaxMemoryUsage = 1024 * 1024;
4223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public FixedSizeRemoteViewsCache(int maxCacheSize) {
4243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCount = maxCacheSize;
4253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2));
4263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
4273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
4283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mMetaData = new RemoteViewsMetaData();
4293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>();
4303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews = new HashMap<Integer, RemoteViews>();
4313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mRequestedIndices = new HashSet<Integer>();
4323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mLoadIndices = new HashSet<Integer>();
4333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
434499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void insert(int position, RemoteViews v, long itemId) {
4363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the count
4373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.size() >= mMaxCount) {
4383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
439499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
440499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Trim the cache if we go beyond the available memory size constraints
4423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryUsage) {
4433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Note: This is currently the most naive mechanism for deciding what to prune when
4443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // we hit the memory limit.  In the future, we may want to calculate which index to
4453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // remove based on both its position as well as it's current memory usage, as well
4463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // as whether it was directly requested vs. whether it was preloaded by our caching
4473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // mechanism.
4483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mIndexRemoteViews.remove(getFarthestPositionFrom(position));
449499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
4503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
4513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Update the metadata cache
4523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexMetaData.containsKey(position)) {
4533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position);
4543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                metaData.set(v, itemId);
4553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
4563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId));
457499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
4583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.put(position, v);
4593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
460499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsMetaData getMetaData() {
4623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mMetaData;
4633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
4643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViews getRemoteViewsAt(int position) {
4653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexRemoteViews.containsKey(position)) {
4663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mIndexRemoteViews.get(position);
4673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
468499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            return null;
469499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
4703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public RemoteViewsIndexMetaData getMetaDataAt(int position) {
4713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mIndexMetaData.containsKey(position)) {
4723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mIndexMetaData.get(position);
473499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
4743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return null;
4753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
476499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
4773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int getRemoteViewsBitmapMemoryUsage() {
4783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Calculate the memory usage of all the RemoteViews bitmaps being cached
4793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int mem = 0;
4803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            for (Integer i : mIndexRemoteViews.keySet()) {
4813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViews v = mIndexRemoteViews.get(i);
482aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                if (v != null) {
483aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                    mem += v.estimateBitmapMemoryUsage();
484aaffa8b4d4d3530e02cd6a0619b0c1485c133e55Winson Chung                }
485499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
4863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mem;
487499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
4883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        private int getFarthestPositionFrom(int pos) {
4893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Find the index farthest away and remove that
4903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDist = 0;
4913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int maxDistIndex = -1;
4923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            for (int i : mIndexRemoteViews.keySet()) {
4933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int dist = Math.abs(i-pos);
4943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (dist > maxDist) {
4953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDistIndex = i;
4963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    maxDist = dist;
497c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung                }
498c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
4993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return maxDistIndex;
500c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung        }
501c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
5023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void queueRequestedPositionToLoad(int position) {
5033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
5043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mRequestedIndices.add(position);
5053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.add(position);
506499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
507499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
5083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void queuePositionsToBePreloadedFromRequestedPosition(int position) {
5093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Check if we need to preload any items
5103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) {
5113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int center = (mPreloadUpperBound + mPreloadLowerBound) / 2;
5123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (Math.abs(position - center) < mMaxCountSlack) {
5133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    return;
514499cb9f516062b654952d282f211bee44c31a3c2Winson Chung                }
515499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
516499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            int count = 0;
5183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mMetaData) {
5193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                count = mMetaData.count;
520499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
5213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
5223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.clear();
5233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the requested indices
5253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.addAll(mRequestedIndices);
5263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
5273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Add all the preload indices
5283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int halfMaxCount = mMaxCount / 2;
5293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadLowerBound = position - halfMaxCount;
5303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mPreloadUpperBound = position + halfMaxCount;
5313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveLowerBound = Math.max(0, mPreloadLowerBound);
5323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1);
5333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) {
5343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.add(i);
5353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
536499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // But remove all the indices that have already been loaded and are cached
5383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.removeAll(mIndexRemoteViews.keySet());
539499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
540499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
5413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public int getNextIndexToLoad() {
5423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // We try and prioritize items that have been requested directly, instead
5433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // of items that are loaded as a result of the caching mechanism
5443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
5453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Prioritize requested indices to be loaded first
5463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (!mRequestedIndices.isEmpty()) {
5473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Integer i = mRequestedIndices.iterator().next();
5483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mRequestedIndices.remove(i);
5493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.remove(i);
5503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    return i.intValue();
5513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
552499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Otherwise, preload other indices as necessary
5543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (!mLoadIndices.isEmpty()) {
5553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Integer i = mLoadIndices.iterator().next();
5563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mLoadIndices.remove(i);
5573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    return i.intValue();
5583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
559499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return -1;
561c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung            }
5623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
563c6d6d4a4e73fcb63eaa13d66fcbf26d847799838Winson Chung
5643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsRemoteViewAt(int position) {
5653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mIndexRemoteViews.containsKey(position);
5663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
5673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public boolean containsMetaDataAt(int position) {
5683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return mIndexMetaData.containsKey(position);
5693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
570499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
5713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        public void reset() {
57261ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // Note: We do not try and reset the meta data, since that information is still used by
57361ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // collection views to validate it's own contents (and will be re-requested if the data
57461ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung            // is invalidated through the notifyDataSetChanged() flow).
57561ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
5763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadLowerBound = 0;
5773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mPreloadUpperBound = -1;
5783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexRemoteViews.clear();
5793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            mIndexMetaData.clear();
5803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mLoadIndices) {
5813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mRequestedIndices.clear();
5823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mLoadIndices.clear();
583499cb9f516062b654952d282f211bee44c31a3c2Winson Chung            }
584499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
585499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
586499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
587499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
588499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mContext = context;
589499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mIntent = intent;
59081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
591a5f6f802c6e472503efd5b598dc3bab57150c1c9Winson Chung        mLayoutInflater = LayoutInflater.from(context);
5929b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        if (mIntent == null) {
5939b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung            throw new IllegalArgumentException("Non-null Intent must be specified.");
5949b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        }
5953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mRequestedViews = new RemoteViewsFrameLayoutRefSet();
596499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
59781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Strip the previously injected app widget id from service intent
59881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
59981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
60081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
60181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
60281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the worker thread
603499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
604499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerThread.start();
605499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        mWorkerQueue = new Handler(mWorkerThread.getLooper());
60681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue = new Handler(Looper.myLooper(), this);
607499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
60881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Initialize the cache and the service connection on startup
60981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
6103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
6113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
612499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
613499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
614499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
6153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private void loadNextIndexInBackground() {
6163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        mWorkerQueue.post(new Runnable() {
6173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            @Override
6183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            public void run() {
6193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Get the next index to load
6203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int position = -1;
6213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                synchronized (mCache) {
6223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    position = mCache.getNextIndexToLoad();
6233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
6243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (position > -1) {
6253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Load the item, and notify any existing RemoteViewsFrameLayouts
6263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    updateRemoteViews(position);
6273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Queue up for the next one to load
6293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    loadNextIndexInBackground();
6303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
6313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
6323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
6333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
6343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private void updateMetaData() {
6363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        if (mServiceConnection.isConnected()) {
6373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            try {
6383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
6393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // get the properties/first view (so that we can use it to
6413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // measure our dummy views)
6423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                boolean hasStableIds = factory.hasStableIds();
6433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int viewTypeCount = factory.getViewTypeCount();
6443ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int count = factory.getCount();
6453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                RemoteViews loadingView = factory.getLoadingView();
6463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                RemoteViews firstView = null;
6473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if ((count > 0) && (loadingView == null)) {
6483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    firstView = factory.getViewAt(0);
6493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
6503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViewsMetaData metaData = mCache.getMetaData();
6513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                synchronized (metaData) {
6523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    metaData.hasStableIds = hasStableIds;
6533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    metaData.viewTypeCount = viewTypeCount + 1;
6543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    metaData.count = count;
6553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    metaData.setLoadingViewTemplates(loadingView, firstView);
6563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
6573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } catch (Exception e) {
6583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // print the error
6593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                Log.e(TAG, "Error in requestMetaData(): " + e.getMessage());
6603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // reset any members after the failed call
6623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViewsMetaData metaData = mCache.getMetaData();
6633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                synchronized (metaData) {
6643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    metaData.reset();
6653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
6663ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
6673ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
6683ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
6693ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6703ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private void updateRemoteViews(final int position) {
6713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        if (mServiceConnection.isConnected()) {
6723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
6733ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6743ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // Load the item information from the remote service
6753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            RemoteViews remoteViews = null;
6763ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            long itemId = 0;
6773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            try {
6783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                remoteViews = factory.getViewAt(position);
6793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                itemId = factory.getItemId(position);
680ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy            } catch (Throwable t) {
681ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy                Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + t.getMessage());
682ec84c3a189e4aa70aa6ea8ba712e5a4f260a153bPatrick Dubroy                t.printStackTrace();
6833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
6843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Return early to prevent additional work in re-centering the view cache, and
6853ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // swapping from the loading view
6863ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return;
6873ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
6883ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
689490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen            if (remoteViews == null) {
690490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen                // If a null view was returned, we break early to prevent it from getting
691490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen                // into our cache and causing problems later. The effect is that the child  at this
692490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen                // position will remain as a loading view until it is updated.
693490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen                Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + " null RemoteViews " +
694490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen                        "returned from RemoteViewsFactory.");
695490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen                return;
696490127df193ecaa4dacf241a1a6b72ed6e5b0a0fAdam Cohen            }
6973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mCache) {
6983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Cache the RemoteViews we loaded
6993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mCache.insert(position, remoteViews, itemId);
7003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
7013ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Notify all the views that we have previously returned for this index that
7023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // there is new data for it.
7033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final RemoteViews rv = remoteViews;
7043ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                final int typeId = mCache.getMetaDataAt(position).typeId;
7053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mMainQueue.post(new Runnable() {
7063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    @Override
7073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    public void run() {
7083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
70981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung                        enqueueDeferredUnbindServiceMessage();
7103ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
7113ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                });
7123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
7133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
714499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
715499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
7169b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    public Intent getRemoteViewsServiceIntent() {
7179b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung        return mIntent;
7189b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung    }
7199b3a2cf2a0a482ce8212eb2775176dd4c23e8e9aWinson Chung
720499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getCount() {
721499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
7223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
7233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
7243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.count;
7253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
726499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
727499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
728499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public Object getItem(int position) {
7293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        // Disallow arbitrary object to be associated with an item for the time being
730499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return null;
731499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
732499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
733499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public long getItemId(int position) {
734499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
7353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
7363ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
7373ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return mCache.getMetaDataAt(position).itemId;
7383ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
7393ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return 0;
7403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
741499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
742499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
743499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getItemViewType(int position) {
744499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
7453ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = 0;
7463ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (mCache) {
7473ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            if (mCache.containsMetaDataAt(position)) {
7483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                typeId = mCache.getMetaDataAt(position).typeId;
7493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            } else {
7503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                return 0;
7513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
7523ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
7533ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
7543ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
7553ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
7563ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.getMappedViewType(typeId);
7573ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
7583ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
7593ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
7603ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    /**
7613ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * Returns the item type id for the specified convert view.  Returns -1 if the convert view
7623ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     * is invalid.
7633ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung     */
7643ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    private int getConvertViewTypeId(View convertView) {
7653ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        int typeId = -1;
766a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen        if (convertView != null) {
767a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            Object tag = convertView.getTag(com.android.internal.R.id.rowTypeId);
768a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            if (tag != null) {
769a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                typeId = (Integer) tag;
770a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen            }
7713ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
7723ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        return typeId;
773499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
774499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
775499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public View getView(int position, View convertView, ViewGroup parent) {
776499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
7773ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        if (mServiceConnection.isConnected()) {
7783ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // "Request" an index so that we can queue it for loading, initiate subsequent
7793ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            // preloading, etc.
7803ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            synchronized (mCache) {
7813ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Queue up other indices to be preloaded based on this position
7823ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                mCache.queuePositionsToBePreloadedFromRequestedPosition(position);
7833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                View convertViewChild = null;
7843ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                int convertViewTypeId = 0;
785181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                RemoteViewsFrameLayout layout = null;
786181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen
787181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                if (convertView instanceof RemoteViewsFrameLayout) {
788181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                    layout = (RemoteViewsFrameLayout) convertView;
7893ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    convertViewChild = layout.getChildAt(0);
7903ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    convertViewTypeId = getConvertViewTypeId(convertViewChild);
7913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
7923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
7933ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // Second, we try and retrieve the RemoteViews from the cache, returning a loading
7943ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                // view and queueing it to be loaded if it has not already been loaded.
7953ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                if (mCache.containsRemoteViewAt(position)) {
7963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    Context context = parent.getContext();
7973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    RemoteViews rv = mCache.getRemoteViewsAt(position);
7983ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    int typeId = mCache.getMetaDataAt(position).typeId;
7993ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8003ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Reuse the convert view where possible
801181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                    if (layout != null) {
8023ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        if (convertViewTypeId == typeId) {
8033ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                            rv.reapply(context, convertViewChild);
804181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                            return layout;
8053ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        }
8063ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
8073ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8083ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // Otherwise, create a new view to be returned
8093ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    View newView = rv.apply(context, parent);
810a32edd4b4c894f4fb3d9fd7e9d5b80321df79e20Adam Cohen                    newView.setTagInternal(com.android.internal.R.id.rowTypeId, new Integer(typeId));
811181d2e3172f24d8c920cbe4b446e8f938ccdc8bbAdam Cohen                    if (layout != null) {
8123ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        layout.removeAllViews();
8133ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    } else {
8143ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        layout = new RemoteViewsFrameLayout(context);
8153ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
8163ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    layout.addView(newView);
8173ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    return layout;
8183ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                } else {
8193ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // If the cache does not have the RemoteViews at this position, then create a
8203ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    // loading view and queue the actual position to be loaded in the background
8213ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    RemoteViewsFrameLayout loadingView = null;
8223ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    final RemoteViewsMetaData metaData = mCache.getMetaData();
8233ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    synchronized (metaData) {
8243ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                        loadingView = metaData.createLoadingView(position, convertView, parent);
8253ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    }
8263ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8273ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mRequestedViews.add(position, loadingView);
8283ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    mCache.queueRequestedPositionToLoad(position);
8293ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    loadNextIndexInBackground();
8303ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8313ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                    return loadingView;
8323ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung                }
8333ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
8343ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
8353ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        return new View(parent.getContext());
836499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
837499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
838499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public int getViewTypeCount() {
839499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
8403ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
8413ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
8423ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.viewTypeCount;
8433ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
844499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
845499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
846499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean hasStableIds() {
847499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        requestBindService();
8483ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        final RemoteViewsMetaData metaData = mCache.getMetaData();
8493ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        synchronized (metaData) {
8503ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            return metaData.hasStableIds;
8513ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        }
852499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
853499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
854499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public boolean isEmpty() {
855499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return getCount() <= 0;
856499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
857499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
858499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    public void notifyDataSetChanged() {
8596364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung        mWorkerQueue.post(new Runnable() {
8606364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            @Override
8616364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung            public void run() {
8626364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                // Complete the actual notifyDataSetChanged() call initiated earlier
8636364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                if (mServiceConnection.isConnected()) {
8646364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory();
8656364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    try {
8666364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                        factory.onDataSetChanged();
8676364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    } catch (Exception e) {
8686364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                        Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage());
8696364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung
8706364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                        // Return early to prevent from further being notified (since nothing has
8716364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                        // changed)
8726364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                        return;
8736364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    }
8746364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                }
8753ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8766364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                // Flush the cache so that we can reload new items from the service
8776364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                synchronized (mCache) {
8786364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    mCache.reset();
8796364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                }
88061ac7e3b3602d9d20b8876b4bf8d2e536af04a47Winson Chung
8816364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                // Re-request the new metadata (only after the notification to the factory)
8826364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                updateMetaData();
8833ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
8846364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                // Propagate the notification back to the base adapter
8856364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                mMainQueue.post(new Runnable() {
8866364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    @Override
8876364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    public void run() {
8886364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                        superNotifyDataSetChanged();
8896364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                    }
8906364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung                });
8913ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung            }
8923ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung        });
8936364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung
8946364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung        // Note: we do not call super.notifyDataSetChanged() until the RemoteViewsFactory has had
8956364f2bbe5254b4274f3feffc48f4259eacc205eWinson Chung        // a chance to update itself and return new meta data associated with the new data.
8963ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung    }
8973ec9a45c36d3ca5ffbc6e85bbeb497b065e14155Winson Chung
898fb60386b46d0c6216c765c10bd33ac42ca780917Adam Cohen    void superNotifyDataSetChanged() {
899499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        super.notifyDataSetChanged();
900499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
901499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
90281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    @Override
90381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    public boolean handleMessage(Message msg) {
90481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        boolean result = false;
90581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        switch (msg.what) {
90681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        case sUnbindServiceMessageType:
90781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            final AppWidgetManager mgr = AppWidgetManager.getInstance(mContext);
90881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            if (mServiceConnection.isConnected()) {
90981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung                mgr.unbindRemoteViewsService(mAppWidgetId, mIntent);
91081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            }
91181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            result = true;
91281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
91381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        default:
91481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            break;
91581f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        }
91681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        return result;
91781f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
91881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
91981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    private void enqueueDeferredUnbindServiceMessage() {
92081f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Remove any existing deferred-unbind messages
92181f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.removeMessages(sUnbindServiceMessageType);
92281f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        mMainQueue.sendEmptyMessageDelayed(sUnbindServiceMessageType, sUnbindServiceDelay);
92381f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung    }
92481f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung
925499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    private boolean requestBindService() {
92681f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung        // Try binding the service (which will start it if it's not already running)
927499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        if (!mServiceConnection.isConnected()) {
92881f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            final AppWidgetManager mgr = AppWidgetManager.getInstance(mContext);
92981f39eb6e76d0be1dd341af835e8002a0f80524eWinson Chung            mgr.bindRemoteViewsService(mAppWidgetId, mIntent, mServiceConnection.asBinder());
930499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        }
931499cb9f516062b654952d282f211bee44c31a3c2Winson Chung
932499cb9f516062b654952d282f211bee44c31a3c2Winson Chung        return mServiceConnection.isConnected();
933499cb9f516062b654952d282f211bee44c31a3c2Winson Chung    }
934499cb9f516062b654952d282f211bee44c31a3c2Winson Chung}
935