Fixing launcher UI not reapplied properly when IDP changed as a result of display changes

> Removing unnecessary check when binding model data as its safe to bind old data,
  we should just not persist it (that check is already there in model writer)
> Adding additional check in model bind to skip old bind if there are multiple binds

Bug: 126259547
Bug: 118441555
Change-Id: Ic250d2af13797d3e70c6e1fc70da65ebe6ab8d70
This commit is contained in:
Sunny Goyal 2019-03-06 15:38:32 -08:00
parent 826405d095
commit 371ea051d1
6 changed files with 35 additions and 86 deletions

View File

@ -275,14 +275,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
mLongPressHelper.setLongPressTimeoutFactor(longPressTimeoutFactor);
}
@Override
public void setTag(Object tag) {
if (tag != null) {
LauncherModel.checkItemInfo((ItemInfo) tag);
}
super.setTag(tag);
}
@Override
public void refreshDrawableState() {
if (!mIgnorePressedStateChange) {

View File

@ -114,8 +114,6 @@ public class ItemInfo {
ItemInfo(ItemInfo info) {
copyFrom(info);
// tempdebug:
LauncherModel.checkItemInfo(this);
}
public void copyFrom(ItemInfo info) {

View File

@ -378,14 +378,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
mUserEventDispatcher = null;
initDeviceProfile(mDeviceProfile.inv);
dispatchDeviceProfileChanged();
reapplyUi();
mDragLayer.recreateControllers();
// TODO: We can probably avoid rebind when only screen size changed.
rebindModel();
onIdpChanged(mDeviceProfile.inv);
}
mOldConfig.setTo(newConfig);
@ -410,8 +403,19 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
@Override
public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
onIdpChanged(idp);
}
private void onIdpChanged(InvariantDeviceProfile idp) {
mUserEventDispatcher = null;
initDeviceProfile(idp);
getRootView().dispatchInsets();
dispatchDeviceProfileChanged();
reapplyUi();
mDragLayer.recreateControllers();
// TODO: We can probably avoid rebind when only screen size changed.
rebindModel();
}
private void initDeviceProfile(InvariantDeviceProfile idp) {

View File

@ -34,7 +34,6 @@ import android.util.Pair;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
@ -207,57 +206,6 @@ public class LauncherModel extends BroadcastReceiver
hasVerticalHotseat, verifyChanges);
}
static void checkItemInfoLocked(
final int itemId, final ItemInfo item, StackTraceElement[] stackTrace) {
ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// If it is a release build on a release device, check all the data is consistent as
// we don't want to crash non-dev users.
if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
if (modelItem.title.toString().equals(item.title.toString()) &&
modelItem.getIntent().filterEquals(item.getIntent()) &&
modelItem.id == item.id &&
modelItem.itemType == item.itemType &&
modelItem.container == item.container &&
modelItem.screenId == item.screenId &&
modelItem.cellX == item.cellX &&
modelItem.cellY == item.cellY &&
modelItem.spanX == item.spanX &&
modelItem.spanY == item.spanY) {
// For all intents and purposes, this is the same object
return;
}
}
// the modelItem needs to match up perfectly with item if our model is
// to be consistent with the database-- for now, just require
// modelItem == item or the equality check above
String msg = "item: " + ((item != null) ? item.toString() : "null") +
"modelItem: " +
((modelItem != null) ? modelItem.toString() : "null") +
"Error: ItemInfo passed to checkItemInfo doesn't match original";
RuntimeException e = new RuntimeException(msg);
if (stackTrace != null) {
e.setStackTrace(stackTrace);
}
throw e;
}
}
static void checkItemInfo(final ItemInfo item) {
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
final int itemId = item.id;
Runnable r = new Runnable() {
public void run() {
synchronized (sBgDataModel) {
checkItemInfoLocked(itemId, item, stackTrace);
}
}
};
runOnWorkerThread(r);
}
/**
* Set this as the current Launcher activity object for the loader.
*/

View File

@ -61,6 +61,8 @@ public abstract class BaseLoaderResults {
protected final WeakReference<Callbacks> mCallbacks;
private int mMyBindingId;
public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
mUiExecutor = new MainThreadExecutor();
@ -94,6 +96,7 @@ public abstract class BaseLoaderResults {
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
mBgDataModel.lastBindId++;
mMyBindingId = mBgDataModel.lastBindId;
}
final int currentScreen;
@ -285,6 +288,10 @@ public abstract class BaseLoaderResults {
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
executor.execute(() -> {
if (mMyBindingId != mBgDataModel.lastBindId) {
Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
return;
}
Callbacks callbacks = mCallbacks.get();
if (callbacks != null) {
task.execute(callbacks);

View File

@ -37,6 +37,7 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
@ -112,19 +113,18 @@ public class ModelWriter {
ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
ShortcutInfo shortcut = (ShortcutInfo) item;
if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
modelShortcut.intent.filterEquals(shortcut.intent) &&
modelShortcut.id == shortcut.id &&
modelShortcut.itemType == shortcut.itemType &&
modelShortcut.container == shortcut.container &&
modelShortcut.screenId == shortcut.screenId &&
modelShortcut.cellX == shortcut.cellX &&
modelShortcut.cellY == shortcut.cellY &&
modelShortcut.spanX == shortcut.spanX &&
modelShortcut.spanY == shortcut.spanY) {
if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
if (modelItem.title.toString().equals(item.title.toString()) &&
modelItem.getIntent().filterEquals(item.getIntent()) &&
modelItem.id == item.id &&
modelItem.itemType == item.itemType &&
modelItem.container == item.container &&
modelItem.screenId == item.screenId &&
modelItem.cellX == item.cellX &&
modelItem.cellY == item.cellY &&
modelItem.spanX == item.spanX &&
modelItem.spanY == item.spanY) {
// For all intents and purposes, this is the same object
return;
}
@ -310,7 +310,7 @@ public class ModelWriter {
/**
* Delete operations tracked using {@link #enqueueDeleteRunnable} will only be called
* if {@link #commitDelete} is called. Note that one of {@link #commitDelete()} or
* {@link #abortDelete()} MUST be called after this method, or else all delete
* {@link #abortDelete} MUST be called after this method, or else all delete
* operations will remain uncommitted indefinitely.
*/
public void prepareToUndoDelete() {
@ -325,7 +325,7 @@ public class ModelWriter {
/**
* If {@link #prepareToUndoDelete} has been called, we store the Runnable to be run when
* {@link #commitDelete()} is called (or abandoned if {@link #abortDelete()} is called).
* {@link #commitDelete()} is called (or abandoned if {@link #abortDelete} is called).
* Otherwise, we run the Runnable immediately.
*/
private void enqueueDeleteRunnable(Runnable r) {