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. Change-Id: I6fe69c2e1dd463402f51222715fae31b9d4dd240
This commit is contained in:
parent
fafd4c276e
commit
e2fd14b9f6
|
@ -76,6 +76,9 @@
|
||||||
<!-- View ID to use for QSB widget -->
|
<!-- View ID to use for QSB widget -->
|
||||||
<item type="id" name="qsb_widget" />
|
<item type="id" name="qsb_widget" />
|
||||||
|
|
||||||
|
<!-- View ID used by cell layout to jail its content -->
|
||||||
|
<item type="id" name="cell_layout_jail_id" />
|
||||||
|
|
||||||
<!-- Accessibility actions -->
|
<!-- Accessibility actions -->
|
||||||
<item type="id" name="action_remove" />
|
<item type="id" name="action_remove" />
|
||||||
<item type="id" name="action_uninstall" />
|
<item type="id" name="action_uninstall" />
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
|
@ -54,6 +53,7 @@ import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
|
||||||
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
|
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
|
||||||
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
|
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
|
||||||
import com.android.launcher3.config.ProviderConfig;
|
import com.android.launcher3.config.ProviderConfig;
|
||||||
|
import com.android.launcher3.util.ParcelableSparseArray;
|
||||||
import com.android.launcher3.util.Thunk;
|
import com.android.launcher3.util.Thunk;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -85,6 +85,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
||||||
private int mMaxGap;
|
private int mMaxGap;
|
||||||
private boolean mDropPending = false;
|
private boolean mDropPending = false;
|
||||||
private boolean mIsDragTarget = true;
|
private boolean mIsDragTarget = true;
|
||||||
|
private boolean mJailContent = true;
|
||||||
|
|
||||||
// These are temporary variables to prevent having to allocate a new object just to
|
// 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.
|
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
|
||||||
|
@ -188,7 +189,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
||||||
mLauncher = (Launcher) context;
|
mLauncher = (Launcher) context;
|
||||||
|
|
||||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
|
|
||||||
|
|
||||||
mCellWidth = mCellHeight = -1;
|
mCellWidth = mCellHeight = -1;
|
||||||
mFixedCellWidth = mFixedCellHeight = -1;
|
mFixedCellWidth = mFixedCellHeight = -1;
|
||||||
|
@ -202,10 +202,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
||||||
mPreviousReorderDirection[0] = INVALID_DIRECTION;
|
mPreviousReorderDirection[0] = INVALID_DIRECTION;
|
||||||
mPreviousReorderDirection[1] = INVALID_DIRECTION;
|
mPreviousReorderDirection[1] = INVALID_DIRECTION;
|
||||||
|
|
||||||
a.recycle();
|
|
||||||
|
|
||||||
setAlwaysDrawnWithCacheEnabled(false);
|
setAlwaysDrawnWithCacheEnabled(false);
|
||||||
|
|
||||||
final Resources res = getResources();
|
final Resources res = getResources();
|
||||||
mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
|
mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
|
||||||
|
|
||||||
|
@ -425,6 +422,32 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void disableJailContent() {
|
||||||
|
mJailContent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchSaveInstanceState(SparseArray<Parcelable> 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<Parcelable> container) {
|
||||||
|
super.dispatchRestoreInstanceState(mJailContent ? getJailedArray(container) : container);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParcelableSparseArray getJailedArray(SparseArray<Parcelable> container) {
|
||||||
|
final Parcelable parcelable = container.get(R.id.cell_layout_jail_id);
|
||||||
|
return parcelable instanceof ParcelableSparseArray ?
|
||||||
|
(ParcelableSparseArray) parcelable : new ParcelableSparseArray();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getIsDragOverlapping() {
|
public boolean getIsDragOverlapping() {
|
||||||
return mIsDragOverlapping;
|
return mIsDragOverlapping;
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,6 @@ import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default launcher application.
|
* Default launcher application.
|
||||||
|
@ -183,8 +182,6 @@ public class Launcher extends Activity
|
||||||
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
|
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
|
||||||
// Type: parcelable
|
// Type: parcelable
|
||||||
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
|
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 INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
|
||||||
static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
|
static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
|
||||||
|
@ -218,9 +215,6 @@ public class Launcher extends Activity
|
||||||
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
|
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
|
||||||
private static final int ACTIVITY_START_DELAY = 1000;
|
private static final int ACTIVITY_START_DELAY = 1000;
|
||||||
|
|
||||||
private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>();
|
|
||||||
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
|
|
||||||
|
|
||||||
// How long to wait before the new-shortcut animation automatically pans the workspace
|
// 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_PAGE_MOVE_DELAY = 500;
|
||||||
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
|
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
|
||||||
|
@ -635,34 +629,12 @@ public class Launcher extends Activity
|
||||||
return !isWorkspaceLoading();
|
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) {
|
public int getViewIdForItem(ItemInfo info) {
|
||||||
// This cast is safe given the > 2B range for int.
|
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
|
||||||
int itemId = (int) info.id;
|
// This cast is safe as long as the id < 0x00FFFFFF
|
||||||
if (mItemIdToViewId.containsKey(itemId)) {
|
// Since we jail all the dynamically generated views, there should be no clashes
|
||||||
return mItemIdToViewId.get(itemId);
|
// with any other views.
|
||||||
}
|
return (int) info.id;
|
||||||
int viewId = generateViewId();
|
|
||||||
mItemIdToViewId.put(itemId, viewId);
|
|
||||||
return viewId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1281,7 +1253,6 @@ public class Launcher extends Activity
|
||||||
*
|
*
|
||||||
* @param savedState The previous state.
|
* @param savedState The previous state.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void restoreState(Bundle savedState) {
|
private void restoreState(Bundle savedState) {
|
||||||
if (savedState == null) {
|
if (savedState == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -1310,9 +1281,6 @@ public class Launcher extends Activity
|
||||||
setWaitingForResult(true);
|
setWaitingForResult(true);
|
||||||
mRestoring = true;
|
mRestoring = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mItemIdToViewId = (HashMap<Integer, Integer>)
|
|
||||||
savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1936,7 +1904,6 @@ public class Launcher extends Activity
|
||||||
|
|
||||||
// Save the current widgets tray?
|
// Save the current widgets tray?
|
||||||
// TODO(hyunyoungs)
|
// TODO(hyunyoungs)
|
||||||
outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
|
|
||||||
|
|
||||||
if (mLauncherCallbacks != null) {
|
if (mLauncherCallbacks != null) {
|
||||||
mLauncherCallbacks.onSaveInstanceState(outState);
|
mLauncherCallbacks.onSaveInstanceState(outState);
|
||||||
|
|
|
@ -568,6 +568,7 @@ public class Workspace extends PagedView
|
||||||
CellLayout customScreen = (CellLayout)
|
CellLayout customScreen = (CellLayout)
|
||||||
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
|
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
|
||||||
customScreen.disableDragTarget();
|
customScreen.disableDragTarget();
|
||||||
|
customScreen.disableJailContent();
|
||||||
|
|
||||||
mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
|
mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
|
||||||
mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
|
mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
|
||||||
|
|
|
@ -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<Parcelable> 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<ParcelableSparseArray> CREATOR =
|
||||||
|
new Parcelable.Creator<ParcelableSparseArray>() {
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue