diff options
-rw-r--r-- | core/java/android/widget/AbsListView.java | 11 | ||||
-rw-r--r-- | core/java/android/widget/AdapterViewAnimator.java | 6 | ||||
-rw-r--r-- | core/java/android/widget/RemoteViewsAdapter.java | 205 |
3 files changed, 215 insertions, 7 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 19aef8e..437da59 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -16,8 +16,6 @@ package android.widget; -import com.android.internal.R; - import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; @@ -69,6 +67,8 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; import android.view.inputmethod.InputMethodManager; +import com.android.internal.R; + import java.util.ArrayList; import java.util.List; @@ -1813,6 +1813,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } ss.checkedItemCount = mCheckedItemCount; + if (mRemoteAdapter != null) { + mRemoteAdapter.saveRemoteViewsCache(); + } + return ss; } @@ -5974,6 +5978,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mDeferNotifyDataSetChanged = false; // Otherwise, create a new RemoteViewsAdapter for binding mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this); + if (mRemoteAdapter.isDataReady()) { + setAdapter(mRemoteAdapter); + } } /** diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index c557963..2266cea 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -813,6 +813,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); + if (mRemoteViewsAdapter != null) { + mRemoteViewsAdapter.saveRemoteViewsCache(); + } return new SavedState(superState, mWhichChild); } @@ -984,6 +987,9 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> mDeferNotifyDataSetChanged = false; // Otherwise, create a new RemoteViewsAdapter for binding mRemoteViewsAdapter = new RemoteViewsAdapter(getContext(), intent, this); + if (mRemoteViewsAdapter.isDataReady()) { + setAdapter(mRemoteViewsAdapter); + } } @Override diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index aa95397..e9c753a 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -29,12 +29,16 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; +import android.widget.RemoteViewsService.RemoteViewsFactory; import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.widget.IRemoteViewsFactory; @@ -83,6 +87,26 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback private Handler mWorkerQueue; private Handler mMainQueue; + // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data + // structures; + private static final HashMap<Pair<Intent.FilterComparison, Integer>, Parcel> + sCachedRemoteViewsCaches = new HashMap<Pair<Intent.FilterComparison, Integer>, + Parcel>(); + private static final HashMap<Pair<Intent.FilterComparison, Integer>, Runnable> + sRemoteViewsCacheRemoveRunnables = new HashMap<Pair<Intent.FilterComparison, Integer>, + Runnable>(); + private static HandlerThread sCacheRemovalThread; + private static Handler sCacheRemovalQueue; + + // We keep the cache around for a duration after onSaveInstanceState for use on re-inflation. + // If a new RemoteViewsAdapter with the same intent / widget id isn't constructed within this + // duration, the cache is dropped. + private static final int REMOTE_VIEWS_CACHE_DURATION = 5000; + + // Used to indicate to the AdapterView that it can use this Adapter immediately after + // construction (happens when we have a cached FixedSizeRemoteViewsCache). + private boolean mDataReady = false; + /** * An interface for the RemoteAdapter to notify other classes when adapters * are actually connected to/disconnected from their actual services. @@ -331,7 +355,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback /** * The meta-data associated with the cache in it's current state. */ - private static class RemoteViewsMetaData { + private static class RemoteViewsMetaData implements Parcelable { int count; int viewTypeCount; boolean hasStableIds; @@ -350,6 +374,51 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback reset(); } + public RemoteViewsMetaData(Parcel src) { + count = src.readInt(); + viewTypeCount = src.readInt(); + hasStableIds = src.readInt() == 0 ? false : true; + mFirstViewHeight = src.readInt(); + if (src.readInt() != 0) { + mUserLoadingView = new RemoteViews(src); + } + if (src.readInt() != 0) { + mFirstView = new RemoteViews(src); + } + int count = src.readInt(); + for (int i = 0; i < count; i++) { + mTypeIdIndexMap.put(src.readInt(), src.readInt()); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(count); + dest.writeInt(viewTypeCount); + dest.writeInt(hasStableIds ? 1 : 0); + dest.writeInt(mFirstViewHeight); + dest.writeInt(mUserLoadingView != null ? 1 : 0); + if (mUserLoadingView != null) { + mUserLoadingView.writeToParcel(dest, flags); + } + dest.writeInt(mFirstView != null ? 1 : 0); + if (mFirstView != null) { + mFirstView.writeToParcel(dest, flags); + } + + int count = mTypeIdIndexMap.size(); + dest.writeInt(count); + for (Integer key: mTypeIdIndexMap.keySet()) { + dest.writeInt(key); + dest.writeInt(mTypeIdIndexMap.get(key)); + } + } + + @Override + public int describeContents() { + return 0; + } + public void set(RemoteViewsMetaData d) { synchronized (d) { count = d.count; @@ -460,7 +529,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback /** * The meta-data associated with a single item in the cache. */ - private static class RemoteViewsIndexMetaData { + private static class RemoteViewsIndexMetaData implements Parcelable { int typeId; long itemId; boolean isRequested; @@ -469,6 +538,22 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback set(v, itemId, requested); } + public RemoteViewsIndexMetaData(Parcel src) { + typeId = src.readInt(); + itemId = src.readLong(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(typeId); + dest.writeLong(itemId); + } + + @Override + public int describeContents() { + return 0; + } + public void set(RemoteViews v, long id, boolean requested) { itemId = id; if (v != null) { @@ -478,12 +563,14 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } isRequested = requested; } + + } /** * */ - private static class FixedSizeRemoteViewsCache { + private static class FixedSizeRemoteViewsCache implements Parcelable { private static final String TAG = "FixedSizeRemoteViewsCache"; // The meta data related to all the RemoteViews, ie. count, is stable, etc. @@ -545,6 +632,57 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mLoadIndices = new HashSet<Integer>(); } + public FixedSizeRemoteViewsCache(Parcel src) { + mMaxCount = src.readInt(); + mMaxCountSlack = src.readInt(); + mPreloadLowerBound = src.readInt(); + mPreloadUpperBound = src.readInt(); + mMetaData = new RemoteViewsMetaData(src); + int count = src.readInt(); + mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); + for (int i = 0; i < count; i++) { + mIndexMetaData.put(src.readInt(), new RemoteViewsIndexMetaData(src)); + } + count = src.readInt(); + mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); + for (int i = 0; i < count; i++) { + mIndexRemoteViews.put(src.readInt(), new RemoteViews(src)); + } + + mTemporaryMetaData = new RemoteViewsMetaData(); + mRequestedIndices = new HashSet<Integer>(); + mLastRequestedIndex = -1; + mLoadIndices = new HashSet<Integer>(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mMaxCount); + dest.writeInt(mMaxCountSlack); + dest.writeInt(mPreloadLowerBound); + dest.writeInt(mPreloadUpperBound); + mMetaData.writeToParcel(dest, 0); + + // We write the index data and cache + int count = mIndexMetaData.size(); + dest.writeInt(count); + for (Integer key: mIndexMetaData.keySet()) { + dest.writeInt(key); + mIndexMetaData.get(key).writeToParcel(dest, flags); + } + count = mIndexRemoteViews.size(); + dest.writeInt(count); + for (Integer key: mIndexRemoteViews.keySet()) { + dest.writeInt(key); + mIndexRemoteViews.get(key).writeToParcel(dest, flags); + } + } + + @Override + public int describeContents() { + return 0; + } + public void insert(int position, RemoteViews v, long itemId, boolean isRequested) { // Trim the cache if we go beyond the count if (mIndexRemoteViews.size() >= mMaxCount) { @@ -747,11 +885,30 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mWorkerQueue = new Handler(mWorkerThread.getLooper()); mMainQueue = new Handler(Looper.myLooper(), this); + if (sCacheRemovalThread == null) { + sCacheRemovalThread = new HandlerThread("RemoteViewsAdapter-cachePruner"); + sCacheRemovalThread.start(); + sCacheRemovalQueue = new Handler(sCacheRemovalThread.getLooper()); + } + // Initialize the cache and the service connection on startup - mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); mServiceConnection = new RemoteViewsAdapterServiceConnection(this); - requestBindService(); + + Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, Integer> + (new Intent.FilterComparison(mIntent), mAppWidgetId); + + synchronized(sCachedRemoteViewsCaches) { + if (sCachedRemoteViewsCaches.containsKey(key)) { + Parcel src = sCachedRemoteViewsCaches.get(key); + src.setDataPosition(0); + mCache = new FixedSizeRemoteViewsCache(src); + mDataReady = true; + } else { + mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize); + requestBindService(); + } + } } @Override @@ -765,6 +922,44 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } } + public boolean isDataReady() { + return mDataReady; + } + + public void saveRemoteViewsCache() { + final Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, + Integer> (new Intent.FilterComparison(mIntent), mAppWidgetId); + + synchronized(sCachedRemoteViewsCaches) { + // If we already have a remove runnable posted for this key, remove it. + if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { + sCacheRemovalQueue.removeCallbacks(sRemoteViewsCacheRemoveRunnables.get(key)); + sRemoteViewsCacheRemoveRunnables.remove(key); + } + + Parcel p = Parcel.obtain(); + synchronized(mCache) { + mCache.writeToParcel(p, 0); + } + sCachedRemoteViewsCaches.put(key, p); + Runnable r = new Runnable() { + @Override + public void run() { + synchronized (sCachedRemoteViewsCaches) { + if (sCachedRemoteViewsCaches.containsKey(key)) { + sCachedRemoteViewsCaches.remove(key); + } + if (sRemoteViewsCacheRemoveRunnables.containsKey(key)) { + sRemoteViewsCacheRemoveRunnables.remove(key); + } + } + } + }; + sRemoteViewsCacheRemoveRunnables.put(key, r); + sCacheRemovalQueue.postDelayed(r, REMOTE_VIEWS_CACHE_DURATION); + } + } + private void loadNextIndexInBackground() { mWorkerQueue.post(new Runnable() { @Override |