Fixing bug when model was not reflected properly on the UI.
If launcher submits a job, and then reloads before the job is executed, the correct model
is not reflected on the Launcher. In that case, we simply rebind the launcher
Change-Id: I380242a4de13e7b2bc326d1a076f0a974435999c
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a1176f1..fdf468c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -400,17 +400,22 @@
getStateManager().reapplyState();
// TODO: We can probably avoid rebind when only screen size changed.
- int currentPage = mWorkspace.getNextPage();
- if (mModel.startLoader(currentPage)) {
- mWorkspace.setCurrentPage(currentPage);
- setWorkspaceLoading(true);
- }
+ rebindModel();
}
mOldConfig.setTo(newConfig);
super.onConfigurationChanged(newConfig);
}
+ @Override
+ public void rebindModel() {
+ int currentPage = mWorkspace.getNextPage();
+ if (mModel.startLoader(currentPage)) {
+ mWorkspace.setCurrentPage(currentPage);
+ setWorkspaceLoading(true);
+ }
+ }
+
private void initDeviceProfile(InvariantDeviceProfile idp) {
// Load configuration-specific DeviceProfile
mDeviceProfile = idp.getDeviceProfile(this);
@@ -420,7 +425,7 @@
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
- mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout(), true);
}
public RotationHelper getRotationHelper() {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 6646b78..04a32f7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -135,6 +135,8 @@
};
public interface Callbacks {
+ public void rebindModel();
+
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
public void startBinding();
@@ -196,8 +198,9 @@
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
- public ModelWriter getWriter(boolean hasVerticalHotseat) {
- return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
+ public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
+ return new ModelWriter(mApp.getContext(), this, sBgDataModel,
+ hasVerticalHotseat, verifyChanges);
}
static void checkItemInfoLocked(
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9aa30e7..fcdc088 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -29,7 +29,6 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.android.launcher3.widget.WidgetsListAdapter;
import java.util.ArrayList;
import java.util.concurrent.Executor;
@@ -80,19 +79,18 @@
*/
public final void scheduleCallbackTask(final CallbackTask task) {
final Callbacks callbacks = mModel.getCallback();
- mUiExecutor.execute(new Runnable() {
- public void run() {
- Callbacks cb = mModel.getCallback();
- if (callbacks == cb && cb != null) {
- task.execute(callbacks);
- }
+ mUiExecutor.execute(() -> {
+ Callbacks cb = mModel.getCallback();
+ if (callbacks == cb && cb != null) {
+ task.execute(callbacks);
}
});
}
public ModelWriter getModelWriter() {
- // Updates from model task, do not deal with icon position in hotseat.
- return mModel.getWriter(false /* hasVerticalHotseat */);
+ // Updates from model task, do not deal with icon position in hotseat. Also no need to
+ // verify changes as the ModelTasks always push the changes to callbacks
+ return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8640401..fff1e69 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -106,6 +106,11 @@
public final WidgetsModel widgetsModel = new WidgetsModel();
/**
+ * Id when the model was last bound
+ */
+ public int lastBindId = 0;
+
+ /**
* Clears all the data
*/
public synchronized void clear() {
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 5acc790..5d4a352 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -25,7 +25,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
@@ -37,7 +36,6 @@
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.android.launcher3.widget.WidgetsListAdapter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -100,6 +98,7 @@
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
+ mBgDataModel.lastBindId++;
}
final int currentScreen;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 40e0f49..72c703b 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -21,12 +21,15 @@
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -50,15 +53,23 @@
public static final boolean DEBUG_DELETE = true;
private final Context mContext;
+ private final LauncherModel mModel;
private final BgDataModel mBgDataModel;
+ private final Handler mUiHandler;
+
private final Executor mWorkerExecutor;
private final boolean mHasVerticalHotseat;
+ private final boolean mVerifyChanges;
- public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
+ public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
+ boolean hasVerticalHotseat, boolean verifyChanges) {
mContext = context;
+ mModel = model;
mBgDataModel = dataModel;
mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
mHasVerticalHotseat = hasVerticalHotseat;
+ mVerifyChanges = verifyChanges;
+ mUiHandler = new Handler(Looper.getMainLooper());
}
private void updateItemInfoProps(
@@ -214,15 +225,16 @@
item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getLong(Settings.EXTRA_VALUE);
writer.put(Favorites._ID, item.id);
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- mWorkerExecutor.execute(new Runnable() {
- public void run() {
- cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+ ModelVerifier verifier = new ModelVerifier();
- synchronized (mBgDataModel) {
- checkItemInfoLocked(item.id, item, stackTrace);
- mBgDataModel.addItem(mContext, item, true);
- }
+ final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+ mWorkerExecutor.execute(() -> {
+ cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+
+ synchronized (mBgDataModel) {
+ checkItemInfoLocked(item.id, item, stackTrace);
+ mBgDataModel.addItem(mContext, item, true);
+ verifier.verifyModel();
}
});
}
@@ -253,6 +265,7 @@
}
FileLog.d(TAG, "Finished deleting items");
}
+ ModelVerifier verifier = new ModelVerifier();
mWorkerExecutor.execute(() -> {
for (ItemInfo item : items) {
@@ -260,6 +273,7 @@
mContext.getContentResolver().delete(uri, null, null);
mBgDataModel.removeItem(mContext, item);
+ verifier.verifyModel();
}
});
}
@@ -273,6 +287,8 @@
FileLog.d(TAG, "Deleting folder " + info, new Exception());
}
+ ModelVerifier verifier = new ModelVerifier();
+
mWorkerExecutor.execute(() -> {
ContentResolver cr = mContext.getContentResolver();
cr.delete(LauncherSettings.Favorites.CONTENT_URI,
@@ -282,6 +298,7 @@
cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
mBgDataModel.removeItem(mContext, info);
+ verifier.verifyModel();
});
}
@@ -336,6 +353,7 @@
private abstract class UpdateItemBaseRunnable implements Runnable {
private final StackTraceElement[] mStackTrace;
+ private final ModelVerifier mVerifier = new ModelVerifier();
UpdateItemBaseRunnable() {
mStackTrace = new Throwable().getStackTrace();
@@ -380,7 +398,45 @@
} else {
mBgDataModel.workspaceItems.remove(modelItem);
}
+ mVerifier.verifyModel();
}
}
}
+
+ /**
+ * Utility class to verify model updates are propagated properly to the callback.
+ */
+ public class ModelVerifier {
+
+ final int startId;
+
+ ModelVerifier() {
+ startId = mBgDataModel.lastBindId;
+ }
+
+ void verifyModel() {
+ if (!mVerifyChanges || mModel.getCallback() == null) {
+ return;
+ }
+
+ int executeId = mBgDataModel.lastBindId;
+
+ mUiHandler.post(() -> {
+ int currentId = mBgDataModel.lastBindId;
+ if (currentId > executeId) {
+ // Model was already bound after job was executed.
+ return;
+ }
+ if (executeId == startId) {
+ // Bound model has not changed during the job
+ return;
+ }
+ // Bound model was changed between submitting the job and executing the job
+ Callbacks callbacks = mModel.getCallback();
+ if (callbacks != null) {
+ callbacks.rebindModel();
+ }
+ });
+ }
+ }
}