diff options
Diffstat (limited to 'src/com/android/launcher3/LauncherProvider.java')
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 1162 |
1 files changed, 450 insertions, 712 deletions
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 1715b02bf..8791e9e57 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -16,9 +16,9 @@ package com.android.launcher3; +import android.annotation.TargetApi; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentProvider; import android.content.ContentProviderOperation; @@ -30,6 +30,7 @@ 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; @@ -37,10 +38,13 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteStatement; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; -import android.provider.Settings; +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; @@ -50,52 +54,39 @@ 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 String TAG = "LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 20; + private static final int DATABASE_VERSION = 26; - static final String OLD_AUTHORITY = "com.android.launcher2.settings"; - static final String AUTHORITY = ProviderConfig.AUTHORITY; + public static final String AUTHORITY = ProviderConfig.AUTHORITY; - // Should we attempt to load anything from the com.android.launcher2 provider? - static final boolean IMPORT_LAUNCHER2_DATABASE = false; + 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"; - 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 EMPTY_DATABASE_CREATED = - "EMPTY_DATABASE_CREATED"; + private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name"; - private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; - - private LauncherProviderChangeListener mListener; - - /** - * {@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; - private static boolean sJustLoadedFromOldDb; + @Thunk LauncherProviderChangeListener mListener; + @Thunk DatabaseHelper mOpenHelper; @Override public boolean onCreate() { final Context context = getContext(); + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); mOpenHelper = new DatabaseHelper(context); + StrictMode.setThreadPolicy(oldPolicy); LauncherAppState.setLauncherProvider(this); return true; } @@ -106,6 +97,7 @@ public class LauncherProvider extends ContentProvider { public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) { mListener = listener; + mOpenHelper.mListener = mListener; } @Override @@ -133,7 +125,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"); @@ -145,13 +137,21 @@ public class LauncherProvider extends ContentProvider { return db.insert(table, nullColumnHack, values); } + private void reloadLauncherIfExternal() { + if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) { + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null) { + app.reloadWorkspace(); + } + } + } + @Override public Uri insert(Uri uri, ContentValues initialValues) { SqlArguments args = new SqlArguments(uri); - // 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)) { + // In very limited cases, we support system|signature permission apps to modify the db. + if (Binder.getCallingPid() != Process.myPid()) { if (!mOpenHelper.initializeExternalAdd(initialValues)) { return null; } @@ -160,11 +160,25 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); addModifiedTime(initialValues); final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues); - if (rowId <= 0) return null; + if (rowId < 0) return null; uri = ContentUris.withAppendedId(uri, rowId); - sendNotify(uri); + notifyListeners(); + if (Utilities.ATLEAST_MARSHMALLOW) { + reloadLauncherIfExternal(); + } else { + // Deprecated behavior to support legacy devices which rely on provider callbacks. + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) { + app.reloadWorkspace(); + } + + String notify = uri.getQueryParameter("notify"); + if (notify == null || "true".equals(notify)) { + getContext().getContentResolver().notifyChange(uri, null); + } + } return uri; } @@ -188,7 +202,8 @@ public class LauncherProvider extends ContentProvider { db.endTransaction(); } - sendNotify(uri); + notifyListeners(); + reloadLauncherIfExternal(); return values.length; } @@ -200,6 +215,7 @@ public class LauncherProvider extends ContentProvider { try { ContentProviderResult[] result = super.applyBatch(operations); db.setTransactionSuccessful(); + reloadLauncherIfExternal(); return result; } finally { db.endTransaction(); @@ -212,8 +228,9 @@ 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(); + reloadLauncherIfExternal(); return count; } @@ -224,17 +241,81 @@ 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(); + reloadLauncherIfExternal(); 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) { @@ -242,7 +323,7 @@ public class LauncherProvider extends ContentProvider { } } - private void addModifiedTime(ContentValues values) { + @Thunk static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis()); } @@ -258,32 +339,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); - } - - /** - * @param Should we load the old db for upgrade? first run only. - */ - synchronized public boolean justLoadedOldDb() { - String spKey = LauncherAppState.getSharedPreferencesKey(); - SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE); - - boolean loadedOldDb = false || sJustLoadedFromOldDb; - - sJustLoadedFromOldDb = false; - if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) { - - SharedPreferences.Editor editor = sp.edit(); - editor.remove(UPGRADED_FROM_OLD_DATABASE); - editor.commit(); - loadedOldDb = true; - } - return loadedOldDb; - } - /** * Clears all the data for a fresh start. */ @@ -301,9 +356,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(); @@ -312,9 +368,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()) { @@ -332,6 +390,10 @@ public class LauncherProvider extends ContentProvider { if (loader == null) { loader = getDefaultLayoutParser(); } + + // There might be some partially restored DB items, due to buggy restore logic in + // previous versions of launcher. + createEmptyDB(); // Populate favorites table with initial favorites if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0) && usingExternallyProvidedLayout) { @@ -344,9 +406,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 (!Utilities.ATLEAST_JB_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); } @@ -356,17 +452,15 @@ public class LauncherProvider extends ContentProvider { Uri.parse(getContext().getString(R.string.old_launcher_provider_uri))); } - private static interface ContentValuesCallback { - public void onRow(ContentValues values); + public void updateFolderItemsRank() { + mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false); } - private static boolean shouldImportLauncher2Database(Context context) { - boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet); - - // We don't import the old databse for tablets, as the grid size has changed. - return !isTablet && IMPORT_LAUNCHER2_DATABASE; + public void convertShortcutsToLauncherActivities() { + mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase()); } + public void deleteDatabase() { // Are you sure? (y/n) final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -376,16 +470,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; @@ -405,17 +502,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"); @@ -450,53 +536,44 @@ public class LauncherProvider extends ContentProvider { "appWidgetProvider TEXT," + "modified INTEGER NOT NULL DEFAULT 0," + "restored INTEGER NOT NULL DEFAULT 0," + - "profileId INTEGER DEFAULT " + userSerialNumber + + "profileId INTEGER DEFAULT " + userSerialNumber + "," + + "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(); - } - if (shouldImportLauncher2Database(mContext)) { - // Try converting the old database - ContentValuesCallback permuteScreensCb = new ContentValuesCallback() { - public void onRow(ContentValues values) { - int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER); - if (container == Favorites.CONTAINER_DESKTOP) { - int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN); - screen = (int) upgradeLauncherDb_permuteScreens(screen); - values.put(LauncherSettings.Favorites.SCREEN, screen); + /** + * 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(); } } - }; - Uri uri = Uri.parse("content://" + Settings.AUTHORITY + - "/old_favorites?notify=true"); - if (!convertDatabase(db, uri, permuteScreensCb, true)) { - // Try and upgrade from the Launcher2 db - uri = Uri.parse(mContext.getString(R.string.old_launcher_provider_uri)); - if (!convertDatabase(db, uri, permuteScreensCb, false)) { - // If we fail, then set a flag to load the default workspace - setFlagEmptyDbCreated(); - return; - } - } - // Right now, in non-default workspace cases, we want to run the final - // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so - // set that flag too. - setFlagJustLoadedOldDb(); - } else { - // Fresh and clean launcher DB. - mMaxItemId = initializeMaxItemId(db); - setFlagEmptyDbCreated(); + }); } + + // 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) { db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" + - LauncherSettings.WorkspaceScreens._ID + " INTEGER," + + LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," + LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," + LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" + ");"); @@ -536,333 +613,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(); - } - - // We rearrange the screens from the old launcher - // 12345 -> 34512 - private long upgradeLauncherDb_permuteScreens(long screen) { - if (screen >= 2) { - return screen - 2; - } else { - return screen + 3; - } - } - - private boolean convertDatabase(SQLiteDatabase db, Uri uri, - ContentValuesCallback cb, boolean deleteRows) { - if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade"); - boolean converted = false; - - final ContentResolver resolver = mContext.getContentResolver(); - Cursor cursor = null; - - try { - cursor = resolver.query(uri, null, null, null, null); - } catch (Exception e) { - // Ignore - } - - // We already have a favorites database in the old provider - if (cursor != null) { - try { - if (cursor.getCount() > 0) { - converted = copyFromCursor(db, cursor, cb) > 0; - if (converted && deleteRows) { - resolver.delete(uri, null, null); - } - } - } finally { - cursor.close(); - } - } - - if (converted) { - // Convert widgets from this import into widgets - if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade"); - convertWidgets(db); - - // Update max item id - mMaxItemId = initializeMaxItemId(db); - if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId); - } - - return converted; - } - - private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) { - final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); - final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); - final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); - final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE); - final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); - final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); - final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); - final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); - final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); - final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); - final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); - final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); - final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); - final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); - - ContentValues[] rows = new ContentValues[c.getCount()]; - int i = 0; - while (c.moveToNext()) { - ContentValues values = new ContentValues(c.getColumnCount()); - values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex)); - values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex)); - values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex)); - values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex)); - values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex)); - values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex)); - values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex)); - values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex)); - values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex)); - values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1); - values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex)); - values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex)); - values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex)); - values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex)); - values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex)); - if (cb != null) { - cb.onRow(values); - } - rows[i++] = values; - } - - int total = 0; - if (i > 0) { - db.beginTransaction(); - try { - int numValues = rows.length; - for (i = 0; i < numValues; i++) { - if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) { - return 0; - } else { - total++; - } - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - return total; + 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 < 3) { - // upgrade 1,2 -> 3 added appWidgetId column - db.beginTransaction(); - try { - // Insert new column for holding appWidgetIds - db.execSQL("ALTER TABLE favorites " + - "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;"); - db.setTransactionSuccessful(); - version = 3; - } catch (SQLException ex) { - // Old version remains, which means we wipe old data - Log.e(TAG, ex.getMessage(), ex); - } finally { - db.endTransaction(); + switch (oldVersion) { + // The version cannot be lower that 12, as Launcher3 never supported a lower + // version of the DB. + 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); } - - // Convert existing widgets only if table upgrade was successful - if (version == 3) { - convertWidgets(db); + 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 < 4) { - version = 4; - } - - // Where's version 5? - // - Donut and sholes on 2.0 shipped with version 4 of launcher1. - // - Passion shipped on 2.1 with version 6 of launcher3 - // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1 - // but version 5 on there was the updateContactsShortcuts change - // which was version 6 in launcher 2 (first shipped on passion 2.1r1). - // The updateContactsShortcuts change is idempotent, so running it twice - // is okay so we'll do that when upgrading the devices that shipped with it. - if (version < 6) { - // We went from 3 to 5 screens. Move everything 1 to the right - db.beginTransaction(); - try { - db.execSQL("UPDATE favorites SET screen=(screen + 1);"); - db.setTransactionSuccessful(); - } 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(); + } } - - // We added the fast track. - if (updateContactsShortcuts(db)) { - version = 6; + case 15: { + if (!addIntegerColumn(db, Favorites.RESTORED, 0)) { + // Old version remains, which means we wipe old data + break; + } } - } - - if (version < 7) { - // Version 7 gets rid of the special search widget. - convertWidgets(db); - version = 7; - } - - if (version < 8) { - // Version 8 (froyo) has the icons all normalized. This should - // already be the case in practice, but we now rely on it and don't - // resample the images each time. - normalizeIcons(db); - version = 8; - } - - if (version < 9) { - // The max id is not yet set at this point (onUpgrade is triggered in the ctor - // before it gets a change to get set, so we need to read it here when we use it) - if (mMaxItemId == -1) { - mMaxItemId = initializeMaxItemId(db); + 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); } - - // Add default hotseat icons - loadFavorites(db, new DefaultLayoutParser(mContext, mAppWidgetHost, this, - mContext.getResources(), R.xml.update_workspace)); - version = 9; - } - - // We bumped the version three time during JB, once to update the launch flags, once to - // update the override for the default launch animation and once to set the mimetype - // to improve startup performance - if (version < 12) { - // Contact shortcuts need a different set of flags to be launched now - // The updateContactsShortcuts change is idempotent, so we can keep using it like - // back in the Donut days - updateContactsShortcuts(db); - version = 12; - } - - 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; - - // This will never happen in the wild, but when we switch to using workspace - // screen ids, redo the import from old launcher. - sJustLoadedFromOldDb = true; - - 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 17: { + // No-op } - } - - 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 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); } - } - - - 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 19: { + // Add userId column + if (!addProfileColumn(db)) { + // Old version remains, which means we wipe old data + break; + } } - } - - 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 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; } - // else old version remains, which means we wipe old data } - if (version != DATABASE_VERSION) { - Log.w(TAG, "Destroying all old data."); - db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); - db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS); - - onCreate(db); - } + // DB was not upgraded + Log.w(TAG, "Destroying all old data."); + createEmptyDB(db); } @Override @@ -873,7 +737,6 @@ public class LauncherProvider extends ContentProvider { createEmptyDB(db); } - /** * Clears all the data for a fresh start. */ @@ -883,20 +746,102 @@ public class LauncherProvider extends ContentProvider { onCreate(db); } - private boolean addProfileColumn(SQLiteDatabase db) { + /** + * 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 { - 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 + ";"); + // 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) { + db.beginTransaction(); + try { + Cursor c = db.query(TABLE_WORKSPACE_SCREENS, + new String[] {LauncherSettings.WorkspaceScreens._ID}, + null, null, null, null, + LauncherSettings.WorkspaceScreens.SCREEN_RANK); + ArrayList<Long> sortedIDs = new ArrayList<Long>(); + long maxId = 0; + try { + while (c.moveToNext()) { + Long id = c.getLong(0); + if (!sortedIDs.contains(id)) { + sortedIDs.add(id); + maxId = Math.max(maxId, id); + } + } + } finally { + c.close(); + } + + db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS); + addWorkspacesTable(db); + + // Add all screen ids back + int total = sortedIDs.size(); + for (int i = 0; i < total; i++) { + ContentValues values = new ContentValues(); + values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i)); + values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i); + addModifiedTime(values); + db.insertOrThrow(TABLE_WORKSPACE_SCREENS, null, values); + } + db.setTransactionSuccessful(); + mMaxScreenId = maxId; + } catch (SQLException ex) { // Old version remains, which means we wipe old data Log.e(TAG, ex.getMessage(), ex); return false; @@ -906,140 +851,60 @@ public class LauncherProvider extends ContentProvider { return true; } - private boolean updateContactsShortcuts(SQLiteDatabase db) { - final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, - new int[] { Favorites.ITEM_TYPE_SHORTCUT }); - - Cursor c = null; - final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT"; + @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) { db.beginTransaction(); try { - // Select and iterate through each matching widget - c = db.query(TABLE_FAVORITES, - new String[] { Favorites._ID, Favorites.INTENT }, - selectWhere, null, null, null, null); - if (c == null) return false; - - if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount()); + if (addRankColumn) { + // Insert new column for holding rank + db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;"); + } - final int idIndex = c.getColumnIndex(Favorites._ID); - final int intentIndex = c.getColumnIndex(Favorites.INTENT); + // Get a map for folder ID to folder width + Cursor c = db.rawQuery("SELECT container, MAX(cellX) FROM favorites" + + " WHERE container IN (SELECT _id FROM favorites WHERE itemType = ?)" + + " GROUP BY container;", + new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}); while (c.moveToNext()) { - long favoriteId = c.getLong(idIndex); - final String intentUri = c.getString(intentIndex); - if (intentUri != null) { - try { - final Intent intent = Intent.parseUri(intentUri, 0); - android.util.Log.d("Home", intent.toString()); - final Uri uri = intent.getData(); - if (uri != null) { - final String data = uri.toString(); - if ((Intent.ACTION_VIEW.equals(intent.getAction()) || - actionQuickContact.equals(intent.getAction())) && - (data.startsWith("content://contacts/people/") || - data.startsWith("content://com.android.contacts/" + - "contacts/lookup/"))) { - - final Intent newIntent = new Intent(actionQuickContact); - // When starting from the launcher, start in a new, cleared task - // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we - // clear the whole thing preemptively here since - // QuickContactActivity will finish itself when launching other - // detail activities. - newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_CLEAR_TASK); - newIntent.putExtra( - Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true); - newIntent.setData(uri); - // Determine the type and also put that in the shortcut - // (that can speed up launch a bit) - newIntent.setDataAndType(uri, newIntent.resolveType(mContext)); - - final ContentValues values = new ContentValues(); - values.put(LauncherSettings.Favorites.INTENT, - newIntent.toUri(0)); - - String updateWhere = Favorites._ID + "=" + favoriteId; - db.update(TABLE_FAVORITES, values, updateWhere, null); - } - } - } catch (RuntimeException ex) { - Log.e(TAG, "Problem upgrading shortcut", ex); - } catch (URISyntaxException e) { - Log.e(TAG, "Problem upgrading shortcut", e); - } - } + db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE " + + "container=? AND cellX IS NOT NULL AND cellY IS NOT NULL;", + new Object[] {c.getLong(1) + 1, c.getLong(0)}); } + c.close(); db.setTransactionSuccessful(); } catch (SQLException ex) { - Log.w(TAG, "Problem while upgrading contacts", ex); + // Old version remains, which means we wipe old data + Log.e(TAG, ex.getMessage(), ex); return false; } finally { db.endTransaction(); - if (c != null) { - c.close(); - } } - return true; } - private void normalizeIcons(SQLiteDatabase db) { - Log.d(TAG, "normalizing icons"); + 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(); - Cursor c = null; - SQLiteStatement update = null; try { - boolean logged = false; - update = db.compileStatement("UPDATE favorites " - + "SET icon=? WHERE _id=?"); - - c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" + - Favorites.ICON_TYPE_BITMAP, null); - - final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); - final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON); - - while (c.moveToNext()) { - long id = c.getLong(idIndex); - byte[] data = c.getBlob(iconIndex); - try { - Bitmap bitmap = Utilities.createIconBitmap( - BitmapFactory.decodeByteArray(data, 0, data.length), - mContext); - if (bitmap != null) { - update.bindLong(1, id); - data = ItemInfo.flattenBitmap(bitmap); - if (data != null) { - update.bindBlob(2, data); - update.execute(); - } - bitmap.recycle(); - } - } catch (Exception e) { - if (!logged) { - Log.e(TAG, "Failed normalizing icon " + id, e); - } else { - Log.e(TAG, "Also failed normalizing icon " + id); - } - logged = true; - } - } + db.execSQL("ALTER TABLE favorites ADD COLUMN " + + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";"); db.setTransactionSuccessful(); } catch (SQLException ex) { - Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); + Log.e(TAG, ex.getMessage(), ex); + return false; } finally { db.endTransaction(); - if (update != null) { - update.close(); - } - if (c != null) { - c.close(); - } } + return true; } // Generates a new ID to use for an object in your database. This method should be only @@ -1075,23 +940,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 @@ -1104,128 +953,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; - } - - /** - * Upgrade existing clock and photo frame widgets into their new widget - * equivalents. - */ - private void convertWidgets(SQLiteDatabase db) { - final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); - final int[] bindSources = new int[] { - Favorites.ITEM_TYPE_WIDGET_CLOCK, - Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME, - Favorites.ITEM_TYPE_WIDGET_SEARCH, - }; - - final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources); - - Cursor c = null; - - db.beginTransaction(); - try { - // Select and iterate through each matching widget - c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE }, - selectWhere, null, null, null, null); - - if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount()); - - final ContentValues values = new ContentValues(); - while (c != null && c.moveToNext()) { - long favoriteId = c.getLong(0); - int favoriteType = c.getInt(1); - - // Allocate and update database with new appWidgetId - try { - int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); - - if (LOGD) { - Log.d(TAG, "allocated appWidgetId=" + appWidgetId - + " for favoriteId=" + favoriteId); - } - values.clear(); - values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET); - values.put(Favorites.APPWIDGET_ID, appWidgetId); - - // Original widgets might not have valid spans when upgrading - if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) { - values.put(LauncherSettings.Favorites.SPANX, 4); - values.put(LauncherSettings.Favorites.SPANY, 1); - } else { - values.put(LauncherSettings.Favorites.SPANX, 2); - values.put(LauncherSettings.Favorites.SPANY, 2); - } - - String updateWhere = Favorites._ID + "=" + favoriteId; - db.update(TABLE_FAVORITES, values, updateWhere, null); - - if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) { - // TODO: check return value - appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - new ComponentName("com.android.alarmclock", - "com.android.alarmclock.AnalogAppWidgetProvider")); - } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) { - // TODO: check return value - appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - new ComponentName("com.android.camera", - "com.android.camera.PhotoAppWidgetProvider")); - } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) { - // TODO: check return value - appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, - getSearchWidgetProvider()); - } - } catch (RuntimeException ex) { - Log.e(TAG, "Problem allocating appWidgetId", ex); - } - } - - db.setTransactionSuccessful(); - } catch (SQLException ex) { - Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); - } finally { - db.endTransaction(); - if (c != null) { - c.close(); - } - } - - // Update max item id - mMaxItemId = initializeMaxItemId(db); - if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId); + 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); @@ -1312,7 +1047,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); @@ -1339,12 +1074,7 @@ public class LauncherProvider extends ContentProvider { return count; } - private ComponentName getSearchWidgetProvider() { - AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(mContext); - return (searchProvider == null) ? null : searchProvider.provider; - } - - 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; @@ -1395,10 +1125,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()); @@ -1540,7 +1270,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) { @@ -1619,6 +1349,8 @@ public class LauncherProvider extends ContentProvider { } finally { db.endTransaction(); } + + updateFolderItemsRank(db, false); } } finally { c.close(); @@ -1639,18 +1371,24 @@ public class LauncherProvider extends ContentProvider { } /** - * Build a query string that will match any row where the column matches - * anything in the values list. + * @return the max _id in the provided table. */ - private static String buildOrWhereString(String column, int[] values) { - StringBuilder selectWhere = new StringBuilder(); - for (int i = values.length - 1; i >= 0; i--) { - selectWhere.append(column).append("=").append(values[i]); - if (i > 0) { - selectWhere.append(" OR "); - } + @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(); } - return selectWhere.toString(); + + if (id == -1) { + throw new RuntimeException("Error: could not query max id in " + table); + } + + return id; } static class SqlArguments { |