diff options
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 8 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 25 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 15 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 6 | ||||
-rw-r--r-- | src/com/android/launcher3/model/GridSizeMigrationTask.java | 59 | ||||
-rw-r--r-- | src/com/android/launcher3/provider/LauncherDbUtils.java | 122 | ||||
-rw-r--r-- | src/com/android/launcher3/provider/LossyScreenMigrationTask.java | 108 | ||||
-rw-r--r-- | src_config/com/android/launcher3/config/FeatureFlags.java | 3 |
8 files changed, 297 insertions, 49 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index a504250aa..705cb58af 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3593,10 +3593,14 @@ public class Launcher extends Activity @Override public void bindScreens(ArrayList<Long> orderedScreenIds) { // Make sure the first screen is always at the start. - if (orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) { + if (FeatureFlags.QSB_ON_FIRST_SCREEN && + orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) { orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID); orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID); mModel.updateWorkspaceScreenOrder(this, orderedScreenIds); + } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) { + // If there are no screens, we need to have an empty screen + mWorkspace.addExtraEmptyScreen(); } bindAddScreens(orderedScreenIds); @@ -3612,7 +3616,7 @@ public class Launcher extends Activity int count = orderedScreenIds.size(); for (int i = 0; i < count; i++) { long screenId = orderedScreenIds.get(i); - if (screenId != Workspace.FIRST_SCREEN_ID) { + if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) { // No need to bind the first screen, as its always bound. mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 557a91a42..4c80ccf69 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -50,6 +50,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.folder.Folder; @@ -57,6 +58,7 @@ import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.CursorIconInfo; import com.android.launcher3.util.FlagOp; @@ -1246,22 +1248,8 @@ public class LauncherModel extends BroadcastReceiver final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; // Get screens ordered by rank. - final Cursor sc = contentResolver.query(screensUri, null, null, null, - LauncherSettings.WorkspaceScreens.SCREEN_RANK); - ArrayList<Long> screenIds = new ArrayList<Long>(); - try { - final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID); - while (sc.moveToNext()) { - try { - screenIds.add(sc.getLong(idIndex)); - } catch (Exception e) { - FileLog.d(TAG, "Invalid screen id", e); - } - } - } finally { - sc.close(); - } - return screenIds; + return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query( + screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK)); } /** @@ -1522,8 +1510,9 @@ public class LauncherModel extends BroadcastReceiver if (!occupied.containsKey(item.screenId)) { GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1); if (item.screenId == Workspace.FIRST_SCREEN_ID) { - // Mark the first row as occupied in order to account for the QSB. - screen.markCells(0, 0, countX + 1, 1, true); + // Mark the first row as occupied (if the feature is enabled) + // in order to account for the QSB. + screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN); } occupied.put(item.screenId, screen); } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index dfb8ba277..4e7d57bb2 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -23,7 +23,6 @@ import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; -import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; @@ -48,15 +47,16 @@ import android.os.Process; import android.os.UserManager; import android.text.TextUtils; import android.util.Log; -import android.util.SparseArray; import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.WorkspaceScreens; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.dynamicui.ExtractionUtils; +import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.util.ManagedProfileHeuristic; import com.android.launcher3.util.NoLocaleSqliteContext; @@ -66,13 +66,12 @@ import com.android.launcher3.util.Thunk; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; public class LauncherProvider extends ContentProvider { private static final String TAG = "LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 26; + private static final int DATABASE_VERSION = 27; public static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -780,7 +779,13 @@ public class LauncherProvider extends ContentProvider { ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext); case 25: convertShortcutsToLauncherActivities(db); - case 26: { + case 26: + // QSB was moved to the grid. Clear the first row on screen 0. + if (FeatureFlags.QSB_ON_FIRST_SCREEN && + !LauncherDbUtils.prepareScreenZeroToHostQsb(db)) { + break; + } + case 27: { // DB Upgraded successfully return; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 85ba57c2d..60a1c9d8d 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -510,6 +510,9 @@ public class Workspace extends PagedView * @param qsb an exisitng qsb to recycle or null. */ public void bindAndInitFirstWorkspaceScreen(View qsb) { + if (!FeatureFlags.QSB_ON_FIRST_SCREEN) { + return; + } // Add the first page CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0); @@ -909,7 +912,8 @@ public class Workspace extends PagedView long id = mWorkspaceScreens.keyAt(i); CellLayout cl = mWorkspaceScreens.valueAt(i); // FIRST_SCREEN_ID can never be removed. - if (id > FIRST_SCREEN_ID && cl.getShortcutsAndWidgets().getChildCount() == 0) { + if ((!FeatureFlags.QSB_ON_FIRST_SCREEN || id > FIRST_SCREEN_ID) + && cl.getShortcutsAndWidgets().getChildCount() == 0) { removeScreens.add(id); } } diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index dd11bde6d..8762fef74 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Point; import android.net.Uri; @@ -26,6 +27,7 @@ import com.android.launcher3.Workspace; import com.android.launcher3.backup.nano.BackupProtos; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.LongArrayMap; @@ -66,9 +68,9 @@ public class GridSizeMigrationTask { private final HashMap<String, Point> mWidgetMinSize = new HashMap<>(); private final ContentValues mTempValues = new ContentValues(); - private final ArrayList<Long> mEntryToRemove = new ArrayList<>(); + protected final ArrayList<Long> mEntryToRemove = new ArrayList<>(); private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>(); - private final ArrayList<DbEntry> mCarryOver = new ArrayList<>(); + protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>(); private final HashSet<String> mValidPackages; private final int mSrcX, mSrcY; @@ -269,9 +271,10 @@ public class GridSizeMigrationTask { * 3) If all those items from the above list can be placed on this screen, place them * (otherwise they are placed on a new screen). */ - private void migrateScreen(long screenId) { + protected void migrateScreen(long screenId) { // If we are migrating the first screen, do not touch the first row. - int startY = screenId == Workspace.FIRST_SCREEN_ID ? 1 : 0; + int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID) + ? 1 : 0; ArrayList<DbEntry> items = loadWorkspaceEntries(screenId); @@ -366,7 +369,7 @@ public class GridSizeMigrationTask { /** * Updates an item in the DB. */ - private void update(DbEntry item) { + protected void update(DbEntry item) { mTempValues.clear(); item.addToContentValues(mTempValues); mUpdateOperations.add(ContentProviderOperation @@ -677,8 +680,8 @@ public class GridSizeMigrationTask { /** * Loads entries for a particular screen id. */ - private ArrayList<DbEntry> loadWorkspaceEntries(long screen) { - Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) { + Cursor c = queryWorkspace( new String[]{ Favorites._ID, // 0 Favorites.ITEM_TYPE, // 1 @@ -690,7 +693,7 @@ public class GridSizeMigrationTask { Favorites.APPWIDGET_PROVIDER, // 7 Favorites.APPWIDGET_ID}, // 8 Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP - + " AND " + Favorites.SCREEN + " = " + screen, null, null, null); + + " AND " + Favorites.SCREEN + " = " + screen); final int indexId = c.getColumnIndexOrThrow(Favorites._ID); final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); @@ -776,9 +779,9 @@ public class GridSizeMigrationTask { * @return the number of valid items in the folder. */ private int getFolderItemsCount(long folderId) { - Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + Cursor c = queryWorkspace( new String[]{Favorites._ID, Favorites.INTENT}, - Favorites.CONTAINER + " = " + folderId, null, null, null); + Favorites.CONTAINER + " = " + folderId); int total = 0; while (c.moveToNext()) { @@ -793,6 +796,11 @@ public class GridSizeMigrationTask { return total; } + protected Cursor queryWorkspace(String[] columns, String where) { + return mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + columns, where, null, null, null); + } + /** * Verifies if the intent should be restored. */ @@ -815,7 +823,7 @@ public class GridSizeMigrationTask { } } - private static class DbEntry extends ItemInfo implements Comparable<DbEntry> { + protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> { public float weight; @@ -913,18 +921,7 @@ public class GridSizeMigrationTask { try { boolean dbChanged = false; - // Initialize list of valid packages. This contain all the packages which are already on - // the device and packages which are being installed. Any item which doesn't belong to - // this set is removed. - // Since the loader removes such items anyway, removing these items here doesn't cause - // any extra data loss and gives us more free space on the grid for better migration. - HashSet validPackages = new HashSet<>(); - for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) { - validPackages.add(info.packageName); - } - validPackages.addAll(PackageInstallerCompat.getInstance(context) - .updateAndGetActiveSessionCache().keySet()); - + HashSet validPackages = getValidPackages(context); // Hotseat Point srcHotseatSize = parsePoint(prefs.getString( KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString)); @@ -1022,4 +1019,20 @@ public class GridSizeMigrationTask { .apply(); } } + + protected static HashSet<String> getValidPackages(Context context) { + // Initialize list of valid packages. This contain all the packages which are already on + // the device and packages which are being installed. Any item which doesn't belong to + // this set is removed. + // Since the loader removes such items anyway, removing these items here doesn't cause + // any extra data loss and gives us more free space on the grid for better migration. + HashSet validPackages = new HashSet<>(); + for (PackageInfo info : context.getPackageManager() + .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { + validPackages.add(info.packageName); + } + validPackages.addAll(PackageInstallerCompat.getInstance(context) + .updateAndGetActiveSessionCache().keySet()); + return validPackages; + } } diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java new file mode 100644 index 000000000..faa5fad12 --- /dev/null +++ b/src/com/android/launcher3/provider/LauncherDbUtils.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2016 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.provider; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.LauncherSettings.WorkspaceScreens; +import com.android.launcher3.logging.FileLog; + +import java.util.ArrayList; + +/** + * A set of utility methods for Launcher DB used for DB updates and migration. + */ +public class LauncherDbUtils { + + private static final String TAG = "LauncherDbUtils"; + + /** + * Makes the first screen as screen 0 (if screen 0 already exists, + * renames it to some other number). + * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear + * the first row. The items in the first screen are moved and resized but the carry-forward + * items are simply deleted. + */ + public static boolean prepareScreenZeroToHostQsb(SQLiteDatabase db) { + db.beginTransaction(); + try { + // Get the existing screens + ArrayList<Long> screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME, + null, null, null, null, null, WorkspaceScreens.SCREEN_RANK)); + + if (screenIds.isEmpty()) { + // No update needed + return true; + } + if (screenIds.get(0) != 0) { + // First screen is not 0, we need to rename screens + if (screenIds.indexOf(0L) > -1) { + // There is already a screen 0. First rename it to a differen screen. + long newScreenId = 1; + while (screenIds.indexOf(newScreenId) > -1) newScreenId++; + renameScreen(db, 0, newScreenId); + } + + // Rename the first screen to 0. + renameScreen(db, screenIds.get(0), 0); + } + + // Check if the first row is empty + try (Cursor c = db.query(Favorites.TABLE_NAME, null, + "container = -100 and screen = 0 and cellY = 0", null, null, null, null)) { + if (c.getCount() == 0) { + // First row is empty, no need to migrate. + return true; + } + } + + LauncherAppState app = LauncherAppState.getInstance(); + new LossyScreenMigrationTask(app.getContext(), app.getInvariantDeviceProfile(), db) + .migrateScreen0(); + db.setTransactionSuccessful(); + return true; + } catch (Exception e) { + Log.e(TAG, "Failed to update workspace size", e); + return false; + } finally { + db.endTransaction(); + } + } + + private static void renameScreen(SQLiteDatabase db, long oldScreen, long newScreen) { + String[] whereParams = new String[] { Long.toString(oldScreen) }; + + ContentValues values = new ContentValues(); + values.put(WorkspaceScreens._ID, newScreen); + db.update(WorkspaceScreens.TABLE_NAME, values, "_id = ?", whereParams); + + values.clear(); + values.put(Favorites.SCREEN, newScreen); + db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams); + } + + /** + * Parses the cursor containing workspace screens table and returns the list of screen IDs + */ + public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) { + ArrayList<Long> screenIds = new ArrayList<Long>(); + try { + final int idIndex = sc.getColumnIndexOrThrow(WorkspaceScreens._ID); + while (sc.moveToNext()) { + try { + screenIds.add(sc.getLong(idIndex)); + } catch (Exception e) { + FileLog.d(TAG, "Invalid screen id", e); + } + } + } finally { + sc.close(); + } + return screenIds; + } +} diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java new file mode 100644 index 000000000..bb6ed0de9 --- /dev/null +++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 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.provider; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Point; +import android.util.Log; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace; +import com.android.launcher3.model.GridSizeMigrationTask; +import com.android.launcher3.util.LongArrayMap; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * An extension of {@link GridSizeMigrationTask} which migrates only one screen and + * deletes all carry-forward items. + */ +public class LossyScreenMigrationTask extends GridSizeMigrationTask { + + private final SQLiteDatabase mDb; + + private final LongArrayMap<DbEntry> mOriginalItems; + private final LongArrayMap<DbEntry> mUpdates; + + protected LossyScreenMigrationTask( + Context context, InvariantDeviceProfile idp, SQLiteDatabase db) { + // Decrease the rows count by 1 + super(context, idp, getValidPackages(context), new HashMap<String, Point>(), + new Point(idp.numColumns, idp.numRows + 1), new Point(idp.numColumns, idp.numRows)); + + mDb = db; + mOriginalItems = new LongArrayMap<>(); + mUpdates = new LongArrayMap<>(); + } + + @Override + protected Cursor queryWorkspace(String[] columns, String where) { + return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null); + } + + @Override + protected void update(DbEntry item) { + mUpdates.put(item.id, item.copy()); + } + + @Override + protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) { + ArrayList<DbEntry> result = super.loadWorkspaceEntries(screen); + for (DbEntry entry : result) { + mOriginalItems.put(entry.id, entry.copy()); + + // Shift all items by 1 in y direction and mark them for update. + entry.cellY++; + mUpdates.put(entry.id, entry.copy()); + } + + return result; + } + + public void migrateScreen0() { + migrateScreen(Workspace.FIRST_SCREEN_ID); + + ContentValues tempValues = new ContentValues(); + for (DbEntry update : mUpdates) { + DbEntry org = mOriginalItems.get(update.id); + + if (org.cellX != update.cellX || org.cellY != update.cellY + || org.spanX != update.spanX || org.spanY != update.spanY) { + tempValues.clear(); + update.addToContentValues(tempValues); + mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?", + new String[] {Long.toString(update.id)}); + } + } + + // Delete any carry over items as we are only migration a single screen. + for (DbEntry entry : mCarryOver) { + mEntryToRemove.add(entry.id); + } + + if (!mEntryToRemove.isEmpty()) { + mDb.delete(Favorites.TABLE_NAME, + Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), null); + } + } +} diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java index 2b9e6ce3d..9806afd21 100644 --- a/src_config/com/android/launcher3/config/FeatureFlags.java +++ b/src_config/com/android/launcher3/config/FeatureFlags.java @@ -30,4 +30,7 @@ public final class FeatureFlags { public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = false; public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false; public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true; + + // Feature flag to enable moving the QSB on the 0th screen of the workspace + public static final boolean QSB_ON_FIRST_SCREEN = true; } |