mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-03 09:26:51 +00:00
Add undo snackbar for deleting items
- Add methods to ModelWriter to prepareForUndoDelete, then enqueueDeleteRunnable, followed by commitDelete or abortDelete. - Add Snackbar floating view - Show Undo snackbar when dropping or flinging to delete target; if the undo action is clicked, we abort the delete, otherwise we commit it. Bug: 24238108 Change-Id: I9997235e1f8525cbb8b1fa2338099609e7358426
This commit is contained in:
@@ -28,6 +28,8 @@ import android.util.Log;
|
||||
import com.android.launcher3.FolderInfo;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetHost;
|
||||
import com.android.launcher3.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.LauncherModel.Callbacks;
|
||||
import com.android.launcher3.LauncherProvider;
|
||||
@@ -35,13 +37,14 @@ 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.logging.FileLog;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -60,6 +63,10 @@ public class ModelWriter {
|
||||
private final boolean mHasVerticalHotseat;
|
||||
private final boolean mVerifyChanges;
|
||||
|
||||
// Keep track of delete operations that occur when an Undo option is present; we may not commit.
|
||||
private final List<Runnable> mDeleteRunnables = new ArrayList<>();
|
||||
private boolean mPreparingToUndo;
|
||||
|
||||
public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
|
||||
boolean hasVerticalHotseat, boolean verifyChanges) {
|
||||
mContext = context;
|
||||
@@ -152,7 +159,7 @@ public class ModelWriter {
|
||||
.put(Favorites.RANK, item.rank)
|
||||
.put(Favorites.SCREEN, item.screenId);
|
||||
|
||||
mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
|
||||
enqueueDeleteRunnable(new UpdateItemRunnable(item, writer));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +183,7 @@ public class ModelWriter {
|
||||
|
||||
contentValues.add(values);
|
||||
}
|
||||
mWorkerExecutor.execute(new UpdateItemsRunnable(items, contentValues));
|
||||
enqueueDeleteRunnable(new UpdateItemsRunnable(items, contentValues));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +265,7 @@ public class ModelWriter {
|
||||
public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
|
||||
ModelVerifier verifier = new ModelVerifier();
|
||||
|
||||
mWorkerExecutor.execute(() -> {
|
||||
enqueueDeleteRunnable(() -> {
|
||||
for (ItemInfo item : items) {
|
||||
final Uri uri = Favorites.getContentUri(item.id);
|
||||
mContext.getContentResolver().delete(uri, null, null);
|
||||
@@ -275,7 +282,7 @@ public class ModelWriter {
|
||||
public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
|
||||
ModelVerifier verifier = new ModelVerifier();
|
||||
|
||||
mWorkerExecutor.execute(() -> {
|
||||
enqueueDeleteRunnable(() -> {
|
||||
ContentResolver cr = mContext.getContentResolver();
|
||||
cr.delete(LauncherSettings.Favorites.CONTENT_URI,
|
||||
LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
|
||||
@@ -288,6 +295,63 @@ public class ModelWriter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the widget info and the widget id.
|
||||
*/
|
||||
public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host) {
|
||||
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...
|
||||
enqueueDeleteRunnable(() -> host.deleteAppWidgetId(info.appWidgetId));
|
||||
}
|
||||
deleteItemFromDatabase(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* operations will remain uncommitted indefinitely.
|
||||
*/
|
||||
public void prepareToUndoDelete() {
|
||||
if (!mPreparingToUndo) {
|
||||
if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
|
||||
throw new IllegalStateException("There are still uncommitted delete operations!");
|
||||
}
|
||||
mDeleteRunnables.clear();
|
||||
mPreparingToUndo = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
* Otherwise, we run the Runnable immediately.
|
||||
*/
|
||||
public void enqueueDeleteRunnable(Runnable r) {
|
||||
if (mPreparingToUndo) {
|
||||
mDeleteRunnables.add(r);
|
||||
} else {
|
||||
mWorkerExecutor.execute(r);
|
||||
}
|
||||
}
|
||||
|
||||
public void commitDelete() {
|
||||
mPreparingToUndo = false;
|
||||
for (Runnable runnable : mDeleteRunnables) {
|
||||
mWorkerExecutor.execute(runnable);
|
||||
}
|
||||
mDeleteRunnables.clear();
|
||||
}
|
||||
|
||||
public void abortDelete() {
|
||||
mPreparingToUndo = false;
|
||||
mDeleteRunnables.clear();
|
||||
// We do a full reload here instead of just a rebind because Folders change their internal
|
||||
// state when dragging an item out, which clobbers the rebind unless we load from the DB.
|
||||
mModel.forceReload();
|
||||
}
|
||||
|
||||
private class UpdateItemRunnable extends UpdateItemBaseRunnable {
|
||||
private final ItemInfo mItem;
|
||||
private final ContentWriter mWriter;
|
||||
|
||||
Reference in New Issue
Block a user