diff options
Diffstat (limited to 'src/com/android/launcher3/LauncherProvider.java')
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 604 |
1 files changed, 369 insertions, 235 deletions
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 1040b1173..cc5e18bc1 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.annotation.TargetApi; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.content.ComponentName; @@ -29,14 +30,21 @@ import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; +import android.database.sqlite.SQLiteStatement; import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Bundle; +import android.os.Process; import android.os.StrictMode; +import android.os.UserManager; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -46,42 +54,35 @@ import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.ProviderConfig; +import com.android.launcher3.util.ManagedProfileHeuristic; +import com.android.launcher3.util.Thunk; import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int MIN_DATABASE_VERSION = 12; - private static final int DATABASE_VERSION = 22; + private static final int DATABASE_VERSION = 26; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; - static final String TABLE_FAVORITES = "favorites"; - static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens"; - static final String PARAMETER_NOTIFY = "notify"; - static final String UPGRADED_FROM_OLD_DATABASE = "UPGRADED_FROM_OLD_DATABASE"; + static final String TABLE_FAVORITES = LauncherSettings.Favorites.TABLE_NAME; + static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; - private LauncherProviderChangeListener mListener; + private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name"; - /** - * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when - * {@link AppWidgetHost#deleteHost()} is called during database creation. - * Use this to recall {@link AppWidgetHost#startListening()} if needed. - */ - static final Uri CONTENT_APPWIDGET_RESET_URI = - Uri.parse("content://" + AUTHORITY + "/appWidgetReset"); - - private DatabaseHelper mOpenHelper; + @Thunk LauncherProviderChangeListener mListener; + @Thunk DatabaseHelper mOpenHelper; @Override public boolean onCreate() { @@ -99,6 +100,7 @@ public class LauncherProvider extends ContentProvider { public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) { mListener = listener; + mOpenHelper.mListener = mListener; } @Override @@ -126,7 +128,7 @@ public class LauncherProvider extends ContentProvider { return result; } - private static long dbInsertAndCheck(DatabaseHelper helper, + @Thunk static long dbInsertAndCheck(DatabaseHelper helper, SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) { if (values == null) { throw new RuntimeException("Error: attempting to insert null values"); @@ -144,7 +146,8 @@ public class LauncherProvider extends ContentProvider { // In very limited cases, we support system|signature permission apps to add to the db String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD); - if (externalAdd != null && "true".equals(externalAdd)) { + final boolean isExternalAll = externalAdd != null && "true".equals(externalAdd); + if (isExternalAll) { if (!mOpenHelper.initializeExternalAdd(initialValues)) { return null; } @@ -156,7 +159,14 @@ public class LauncherProvider extends ContentProvider { if (rowId < 0) return null; uri = ContentUris.withAppendedId(uri, rowId); - sendNotify(uri); + notifyListeners(); + + if (isExternalAll) { + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null) { + app.reloadWorkspace(); + } + } return uri; } @@ -181,7 +191,7 @@ public class LauncherProvider extends ContentProvider { db.endTransaction(); } - sendNotify(uri); + notifyListeners(); return values.length; } @@ -205,7 +215,7 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.delete(args.table, args.where, args.args); - if (count > 0) sendNotify(uri); + if (count > 0) notifyListeners(); return count; } @@ -217,17 +227,80 @@ public class LauncherProvider extends ContentProvider { addModifiedTime(values); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.update(args.table, values, args.where, args.args); - if (count > 0) sendNotify(uri); + if (count > 0) notifyListeners(); return count; } - private void sendNotify(Uri uri) { - String notify = uri.getQueryParameter(PARAMETER_NOTIFY); - if (notify == null || "true".equals(notify)) { - getContext().getContentResolver().notifyChange(uri, null); + @Override + public Bundle call(String method, String arg, Bundle extras) { + if (Binder.getCallingUid() != Process.myUid()) { + return null; + } + + switch (method) { + case LauncherSettings.Settings.METHOD_GET_BOOLEAN: { + Bundle result = new Bundle(); + result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, + getContext().getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE) + .getBoolean(arg, extras.getBoolean( + LauncherSettings.Settings.EXTRA_DEFAULT_VALUE))); + return result; + } + case LauncherSettings.Settings.METHOD_SET_BOOLEAN: { + boolean value = extras.getBoolean(LauncherSettings.Settings.EXTRA_VALUE); + getContext().getSharedPreferences( + LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE) + .edit().putBoolean(arg, value).apply(); + if (mListener != null) { + mListener.onSettingsChanged(arg, value); + } + Bundle result = new Bundle(); + result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value); + return result; + } + } + return null; + } + + /** + * Deletes any empty folder from the DB. + * @return Ids of deleted folders. + */ + public List<Long> deleteEmptyFolders() { + ArrayList<Long> folderIds = new ArrayList<Long>(); + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + // Select folders whose id do not match any container value. + String selection = LauncherSettings.Favorites.ITEM_TYPE + " = " + + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND " + + LauncherSettings.Favorites._ID + " NOT IN (SELECT " + + LauncherSettings.Favorites.CONTAINER + " FROM " + + TABLE_FAVORITES + ")"; + Cursor c = db.query(TABLE_FAVORITES, + new String[] {LauncherSettings.Favorites._ID}, + selection, null, null, null, null); + while (c.moveToNext()) { + folderIds.add(c.getLong(0)); + } + c.close(); + if (folderIds.size() > 0) { + db.delete(TABLE_FAVORITES, Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, folderIds), null); + } + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + folderIds.clear(); + } finally { + db.endTransaction(); } + return folderIds; + } + private void notifyListeners() { // always notify the backup agent LauncherBackupAgentHelper.dataChanged(getContext()); if (mListener != null) { @@ -235,7 +308,7 @@ public class LauncherProvider extends ContentProvider { } } - private static void addModifiedTime(ContentValues values) { + @Thunk static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -251,12 +324,6 @@ public class LauncherProvider extends ContentProvider { return mOpenHelper.generateNewScreenId(); } - // This is only required one time while loading the workspace during the - // upgrade path, and should never be called from anywhere else. - public void updateMaxScreenId(long maxScreenId) { - mOpenHelper.updateMaxScreenId(maxScreenId); - } - /** * Clears all the data for a fresh start. */ @@ -274,9 +341,10 @@ public class LauncherProvider extends ContentProvider { /** * Loads the default workspace based on the following priority scheme: - * 1) From a package provided by play store - * 2) From a partner configuration APK, already in the system image - * 3) The default configuration for the particular device + * 1) From the app restrictions + * 2) From a package provided by play store + * 3) From a partner configuration APK, already in the system image + * 4) The default configuration for the particular device */ synchronized public void loadDefaultFavoritesIfNecessary() { String spKey = LauncherAppState.getSharedPreferencesKey(); @@ -285,9 +353,11 @@ public class LauncherProvider extends ContentProvider { if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) { Log.d(TAG, "loading default workspace"); - AutoInstallsLayout loader = AutoInstallsLayout.get(getContext(), - mOpenHelper.mAppWidgetHost, mOpenHelper); - + AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(); + if (loader == null) { + loader = AutoInstallsLayout.get(getContext(), + mOpenHelper.mAppWidgetHost, mOpenHelper); + } if (loader == null) { final Partner partner = Partner.get(getContext().getPackageManager()); if (partner != null && partner.hasDefaultLayout()) { @@ -321,9 +391,43 @@ public class LauncherProvider extends ContentProvider { } } + /** + * Creates workspace loader from an XML resource listed in the app restrictions. + * + * @return the loader if the restrictions are set and the resource exists; null otherwise. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction() { + // UserManager.getApplicationRestrictions() requires minSdkVersion >= 18 + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + return null; + } + + Context ctx = getContext(); + UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE); + Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName()); + if (bundle == null) { + return null; + } + + String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME); + if (packageName != null) { + try { + Resources targetResources = ctx.getPackageManager() + .getResourcesForApplication(packageName); + return AutoInstallsLayout.get(ctx, packageName, targetResources, + mOpenHelper.mAppWidgetHost, mOpenHelper); + } catch (NameNotFoundException e) { + Log.e(TAG, "Target package for restricted profile not found", e); + return null; + } + } + return null; + } + private DefaultLayoutParser getDefaultLayoutParser() { int defaultLayout = LauncherAppState.getInstance() - .getDynamicGrid().getDeviceProfile().defaultLayoutId; + .getInvariantDeviceProfile().defaultLayoutId; return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost, mOpenHelper, getContext().getResources(), defaultLayout); } @@ -337,6 +441,11 @@ public class LauncherProvider extends ContentProvider { mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false); } + public void convertShortcutsToLauncherActivities() { + mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase()); + } + + public void deleteDatabase() { // Are you sure? (y/n) final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -346,16 +455,19 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase.deleteDatabase(dbFile); } mOpenHelper = new DatabaseHelper(getContext()); + mOpenHelper.mListener = mListener; } private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback { private final Context mContext; - private final AppWidgetHost mAppWidgetHost; + @Thunk final AppWidgetHost mAppWidgetHost; private long mMaxItemId = -1; private long mMaxScreenId = -1; private boolean mNewDbCreated = false; + @Thunk LauncherProviderChangeListener mListener; + DatabaseHelper(Context context) { super(context, LauncherFiles.LAUNCHER_DB, null, DATABASE_VERSION); mContext = context; @@ -375,17 +487,6 @@ public class LauncherProvider extends ContentProvider { return mNewDbCreated; } - /** - * Send notification that we've deleted the {@link AppWidgetHost}, - * probably as part of the initial database creation. The receiver may - * want to re-call {@link AppWidgetHost#startListening()} to ensure - * callbacks are correctly set. - */ - private void sendAppWidgetResetNotify() { - final ContentResolver resolver = mContext.getContentResolver(); - resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null); - } - @Override public void onCreate(SQLiteDatabase db) { if (LOGD) Log.d(TAG, "creating new launcher database"); @@ -421,19 +522,38 @@ public class LauncherProvider extends ContentProvider { "modified INTEGER NOT NULL DEFAULT 0," + "restored INTEGER NOT NULL DEFAULT 0," + "profileId INTEGER DEFAULT " + userSerialNumber + "," + - "rank INTEGER NOT NULL DEFAULT 0" + + "rank INTEGER NOT NULL DEFAULT 0," + + "options INTEGER NOT NULL DEFAULT 0" + ");"); addWorkspacesTable(db); // Database was just created, so wipe any previous widgets if (mAppWidgetHost != null) { mAppWidgetHost.deleteHost(); - sendAppWidgetResetNotify(); + + /** + * Send notification that we've deleted the {@link AppWidgetHost}, + * probably as part of the initial database creation. The receiver may + * want to re-call {@link AppWidgetHost#startListening()} to ensure + * callbacks are correctly set. + */ + new MainThreadExecutor().execute(new Runnable() { + + @Override + public void run() { + if (mListener != null) { + mListener.onAppWidgetHostReset(); + } + } + }); } // Fresh and clean launcher DB. mMaxItemId = initializeMaxItemId(db); setFlagEmptyDbCreated(); + + // When a new DB is created, remove all previously stored managed profile information. + ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(), mContext); } private void addWorkspacesTable(SQLiteDatabase db) { @@ -478,142 +598,120 @@ public class LauncherProvider extends ContentProvider { private void setFlagJustLoadedOldDb() { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true); - editor.putBoolean(EMPTY_DATABASE_CREATED, false); - editor.commit(); + sp.edit().putBoolean(EMPTY_DATABASE_CREATED, false).commit(); } private void setFlagEmptyDbCreated() { String spKey = LauncherAppState.getSharedPreferencesKey(); SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sp.edit(); - editor.putBoolean(EMPTY_DATABASE_CREATED, true); - editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false); - editor.commit(); + sp.edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion); - - int version = oldVersion; - if (version < MIN_DATABASE_VERSION) { - // The version cannot be lower that this, as Launcher3 never supported a lower + switch (oldVersion) { + // The version cannot be lower that 12, as Launcher3 never supported a lower // version of the DB. - createEmptyDB(db); - version = DATABASE_VERSION; - } - - if (version < 13) { - // With the new shrink-wrapped and re-orderable workspaces, it makes sense - // to persist workspace screens and their relative order. - mMaxScreenId = 0; - - addWorkspacesTable(db); - version = 13; - } - - if (version < 14) { - db.beginTransaction(); - try { - // Insert new column for holding widget provider name - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN appWidgetProvider TEXT;"); - db.setTransactionSuccessful(); - version = 14; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 12: { + // With the new shrink-wrapped and re-orderable workspaces, it makes sense + // to persist workspace screens and their relative order. + mMaxScreenId = 0; + addWorkspacesTable(db); } - } - - if (version < 15) { - db.beginTransaction(); - try { - // Insert new column for holding update timestamp - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.execSQL("ALTER TABLE workspaceScreens " + - "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - version = 15; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 13: { + db.beginTransaction(); + try { + // Insert new column for holding widget provider name + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN appWidgetProvider TEXT;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - } - - - if (version < 16) { - db.beginTransaction(); - try { - // Insert new column for holding restore status - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;"); - db.setTransactionSuccessful(); - version = 16; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + case 14: { + db.beginTransaction(); + try { + // Insert new column for holding update timestamp + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); + db.execSQL("ALTER TABLE workspaceScreens " + + "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;"); + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.e(TAG, ex.getMessage(), ex); + // Old version remains, which means we wipe old data + break; + } finally { + db.endTransaction(); + } } - } - - if (version < 17) { - // We use the db version upgrade here to identify users who may not have seen - // clings yet (because they weren't available), but for whom the clings are now - // available (tablet users). Because one of the possible cling flows (migration) - // is very destructive (wipes out workspaces), we want to prevent this from showing - // until clear data. We do so by marking that the clings have been shown. - LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext); - version = 17; - } - - if (version < 18) { - // No-op - version = 18; - } - - if (version < 19) { - // Due to a data loss bug, some users may have items associated with screen ids - // which no longer exist. Since this can cause other problems, and since the user - // will never see these items anyway, we use database upgrade as an opportunity to - // clean things up. - removeOrphanedItems(db); - version = 19; - } - - if (version < 20) { - // Add userId column - if (addProfileColumn(db)) { - version = 20; + case 15: { + if (!addIntegerColumn(db, Favorites.RESTORED, 0)) { + // Old version remains, which means we wipe old data + break; + } } - // else old version remains, which means we wipe old data - } - - if (version < 21) { - if (updateFolderItemsRank(db, true)) { - version = 21; + case 16: { + // We use the db version upgrade here to identify users who may not have seen + // clings yet (because they weren't available), but for whom the clings are now + // available (tablet users). Because one of the possible cling flows (migration) + // is very destructive (wipes out workspaces), we want to prevent this from showing + // until clear data. We do so by marking that the clings have been shown. + LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext); } - } - - if (version == 21) { - // Recreate workspace table with screen id a primary key - if (recreateWorkspaceTable(db)) { - version = 22; + case 17: { + // No-op + } + case 18: { + // Due to a data loss bug, some users may have items associated with screen ids + // which no longer exist. Since this can cause other problems, and since the user + // will never see these items anyway, we use database upgrade as an opportunity to + // clean things up. + removeOrphanedItems(db); + } + case 19: { + // Add userId column + if (!addProfileColumn(db)) { + // Old version remains, which means we wipe old data + break; + } + } + case 20: + if (!updateFolderItemsRank(db, true)) { + break; + } + case 21: + // Recreate workspace table with screen id a primary key + if (!recreateWorkspaceTable(db)) { + break; + } + case 22: { + if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) { + // Old version remains, which means we wipe old data + break; + } + } + case 23: + // No-op + case 24: + ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext); + case 25: + convertShortcutsToLauncherActivities(db); + case 26: { + // DB Upgraded successfully + return; } } - if (version != DATABASE_VERSION) { - Log.w(TAG, "Destroying all old data."); - createEmptyDB(db); - } + // DB was not upgraded + Log.w(TAG, "Destroying all old data."); + createEmptyDB(db); } @Override @@ -624,7 +722,6 @@ public class LauncherProvider extends ContentProvider { createEmptyDB(db); } - /** * Clears all the data for a fresh start. */ @@ -635,6 +732,63 @@ public class LauncherProvider extends ContentProvider { } /** + * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid + * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}. + */ + @Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) { + db.beginTransaction(); + Cursor c = null; + SQLiteStatement updateStmt = null; + + try { + // Only consider the primary user as other users can't have a shortcut. + long userSerial = UserManagerCompat.getInstance(mContext) + .getSerialNumberForUser(UserHandleCompat.myUserHandle()); + c = db.query(TABLE_FAVORITES, new String[] { + Favorites._ID, + Favorites.INTENT, + }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial, + null, null, null, null); + + updateStmt = db.compileStatement("UPDATE favorites SET itemType=" + + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?"); + + final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); + final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT); + + while (c.moveToNext()) { + String intentDescription = c.getString(intentIndex); + Intent intent; + try { + intent = Intent.parseUri(intentDescription, 0); + } catch (URISyntaxException e) { + Log.e(TAG, "Unable to parse intent", e); + continue; + } + + if (!Utilities.isLauncherAppTarget(intent)) { + continue; + } + + long id = c.getLong(idIndex); + updateStmt.bindLong(1, id); + updateStmt.executeUpdateDelete(); + } + db.setTransactionSuccessful(); + } catch (SQLException ex) { + Log.w(TAG, "Error deduping shortcuts", ex); + } finally { + db.endTransaction(); + if (c != null) { + c.close(); + } + if (updateStmt != null) { + updateStmt.close(); + } + } + } + + /** * Recreates workspace table and migrates data to the new table. */ public boolean recreateWorkspaceTable(SQLiteDatabase db) { @@ -682,7 +836,7 @@ public class LauncherProvider extends ContentProvider { return true; } - private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { + @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { db.beginTransaction(); try { if (addRankColumn) { @@ -715,20 +869,21 @@ public class LauncherProvider extends ContentProvider { } private boolean addProfileColumn(SQLiteDatabase db) { + UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); + // Default to the serial number of this user, for older + // shortcuts. + long userSerialNumber = userManager.getSerialNumberForUser( + UserHandleCompat.myUserHandle()); + return addIntegerColumn(db, Favorites.PROFILE_ID, userSerialNumber); + } + + private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) { db.beginTransaction(); try { - UserManagerCompat userManager = UserManagerCompat.getInstance(mContext); - // Default to the serial number of this user, for older - // shortcuts. - long userSerialNumber = userManager.getSerialNumberForUser( - UserHandleCompat.myUserHandle()); - // Insert new column for holding user serial number - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN profileId INTEGER DEFAULT " - + userSerialNumber + ";"); + db.execSQL("ALTER TABLE favorites ADD COLUMN " + + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";"); db.setTransactionSuccessful(); } catch (SQLException ex) { - // Old version remains, which means we wipe old data Log.e(TAG, ex.getMessage(), ex); return false; } finally { @@ -770,23 +925,7 @@ public class LauncherProvider extends ContentProvider { } private long initializeMaxItemId(SQLiteDatabase db) { - Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null); - - // get the result - final int maxIdIndex = 0; - long id = -1; - if (c != null && c.moveToNext()) { - id = c.getLong(maxIdIndex); - } - if (c != null) { - c.close(); - } - - if (id == -1) { - throw new RuntimeException("Error: could not query max item id"); - } - - return id; + return getMaxId(db, TABLE_FAVORITES); } // Generates a new ID to use for an workspace screen in your database. This method @@ -799,40 +938,14 @@ public class LauncherProvider extends ContentProvider { throw new RuntimeException("Error: max screen id was not initialized"); } mMaxScreenId += 1; - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true); return mMaxScreenId; } - public void updateMaxScreenId(long maxScreenId) { - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true); - mMaxScreenId = maxScreenId; - } - private long initializeMaxScreenId(SQLiteDatabase db) { - Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null); - - // get the result - final int maxIdIndex = 0; - long id = -1; - if (c != null && c.moveToNext()) { - id = c.getLong(maxIdIndex); - } - if (c != null) { - c.close(); - } - - if (id == -1) { - throw new RuntimeException("Error: could not query max screen id"); - } - - // Log to disk - Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true); - return id; + return getMaxId(db, TABLE_WORKSPACE_SCREENS); } - private boolean initializeExternalAdd(ContentValues values) { + @Thunk boolean initializeExternalAdd(ContentValues values) { // 1. Ensure that externally added items have a valid item id long id = generateNewItemId(); values.put(LauncherSettings.Favorites._ID, id); @@ -919,7 +1032,7 @@ public class LauncherProvider extends ContentProvider { return rank; } - private int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) { + @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) { ArrayList<Long> screenIds = new ArrayList<Long>(); // TODO: Use multiple loaders with fall-back and transaction. int count = loader.loadLayout(db, screenIds); @@ -946,7 +1059,7 @@ public class LauncherProvider extends ContentProvider { return count; } - private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) { + @Thunk void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) { final ContentResolver resolver = mContext.getContentResolver(); Cursor c = null; int count = 0; @@ -997,10 +1110,10 @@ public class LauncherProvider extends ContentProvider { int curY = 0; final LauncherAppState app = LauncherAppState.getInstance(); - final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - final int width = (int) grid.numColumns; - final int height = (int) grid.numRows; - final int hotseatWidth = (int) grid.numHotseatIcons; + final InvariantDeviceProfile profile = app.getInvariantDeviceProfile(); + final int width = (int) profile.numColumns; + final int height = (int) profile.numRows; + final int hotseatWidth = (int) profile.numHotseatIcons; final HashSet<String> seenIntents = new HashSet<String>(c.getCount()); @@ -1142,7 +1255,7 @@ public class LauncherProvider extends ContentProvider { int hotseatX = hotseat.keyAt(idx); ContentValues values = hotseat.valueAt(idx); - if (hotseatX == grid.hotseatAllAppsRank) { + if (hotseatX == profile.hotseatAllAppsRank) { // let's drop this in the next available hole in the hotseat while (++hotseatX < hotseatWidth) { if (hotseat.get(hotseatX) == null) { @@ -1242,6 +1355,27 @@ public class LauncherProvider extends ContentProvider { } } + /** + * @return the max _id in the provided table. + */ + @Thunk static long getMaxId(SQLiteDatabase db, String table) { + Cursor c = db.rawQuery("SELECT MAX(_id) FROM " + table, null); + // get the result + long id = -1; + if (c != null && c.moveToNext()) { + id = c.getLong(0); + } + if (c != null) { + c.close(); + } + + if (id == -1) { + throw new RuntimeException("Error: could not query max id in " + table); + } + + return id; + } + static class SqlArguments { public final String table; public final String where; |