Binding Taskbar directly from Launcher model

This allows taskbar to be loaded even in case of 3P Launchers
and removes dependency on LauncherActivity lifecycle

Bug: 187353581
Bug: 188788621
Test: Manual
Change-Id: I5a0988e0697b41677d4c58f0213aef14ec0c0972
This commit is contained in:
Sunny Goyal
2021-06-23 12:36:18 -07:00
parent d642b65137
commit 711c596c86
26 changed files with 427 additions and 242 deletions
@@ -23,12 +23,13 @@ import android.content.ContentResolver;
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 androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -36,19 +37,22 @@ import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.widget.LauncherAppWidgetHost;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -63,7 +67,10 @@ public class ModelWriter {
private final Context mContext;
private final LauncherModel mModel;
private final BgDataModel mBgDataModel;
private final Handler mUiHandler;
private final LooperExecutor mUiExecutor;
@Nullable
private final Callbacks mOwner;
private final boolean mHasVerticalHotseat;
private final boolean mVerifyChanges;
@@ -73,13 +80,15 @@ public class ModelWriter {
private boolean mPreparingToUndo;
public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
boolean hasVerticalHotseat, boolean verifyChanges) {
boolean hasVerticalHotseat, boolean verifyChanges,
@Nullable Callbacks owner) {
mContext = context;
mModel = model;
mBgDataModel = dataModel;
mHasVerticalHotseat = hasVerticalHotseat;
mVerifyChanges = verifyChanges;
mUiHandler = new Handler(Looper.getMainLooper());
mOwner = owner;
mUiExecutor = Executors.MAIN_EXECUTOR;
}
private void updateItemInfoProps(
@@ -162,6 +171,8 @@ public class ModelWriter {
public void moveItemInDatabase(final ItemInfo item,
int container, int screenId, int cellX, int cellY) {
updateItemInfoProps(item, container, screenId, cellX, cellY);
notifyItemModified(item);
enqueueDeleteRunnable(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
@@ -178,6 +189,7 @@ public class ModelWriter {
public void moveItemsInDatabase(final ArrayList<ItemInfo> items, int container, int screen) {
ArrayList<ContentValues> contentValues = new ArrayList<>();
int count = items.size();
notifyOtherCallbacks(c -> c.bindItemsModified(items));
for (int i = 0; i < count; i++) {
ItemInfo item = items.get(i);
@@ -203,8 +215,9 @@ public class ModelWriter {
updateItemInfoProps(item, container, screenId, cellX, cellY);
item.spanX = spanX;
item.spanY = spanY;
notifyItemModified(item);
((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () ->
MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
@@ -219,13 +232,18 @@ public class ModelWriter {
* Update an item to the database in a specified container.
*/
public void updateItemInDatabase(ItemInfo item) {
((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () -> {
notifyItemModified(item);
MODEL_EXECUTOR.execute(new UpdateItemRunnable(item, () -> {
ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
return writer;
}));
}
private void notifyItemModified(ItemInfo item) {
notifyOtherCallbacks(c -> c.bindItemsModified(Collections.singletonList(item)));
}
/**
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
@@ -236,10 +254,11 @@ public class ModelWriter {
final ContentResolver cr = mContext.getContentResolver();
item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE);
notifyOtherCallbacks(c -> c.bindItems(Collections.singletonList(item), false));
ModelVerifier verifier = new ModelVerifier();
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
((Executor) MODEL_EXECUTOR).execute(() -> {
MODEL_EXECUTOR.execute(() -> {
// Write the item on background thread, as some properties might have been updated in
// the background.
final ContentWriter writer = new ContentWriter(mContext);
@@ -281,6 +300,7 @@ public class ModelWriter {
(item) -> item.getTargetComponent() == null ? ""
: item.getTargetComponent().getPackageName()).collect(
Collectors.joining(",")), new Exception());
notifyDelete(items);
enqueueDeleteRunnable(() -> {
for (ItemInfo item : items) {
final Uri uri = Favorites.getContentUri(item.id);
@@ -297,6 +317,7 @@ public class ModelWriter {
*/
public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
ModelVerifier verifier = new ModelVerifier();
notifyDelete(Collections.singleton(info));
enqueueDeleteRunnable(() -> {
ContentResolver cr = mContext.getContentResolver();
@@ -315,6 +336,7 @@ public class ModelWriter {
* Deletes the widget info and the widget id.
*/
public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) {
notifyDelete(Collections.singleton(info));
if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
@@ -323,6 +345,10 @@ public class ModelWriter {
deleteItemFromDatabase(info);
}
private void notifyDelete(Collection<? extends ItemInfo> items) {
notifyOtherCallbacks(c -> c.bindWorkspaceComponentsRemoved(ItemInfoMatcher.ofItems(items)));
}
/**
* Delete operations tracked using {@link #enqueueDeleteRunnable} will only be called
* if {@link #commitDelete} is called. Note that one of {@link #commitDelete()} or
@@ -348,14 +374,14 @@ public class ModelWriter {
if (mPreparingToUndo) {
mDeleteRunnables.add(r);
} else {
((Executor) MODEL_EXECUTOR).execute(r);
MODEL_EXECUTOR.execute(r);
}
}
public void commitDelete() {
mPreparingToUndo = false;
for (Runnable runnable : mDeleteRunnables) {
((Executor) MODEL_EXECUTOR).execute(runnable);
MODEL_EXECUTOR.execute(runnable);
}
mDeleteRunnables.clear();
}
@@ -371,6 +397,20 @@ public class ModelWriter {
mModel.forceReload();
}
private void notifyOtherCallbacks(CallbackTask task) {
if (mOwner == null) {
// If the call is happening from a model, it will take care of updating the callbacks
return;
}
mUiExecutor.execute(() -> {
for (Callbacks c : mModel.getCallbacks()) {
if (c != mOwner) {
task.execute(c);
}
}
});
}
private class UpdateItemRunnable extends UpdateItemBaseRunnable {
private final ItemInfo mItem;
private final Supplier<ContentWriter> mWriter;
@@ -491,7 +531,7 @@ public class ModelWriter {
int executeId = mBgDataModel.lastBindId;
mUiHandler.post(() -> {
mUiExecutor.post(() -> {
int currentId = mBgDataModel.lastBindId;
if (currentId > executeId) {
// Model was already bound after job was executed.