From d1a0e8b5c8494945fe48c19fe0e13b6b161b90a7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 27 Aug 2015 17:45:46 -0700 Subject: [PATCH] Jailing the saved instance state of all the dynamically generated views Using itemId instead of generating a new id for each item. This is because if the process gets killed, View.generateId will get reset but we will still receive the generated item id map in onRestoreInstance. This will cause conflicts with newly generated item ids. We wrap all the generated homescreen views inside a single sparse array. This ensures that we do not cause any conflict with dynamically generated views in other parts of the UI. Bug: 16840760 Change-Id: I6fe69c2e1dd463402f51222715fae31b9d4dd240 --- res/values/config.xml | 3 ++ src/com/android/launcher3/CellLayout.java | 36 ++++++++++--- src/com/android/launcher3/Launcher.java | 43 ++------------- src/com/android/launcher3/Workspace.java | 1 + .../launcher3/util/ParcelableSparseArray.java | 52 +++++++++++++++++++ 5 files changed, 90 insertions(+), 45 deletions(-) create mode 100644 src/com/android/launcher3/util/ParcelableSparseArray.java diff --git a/res/values/config.xml b/res/values/config.xml index 93c6d14f4a..5cde42e8a0 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -81,6 +81,9 @@ + + + diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 5ae7310ffc..5539d9f8af 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -25,7 +25,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; -import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -48,13 +47,13 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.DecelerateInterpolator; -import android.widget.Toast; import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler; import com.android.launcher3.FolderIcon.FolderRingAnimator; import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; +import com.android.launcher3.util.ParcelableSparseArray; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -86,6 +85,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { private int mMaxGap; private boolean mDropPending = false; private boolean mIsDragTarget = true; + private boolean mJailContent = true; // These are temporary variables to prevent having to allocate a new object just to // return an (x, y) value from helper functions. Do NOT use them to maintain other state. @@ -189,7 +189,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mLauncher = (Launcher) context; DeviceProfile grid = mLauncher.getDeviceProfile(); - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0); mCellWidth = mCellHeight = -1; mFixedCellWidth = mFixedCellHeight = -1; @@ -203,10 +202,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mPreviousReorderDirection[0] = INVALID_DIRECTION; mPreviousReorderDirection[1] = INVALID_DIRECTION; - a.recycle(); - setAlwaysDrawnWithCacheEnabled(false); - final Resources res = getResources(); mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx; @@ -426,10 +422,36 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { } } - boolean getIsDragOverlapping() { + public boolean getIsDragOverlapping() { return mIsDragOverlapping; } + public void disableJailContent() { + mJailContent = false; + } + + @Override + protected void dispatchSaveInstanceState(SparseArray container) { + if (mJailContent) { + ParcelableSparseArray jail = getJailedArray(container); + super.dispatchSaveInstanceState(jail); + container.put(R.id.cell_layout_jail_id, jail); + } else { + super.dispatchSaveInstanceState(container); + } + } + + @Override + protected void dispatchRestoreInstanceState(SparseArray container) { + super.dispatchRestoreInstanceState(mJailContent ? getJailedArray(container) : container); + } + + private ParcelableSparseArray getJailedArray(SparseArray container) { + final Parcelable parcelable = container.get(R.id.cell_layout_jail_id); + return parcelable instanceof ParcelableSparseArray ? + (ParcelableSparseArray) parcelable : new ParcelableSparseArray(); + } + @Override protected void onDraw(Canvas canvas) { if (!mIsDragTarget) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 370f695862..37603379ac 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -124,7 +124,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; /** * Default launcher application. @@ -193,8 +192,6 @@ public class Launcher extends Activity private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info"; // Type: parcelable private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id"; - // Type: int[] - private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids"; static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed"; static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed"; @@ -228,9 +225,6 @@ public class Launcher extends Activity private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; private static final int ACTIVITY_START_DELAY = 1000; - private HashMap mItemIdToViewId = new HashMap(); - private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1); - // How long to wait before the new-shortcut animation automatically pans the workspace private static int NEW_APPS_PAGE_MOVE_DELAY = 500; private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; @@ -656,34 +650,12 @@ public class Launcher extends Activity return !isWorkspaceLoading(); } - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public static int generateViewId() { - if (Utilities.ATLEAST_JB_MR1) { - return View.generateViewId(); - } else { - // View.generateViewId() is not available. The following fallback logic is a copy - // of its implementation. - for (;;) { - final int result = sNextGeneratedId.get(); - // aapt-generated IDs have the high byte nonzero; clamp to the range under that. - int newValue = result + 1; - if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0. - if (sNextGeneratedId.compareAndSet(result, newValue)) { - return result; - } - } - } - } - public int getViewIdForItem(ItemInfo info) { - // This cast is safe given the > 2B range for int. - int itemId = (int) info.id; - if (mItemIdToViewId.containsKey(itemId)) { - return mItemIdToViewId.get(itemId); - } - int viewId = generateViewId(); - mItemIdToViewId.put(itemId, viewId); - return viewId; + // aapt-generated IDs have the high byte nonzero; clamp to the range under that. + // This cast is safe as long as the id < 0x00FFFFFF + // Since we jail all the dynamically generated views, there should be no clashes + // with any other views. + return (int) info.id; } /** @@ -1337,7 +1309,6 @@ public class Launcher extends Activity * * @param savedState The previous state. */ - @SuppressWarnings("unchecked") private void restoreState(Bundle savedState) { if (savedState == null) { return; @@ -1373,9 +1344,6 @@ public class Launcher extends Activity setWaitingForResult(true); mRestoring = true; } - - mItemIdToViewId = (HashMap) - savedState.getSerializable(RUNTIME_STATE_VIEW_IDS); } /** @@ -2019,7 +1987,6 @@ public class Launcher extends Activity // Save the current widgets tray? // TODO(hyunyoungs) - outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId); if (mLauncherCallbacks != null) { mLauncherCallbacks.onSaveInstanceState(outState); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b63ddbaa7e..8d70c0b39a 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -579,6 +579,7 @@ public class Workspace extends PagedView CellLayout customScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false); customScreen.disableDragTarget(); + customScreen.disableJailContent(); mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen); mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID); diff --git a/src/com/android/launcher3/util/ParcelableSparseArray.java b/src/com/android/launcher3/util/ParcelableSparseArray.java new file mode 100644 index 0000000000..093577e593 --- /dev/null +++ b/src/com/android/launcher3/util/ParcelableSparseArray.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.util; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseArray; + +public class ParcelableSparseArray extends SparseArray implements Parcelable { + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + final int count = size(); + dest.writeInt(count); + for (int i = 0; i < count; i++) { + dest.writeInt(keyAt(i)); + dest.writeParcelable(valueAt(i), 0); + } + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public ParcelableSparseArray createFromParcel(Parcel source) { + final ParcelableSparseArray array = new ParcelableSparseArray(); + final ClassLoader loader = array.getClass().getClassLoader(); + final int count = source.readInt(); + for (int i = 0; i < count; i++) { + array.put(source.readInt(), source.readParcelable(loader)); + } + return array; + } + + public ParcelableSparseArray[] newArray(int size) { + return new ParcelableSparseArray[size]; + } + }; +}