From 8b51b173209a9627157172f22fa23fa36065b061 Mon Sep 17 00:00:00 2001 From: Michael Jurka Date: Mon, 6 Jan 2014 14:33:50 -0800 Subject: [PATCH 01/24] Move saved wallpaper images from cache directory to data directory Bug: 12028275 Change-Id: Ia8fdd0ea50a4dad34e560287c50ae3fcc6d09cf9 --- .../android/launcher3/SavedWallpaperImages.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java index 58add7022d..44bfdf1f9d 100644 --- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java +++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java @@ -85,6 +85,9 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { } public SavedWallpaperImages(Activity context) { + // We used to store the saved images in the cache directory, but that meant they'd get + // deleted sometimes-- move them to the data directory + ImageDb.moveFromCacheDirectoryIfNecessary(context); mDb = new ImageDb(context); mContext = context; mLayoutInflater = context.getLayoutInflater(); @@ -215,11 +218,20 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { Context mContext; public ImageDb(Context context) { - super(context, new File(context.getCacheDir(), DB_NAME).getPath(), null, DB_VERSION); + super(context, context.getDatabasePath(DB_NAME).getPath(), null, DB_VERSION); // Store the context for later use mContext = context; } + public static void moveFromCacheDirectoryIfNecessary(Context context) { + // We used to store the saved images in the cache directory, but that meant they'd get + // deleted sometimes-- move them to the data directory + File oldSavedImagesFile = new File(context.getCacheDir(), ImageDb.DB_NAME); + File savedImagesFile = context.getDatabasePath(ImageDb.DB_NAME); + if (oldSavedImagesFile.exists()) { + oldSavedImagesFile.renameTo(savedImagesFile); + } + } @Override public void onCreate(SQLiteDatabase database) { database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" + From 6e27f642ae66dd1920b25b527fced7268943d11a Mon Sep 17 00:00:00 2001 From: Michael Jurka Date: Tue, 10 Dec 2013 13:40:30 +0100 Subject: [PATCH 02/24] Recover when widget preview database is deleted Bug: 12109621 Change-Id: I8d59700d31d6856d6151b965786c87585801317b --- .../android/launcher3/LauncherAppState.java | 9 ++- .../launcher3/WidgetPreviewLoader.java | 67 +++++++++++++------ 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 84a1d04118..d27cedcfc7 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -83,7 +83,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { mIsScreenLarge = isScreenLarge(sContext.getResources()); mScreenDensity = sContext.getResources().getDisplayMetrics().density; - mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(sContext); + recreateWidgetPreviewDb(); mIconCache = new IconCache(sContext); mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class)); @@ -113,6 +113,13 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true, mFavoritesObserver); } + + public void recreateWidgetPreviewDb() { + if (mWidgetPreviewCacheDb != null) { + mWidgetPreviewCacheDb.close(); + } + mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(sContext); + } /** * Call from Application.onTerminate(), which is not guaranteed to ever be called. diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 7e1ad6d761..3db0b51ade 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -10,6 +10,7 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDiskIOException; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; @@ -164,6 +165,12 @@ public class WidgetPreviewLoader { editor.commit(); } } + + public void recreateDb() { + LauncherAppState app = LauncherAppState.getInstance(); + app.recreateWidgetPreviewDb(); + mDb = app.getWidgetPreviewCacheDb(); + } public void setPreviewSize(int previewWidth, int previewHeight, PagedViewCellLayout widgetSpacingLayout) { @@ -347,13 +354,20 @@ public class WidgetPreviewLoader { preview.compress(Bitmap.CompressFormat.PNG, 100, stream); values.put(CacheDb.COLUMN_PREVIEW_BITMAP, stream.toByteArray()); values.put(CacheDb.COLUMN_SIZE, mSize); - db.insert(CacheDb.TABLE_NAME, null, values); + try { + db.insert(CacheDb.TABLE_NAME, null, values); + } catch (SQLiteDiskIOException e) { + recreateDb(); + } } private void clearDb() { SQLiteDatabase db = mDb.getWritableDatabase(); // Delete everything - db.delete(CacheDb.TABLE_NAME, null, null); + try { + db.delete(CacheDb.TABLE_NAME, null, null); + } catch (SQLiteDiskIOException e) { + } } public static void removePackageFromDb(final CacheDb cacheDb, final String packageName) { @@ -363,13 +377,17 @@ public class WidgetPreviewLoader { new AsyncTask() { public Void doInBackground(Void ... args) { SQLiteDatabase db = cacheDb.getWritableDatabase(); - db.delete(CacheDb.TABLE_NAME, - CacheDb.COLUMN_NAME + " LIKE ? OR " + - CacheDb.COLUMN_NAME + " LIKE ?", // SELECT query - new String[] { - WIDGET_PREFIX + packageName + "/%", - SHORTCUT_PREFIX + packageName + "/%"} // args to SELECT query - ); + try { + db.delete(CacheDb.TABLE_NAME, + CacheDb.COLUMN_NAME + " LIKE ? OR " + + CacheDb.COLUMN_NAME + " LIKE ?", // SELECT query + new String[] { + WIDGET_PREFIX + packageName + "/%", + SHORTCUT_PREFIX + packageName + "/%" + } // args to SELECT query + ); + } catch (SQLiteDiskIOException e) { + } synchronized(sInvalidPackages) { sInvalidPackages.remove(packageName); } @@ -382,9 +400,12 @@ public class WidgetPreviewLoader { new AsyncTask() { public Void doInBackground(Void ... args) { SQLiteDatabase db = cacheDb.getWritableDatabase(); - db.delete(CacheDb.TABLE_NAME, - CacheDb.COLUMN_NAME + " = ? ", // SELECT query - new String[] { objectName }); // args to SELECT query + try { + db.delete(CacheDb.TABLE_NAME, + CacheDb.COLUMN_NAME + " = ? ", // SELECT query + new String[] { objectName }); // args to SELECT query + } catch (SQLiteDiskIOException e) { + } return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); @@ -396,14 +417,20 @@ public class WidgetPreviewLoader { CacheDb.COLUMN_SIZE + " = ?"; } SQLiteDatabase db = mDb.getReadableDatabase(); - Cursor result = db.query(CacheDb.TABLE_NAME, - new String[] { CacheDb.COLUMN_PREVIEW_BITMAP }, // cols to return - mCachedSelectQuery, // select query - new String[] { name, mSize }, // args to select query - null, - null, - null, - null); + Cursor result; + try { + result = db.query(CacheDb.TABLE_NAME, + new String[] { CacheDb.COLUMN_PREVIEW_BITMAP }, // cols to return + mCachedSelectQuery, // select query + new String[] { name, mSize }, // args to select query + null, + null, + null, + null); + } catch (SQLiteDiskIOException e) { + recreateDb(); + return null; + } if (result.getCount() > 0) { result.moveToFirst(); byte[] blob = result.getBlob(0); From 5743aa9a86b7f559d9beb09c223dad8e3913c5c6 Mon Sep 17 00:00:00 2001 From: Chris Wren Date: Fri, 10 Jan 2014 18:02:06 -0500 Subject: [PATCH 03/24] backup keys that might have slipped past seems a little paranoid, but can't hurt. Bug: 12455866 Change-Id: If9da4cc021ee4fac32c822a91fabda69bf3ff8aa --- src/com/android/launcher3/LauncherBackupHelper.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index a081c21911..4e70e6efdf 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -296,8 +296,9 @@ public class LauncherBackupHelper implements BackupHelper { final long updateTime = cursor.getLong(ID_MODIFIED); Key key = getKey(Key.FAVORITE, id); keys.add(key); - currentIds.add(keyToBackupKey(key)); - if (updateTime >= in.t) { + final String backupKey = keyToBackupKey(key); + currentIds.add(backupKey); + if (!savedIds.contains(backupKey) || updateTime >= in.t) { byte[] blob = packFavorite(cursor); writeRowToBackup(key, blob, out, data); } @@ -364,8 +365,9 @@ public class LauncherBackupHelper implements BackupHelper { final long updateTime = cursor.getLong(ID_MODIFIED); Key key = getKey(Key.SCREEN, id); keys.add(key); - currentIds.add(keyToBackupKey(key)); - if (updateTime >= in.t) { + final String backupKey = keyToBackupKey(key); + currentIds.add(backupKey); + if (!savedIds.contains(backupKey) || updateTime >= in.t) { byte[] blob = packScreen(cursor); writeRowToBackup(key, blob, out, data); } From 4b171361ca4ef2d88cd9493c6d92bb390df45289 Mon Sep 17 00:00:00 2001 From: Chris Wren Date: Mon, 13 Jan 2014 17:39:02 -0500 Subject: [PATCH 04/24] hide launcher restore behind a flag. enable with 'adb shell settings put secure launcher_restore_enabled 1' before signing into wiped device Bug: 12532845 Change-Id: I1471c39dac2e6e1412f7720b1ba8edf46273c593 --- .../launcher3/LauncherBackupAgentHelper.java | 15 +++++-- .../launcher3/LauncherBackupHelper.java | 5 ++- .../LauncherPreferencesBackupHelper.java | 39 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 src/com/android/launcher3/LauncherPreferencesBackupHelper.java diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 83e4a60d45..876cf08878 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -21,6 +21,7 @@ import android.app.backup.BackupManager; import android.app.backup.SharedPreferencesBackupHelper; import android.content.Context; import android.content.SharedPreferences; +import android.provider.Settings; public class LauncherBackupAgentHelper extends BackupAgentHelper { @@ -28,6 +29,8 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { private static BackupManager sBackupManager; + protected static final String SETTING_RESTORE_ENABLED = "launcher_restore_enabled"; + /** * Notify the backup manager that out database is dirty. * @@ -54,9 +57,15 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { @Override public void onCreate() { + + boolean restoreEnabled = 0 != Settings.Secure.getInt( + getContentResolver(), SETTING_RESTORE_ENABLED, 0); + addHelper(LauncherBackupHelper.LAUNCHER_PREFS_PREFIX, - new SharedPreferencesBackupHelper(this, - LauncherAppState.getSharedPreferencesKey())); - addHelper(LauncherBackupHelper.LAUNCHER_PREFIX, new LauncherBackupHelper(this)); + new LauncherPreferencesBackupHelper(this, + LauncherAppState.getSharedPreferencesKey(), + restoreEnabled)); + addHelper(LauncherBackupHelper.LAUNCHER_PREFIX, + new LauncherBackupHelper(this, restoreEnabled)); } } diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index a081c21911..704d790dae 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -137,12 +137,15 @@ public class LauncherBackupHelper implements BackupHelper { private final Context mContext; + private final boolean mRestoreEnabled; + private HashMap mWidgetMap; private ArrayList mKeys; - public LauncherBackupHelper(Context context) { + public LauncherBackupHelper(Context context, boolean restoreEnabled) { mContext = context; + mRestoreEnabled = restoreEnabled; } private void dataChanged() { diff --git a/src/com/android/launcher3/LauncherPreferencesBackupHelper.java b/src/com/android/launcher3/LauncherPreferencesBackupHelper.java new file mode 100644 index 0000000000..1ac6ff9628 --- /dev/null +++ b/src/com/android/launcher3/LauncherPreferencesBackupHelper.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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; + +import android.app.backup.BackupDataInputStream; +import android.app.backup.SharedPreferencesBackupHelper; +import android.content.Context; + +public class LauncherPreferencesBackupHelper extends SharedPreferencesBackupHelper { + + private final boolean mRestoreEnabled; + + public LauncherPreferencesBackupHelper(Context context, String sharedPreferencesKey, + boolean restoreEnabled) { + super(context, sharedPreferencesKey); + mRestoreEnabled = restoreEnabled; + } + + @Override + public void restoreEntity(BackupDataInputStream data) { + if (mRestoreEnabled) { + super.restoreEntity(data); + } + } +} From 4808aa6b42ff31aa792b6dc0601bd6f24a554e21 Mon Sep 17 00:00:00 2001 From: Michael Jurka Date: Tue, 14 Jan 2014 13:50:53 +0100 Subject: [PATCH 05/24] Add ability to center the crop (disabled) --- WallpaperPicker/res/values/config.xml | 3 +++ .../launcher3/WallpaperCropActivity.java | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/WallpaperPicker/res/values/config.xml b/WallpaperPicker/res/values/config.xml index 1b24190735..71580b5b07 100644 --- a/WallpaperPicker/res/values/config.xml +++ b/WallpaperPicker/res/values/config.xml @@ -15,4 +15,7 @@ --> false + + false diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index b3ef073091..ee7b819512 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -330,10 +330,10 @@ public class WallpaperCropActivity extends Activity { protected void cropImageAndSetWallpaper(Uri uri, OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { + boolean centerCrop = getResources().getBoolean(R.bool.center_crop); // Get the crop boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - Display d = getWindowManager().getDefaultDisplay(); Point displaySize = new Point(); @@ -358,15 +358,25 @@ public class WallpaperCropActivity extends Activity { // ADJUST CROP WIDTH // Extend the crop all the way to the right, for parallax // (or all the way to the left, in RTL) - float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; + float extraSpace; + if (centerCrop) { + extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left); + } else { + extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; + } // Cap the amount of extra width float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width(); extraSpace = Math.min(extraSpace, maxExtraSpace); - if (ltr) { - cropRect.right += extraSpace; + if (centerCrop) { + cropRect.left -= extraSpace / 2f; + cropRect.right += extraSpace / 2f; } else { - cropRect.left -= extraSpace; + if (ltr) { + cropRect.right += extraSpace; + } else { + cropRect.left -= extraSpace; + } } // ADJUST CROP HEIGHT From e26d09484575f9b1855e5bc42d4fa05a5342f054 Mon Sep 17 00:00:00 2001 From: Dan Sandler Date: Mon, 13 Jan 2014 14:30:14 -0500 Subject: [PATCH 06/24] Fix longpress crash. The AllApps button doesn't usually accept longpresses, but you can trick it into trying by holding one finger on it and another on another icon in the hotseat. This patch defends against that and bails out if the longpressed item has the all apps rank (position in hotseat). Bug: 11740833 Change-Id: I99785ccbc9e6dc6be2a9e56289b3cc0275fbb65c --- src/com/android/launcher3/Launcher.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 765fca4993..aadcd8799b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2781,7 +2781,8 @@ public class Launcher extends Activity // The hotseat touch handling does not go through Workspace, and we always allow long press // on hotseat items. final View itemUnderLongClick = longClickCellInfo.cell; - boolean allowLongPress = isHotseatLayout(v) || mWorkspace.allowLongPress(); + final boolean inHotseat = isHotseatLayout(v); + boolean allowLongPress = inHotseat || mWorkspace.allowLongPress(); if (allowLongPress && !mDragController.isDragging()) { if (itemUnderLongClick == null) { // User long pressed on empty space @@ -2794,7 +2795,11 @@ public class Launcher extends Activity mWorkspace.enterOverviewMode(); } } else { - if (!(itemUnderLongClick instanceof Folder)) { + final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank( + mHotseat.getOrderInHotseat( + longClickCellInfo.cellX, + longClickCellInfo.cellY)); + if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) { // User long pressed on an item mWorkspace.startDrag(longClickCellInfo); } From 5dee7aff5fa8774d37977d12df7ce8986752321d Mon Sep 17 00:00:00 2001 From: Chris Wren Date: Fri, 20 Dec 2013 17:22:11 -0500 Subject: [PATCH 07/24] restore app favorites and screens version 0: restore assuming apps are already installed. Bug: 10779035 Change-Id: I7f9aa418a7d3d5460a79a229c0fbc80305b5eb5c --- .../launcher3/LauncherBackupHelper.java | 85 +++++++++++++++++-- .../android/launcher3/LauncherProvider.java | 13 ++- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 556d4af791..b91a06abe6 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -38,6 +38,7 @@ import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -331,9 +332,15 @@ public class LauncherBackupHelper implements BackupHelper { if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); + if (!mRestoreEnabled) { + if (DEBUG) Log.v(TAG, "restore not enabled: skipping database mutation"); + return; + } + try { - Favorite favorite = unpackFavorite(buffer, 0, dataSize); - if (DEBUG) Log.d(TAG, "unpacked " + favorite.itemType); + ContentResolver cr = mContext.getContentResolver(); + ContentValues values = unpackFavorite(buffer, 0, dataSize); + cr.insert(Favorites.CONTENT_URI, values); } catch (InvalidProtocolBufferNanoException e) { Log.w(TAG, "failed to decode proto", e); } @@ -363,6 +370,7 @@ public class LauncherBackupHelper implements BackupHelper { Set currentIds = new HashSet(cursor.getCount()); try { cursor.moveToPosition(-1); + Log.d(TAG, "dumping screens after: " + in.t); while(cursor.moveToNext()) { final long id = cursor.getLong(ID_INDEX); final long updateTime = cursor.getLong(ID_MODIFIED); @@ -373,6 +381,9 @@ public class LauncherBackupHelper implements BackupHelper { if (!savedIds.contains(backupKey) || updateTime >= in.t) { byte[] blob = packScreen(cursor); writeRowToBackup(key, blob, out, data); + if (DEBUG) Log.d(TAG, "wrote screen " + id); + } else { + if (DEBUG) Log.d(TAG, "screen " + id + " was too old: " + updateTime); } } } finally { @@ -399,9 +410,17 @@ public class LauncherBackupHelper implements BackupHelper { Log.v(TAG, "unpacking screen " + key.id); if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " + Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP)); + + if (!mRestoreEnabled) { + if (DEBUG) Log.v(TAG, "restore not enabled: skipping database mutation"); + return; + } + try { - Screen screen = unpackScreen(buffer, 0, dataSize); - if (DEBUG) Log.d(TAG, "unpacked " + screen.rank); + ContentResolver cr = mContext.getContentResolver(); + ContentValues values = unpackScreen(buffer, 0, dataSize); + cr.insert(WorkspaceScreens.CONTENT_URI, values); + } catch (InvalidProtocolBufferNanoException e) { Log.w(TAG, "failed to decode proto", e); } @@ -519,6 +538,13 @@ public class LauncherBackupHelper implements BackupHelper { if (icon == null) { Log.w(TAG, "failed to unpack icon for " + key.name); } + + if (!mRestoreEnabled) { + if (DEBUG) Log.v(TAG, "restore not enabled: skipping database mutation"); + return; + } else { + // future site of icon cache mutation + } } catch (InvalidProtocolBufferNanoException e) { Log.w(TAG, "failed to decode proto", e); } @@ -634,6 +660,13 @@ public class LauncherBackupHelper implements BackupHelper { Log.w(TAG, "failed to unpack widget icon for " + key.name); } } + + if (!mRestoreEnabled) { + if (DEBUG) Log.v(TAG, "restore not enabled: skipping database mutation"); + return; + } else { + // future site of widget table mutation + } } catch (InvalidProtocolBufferNanoException e) { Log.w(TAG, "failed to decode proto", e); } @@ -770,11 +803,43 @@ public class LauncherBackupHelper implements BackupHelper { } /** Deserialize a Favorite from persistence, after verifying checksum wrapper. */ - private Favorite unpackFavorite(byte[] buffer, int offset, int dataSize) + private ContentValues unpackFavorite(byte[] buffer, int offset, int dataSize) throws InvalidProtocolBufferNanoException { Favorite favorite = new Favorite(); MessageNano.mergeFrom(favorite, readCheckedBytes(buffer, offset, dataSize)); - return favorite; + if (DEBUG) Log.d(TAG, "unpacked " + favorite.itemType + ", " + favorite.id); + ContentValues values = new ContentValues(); + values.put(Favorites._ID, favorite.id); + values.put(Favorites.SCREEN, favorite.screen); + values.put(Favorites.CONTAINER, favorite.container); + values.put(Favorites.CELLX, favorite.cellX); + values.put(Favorites.CELLY, favorite.cellY); + values.put(Favorites.SPANX, favorite.spanX); + values.put(Favorites.SPANY, favorite.spanY); + values.put(Favorites.ICON_TYPE, favorite.iconType); + if (favorite.iconType == Favorites.ICON_TYPE_RESOURCE) { + values.put(Favorites.ICON_PACKAGE, favorite.iconPackage); + values.put(Favorites.ICON_RESOURCE, favorite.iconResource); + } + if (favorite.iconType == Favorites.ICON_TYPE_BITMAP) { + values.put(Favorites.ICON, favorite.icon); + } + if (!TextUtils.isEmpty(favorite.title)) { + values.put(Favorites.TITLE, favorite.title); + } else { + values.put(Favorites.TITLE, ""); + } + if (!TextUtils.isEmpty(favorite.intent)) { + values.put(Favorites.INTENT, favorite.intent); + } + values.put(Favorites.ITEM_TYPE, favorite.itemType); + if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) { + if (!TextUtils.isEmpty(favorite.appWidgetProvider)) { + values.put(Favorites.APPWIDGET_PROVIDER, favorite.appWidgetProvider); + } + values.put(Favorites.APPWIDGET_ID, favorite.appWidgetId); + } + return values; } /** Serialize a Screen for persistence, including a checksum wrapper. */ @@ -787,11 +852,15 @@ public class LauncherBackupHelper implements BackupHelper { } /** Deserialize a Screen from persistence, after verifying checksum wrapper. */ - private Screen unpackScreen(byte[] buffer, int offset, int dataSize) + private ContentValues unpackScreen(byte[] buffer, int offset, int dataSize) throws InvalidProtocolBufferNanoException { Screen screen = new Screen(); MessageNano.mergeFrom(screen, readCheckedBytes(buffer, offset, dataSize)); - return screen; + if (DEBUG) Log.d(TAG, "unpacked " + screen.id + "/" + screen.rank); + ContentValues values = new ContentValues(); + values.put(WorkspaceScreens._ID, screen.id); + values.put(WorkspaceScreens.SCREEN_RANK, screen.rank); + return values; } /** Serialize an icon Resource for persistence, including a checksum wrapper. */ diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 7adbadea10..5676a9adb7 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -138,9 +138,10 @@ public class LauncherProvider extends ContentProvider { if (values == null) { throw new RuntimeException("Error: attempting to insert null values"); } - if (!values.containsKey(LauncherSettings.Favorites._ID)) { + if (!values.containsKey(LauncherSettings.BaseLauncherColumns._ID)) { throw new RuntimeException("Error: attempting to add item without specifying an id"); } + helper.checkId(table, values); return db.insert(table, nullColumnHack, values); } @@ -271,6 +272,7 @@ public class LauncherProvider extends ContentProvider { SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE); if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) { + Log.d(TAG, "loading default workspace"); int workspaceResId = origWorkspaceResId; // Use default workspace resource if none provided @@ -882,6 +884,15 @@ public class LauncherProvider extends ContentProvider { mMaxItemId = id + 1; } + public void checkId(String table, ContentValues values) { + long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID); + if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) { + mMaxScreenId = Math.max(id, mMaxScreenId); + } else { + mMaxItemId = Math.max(id, mMaxItemId); + } + } + private long initializeMaxItemId(SQLiteDatabase db) { Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null); From 285b6e1483fd7ddc941fb2c9c50d43ee1b1841ca Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 14 Jan 2014 10:30:27 -0800 Subject: [PATCH 08/24] Properly saving migration cling keys. Change-Id: Id52a12da6af0fc91463dae07bbeb355ee04873d0 --- src/com/android/launcher3/LauncherClings.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index 541eaddd0d..85937aa62e 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -373,14 +373,14 @@ class LauncherClings { showMigrationWorkspaceCling(); } }; - dismissCling(cling, cb, WORKSPACE_CLING_DISMISSED_KEY, + dismissCling(cling, cb, MIGRATION_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION, true); } }; mLauncher.getWorkspace().post(dismissCb); } - private void dismissAnyWorkspaceCling(Cling cling, View v) { + private void dismissAnyWorkspaceCling(Cling cling, String key, View v) { Runnable cb = null; if (v == null) { cb = new Runnable() { @@ -389,8 +389,7 @@ class LauncherClings { } }; } - dismissCling(cling, cb, WORKSPACE_CLING_DISMISSED_KEY, - DISMISS_CLING_DURATION, true); + dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true); // Fade in the search bar mLauncher.getSearchBar().showSearchBar(true); @@ -428,12 +427,12 @@ class LauncherClings { public void dismissMigrationWorkspaceCling(View v) { Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling); - dismissAnyWorkspaceCling(cling, v); + dismissAnyWorkspaceCling(cling, MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, v); } public void dismissWorkspaceCling(View v) { Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling); - dismissAnyWorkspaceCling(cling, v); + dismissAnyWorkspaceCling(cling, WORKSPACE_CLING_DISMISSED_KEY, v); } public void dismissFolderCling(View v) { From eedb00a674358bb88dce1e0d3a90bd6cb9e97cfc Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Mon, 13 Jan 2014 13:45:07 -0800 Subject: [PATCH 09/24] Fix NPE in Workspace.onDropCompleted Bug: 11627757 Change-Id: I9dc86856d4bc00253d6350d157be541a8c46888d --- src/com/android/launcher3/LauncherAppState.java | 4 ++++ src/com/android/launcher3/Workspace.java | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 5e41fcad05..5cfcf8b9fa 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -238,4 +238,8 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { return getInstance().mBuildInfo.isDogfoodBuild() && Launcher.isPropertyEnabled(Launcher.DISABLE_ALL_APPS_PROPERTY); } + + public static boolean isDogfoodBuild() { + return getInstance().mBuildInfo.isDogfoodBuild(); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b6276c0f20..91c29cba9f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4051,7 +4051,13 @@ public class Workspace extends SmoothPagedView } else { cellLayout = getScreenWithId(mDragInfo.screenId); } - cellLayout.onDropChild(mDragInfo.cell); + if (cellLayout == null && LauncherAppState.isDogfoodBuild()) { + throw new RuntimeException("Invalid state: cellLayout == null in " + + "Workspace#onDropCompleted. Please file a bug. "); + } + if (cellLayout != null) { + cellLayout.onDropChild(mDragInfo.cell); + } } if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful)) && mDragInfo.cell != null) { From 65b6a603fa972a1bd62298558016eb4712e8c727 Mon Sep 17 00:00:00 2001 From: Chris Wren Date: Fri, 10 Jan 2014 14:11:25 -0500 Subject: [PATCH 10/24] search for a valid journal there are too many bytes, so we need to find the valid subset, without stepping past the end! Bug: 12489602 Change-Id: Ic9d7c804c199740ff50d0864f99632ae68619369 --- .../launcher3/LauncherBackupHelper.java | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 556d4af791..7268b0865a 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -858,7 +858,7 @@ public class LauncherBackupHelper implements BackupHelper { * in that case, do a full backup. * * @param oldState the read-0only file descriptor pointing to the old journal - * @return a Journal protocol bugffer + * @return a Journal protocol buffer */ private Journal readJournal(ParcelFileDescriptor oldState) { Journal journal = new Journal(); @@ -867,38 +867,50 @@ public class LauncherBackupHelper implements BackupHelper { } FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor()); try { - int remaining = inStream.available(); - if (DEBUG) Log.d(TAG, "available " + remaining); - if (remaining < MAX_JOURNAL_SIZE) { - byte[] buffer = new byte[remaining]; + int availableBytes = inStream.available(); + if (DEBUG) Log.d(TAG, "available " + availableBytes); + if (availableBytes < MAX_JOURNAL_SIZE) { + byte[] buffer = new byte[availableBytes]; int bytesRead = 0; - while (remaining > 0) { + boolean valid = false; + while (availableBytes > 0) { try { - int result = inStream.read(buffer, bytesRead, remaining); + // OMG what are you doing? This is crazy inefficient! + // If we read a byte that is not ours, we will cause trouble: b/12491813 + // However, we don't know how many bytes to expect (oops). + // So we have to step through *slowly*, watching for the end. + int result = inStream.read(buffer, bytesRead, 1); if (result > 0) { - if (DEBUG) Log.d(TAG, "read some bytes: " + result); - remaining -= result; + availableBytes -= result; bytesRead += result; + if (DEBUG && (bytesRead % 100 == 0)) { + Log.d(TAG, "read some bytes: " + bytesRead); + } } else { - // stop reading ands see what there is to parse - Log.w(TAG, "read error: " + result); - remaining = 0; + Log.w(TAG, "unexpected end of file while reading journal."); + // stop reading and see what there is to parse + availableBytes = 0; } } catch (IOException e) { - Log.w(TAG, "failed to read the journal", e); + Log.e(TAG, "failed to read the journal", e); buffer = null; - remaining = 0; + availableBytes = 0; + } + + // check the buffer to see if we have a valid journal + try { + MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, bytesRead)); + // if we are here, then we have read a valid, checksum-verified journal + valid = true; + availableBytes = 0; + } catch (InvalidProtocolBufferNanoException e) { + // if we don't have the whole journal yet, mergeFrom will throw. keep going. + journal.clear(); } } if (DEBUG) Log.d(TAG, "journal bytes read: " + bytesRead); - - if (buffer != null) { - try { - MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, bytesRead)); - } catch (InvalidProtocolBufferNanoException e) { - Log.d(TAG, "failed to read the journal", e); - journal.clear(); - } + if (!valid) { + Log.w(TAG, "failed to read the journal: could not find a valid journal"); } } } catch (IOException e) { @@ -968,7 +980,9 @@ public class LauncherBackupHelper implements BackupHelper { FileOutputStream outStream = null; try { outStream = new FileOutputStream(newState.getFileDescriptor()); - outStream.write(writeCheckedBytes(journal)); + final byte[] journalBytes = writeCheckedBytes(journal); + if (DEBUG) Log.d(TAG, "writing " + journalBytes.length + " bytes of journal"); + outStream.write(journalBytes); outStream.close(); } catch (IOException e) { Log.d(TAG, "failed to write backup journal", e); From 1b921efc84e09e39ec5d2bd114ec919dfe2a79f0 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Tue, 14 Jan 2014 16:00:17 -0800 Subject: [PATCH 11/24] Keep selected wallpaper on configuration change. Keeps the currently selected wallpaper when a configuration (for example, device rotation) occurs. This change also effects the logic when the thumbnail gets displayed after loading the image from an external URI, as this must be now handled independently of which wallpaper is selected. Bug: 11007874 Change-Id: Id676c378aec38d0b21e5d9150846f33aff415cad --- .../launcher3/WallpaperPickerActivity.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index d3c779fe4b..c54e4779c1 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -85,6 +85,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6; public static final int PICK_LIVE_WALLPAPER = 7; private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES"; + private static final String SELECTED_INDEX = "SELECTED_INDEX"; private static final String OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb.jpg"; private static final String DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb2.jpg"; @@ -103,6 +104,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { ArrayList mTempWallpaperTiles = new ArrayList(); private SavedWallpaperImages mSavedImages; private WallpaperInfo mLiveWallpaperInfoOnPickerLaunch; + private int mSelectedIndex; public static abstract class WallpaperTileInfo { protected View mView; @@ -148,7 +150,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public void run() { if (mBitmapSource != null && mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) { - mView.setVisibility(View.VISIBLE); a.selectTile(mView); } else { ViewGroup parent = (ViewGroup) mView.getParent(); @@ -430,8 +431,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if ((right - left) > 0 && (bottom - top) > 0) { - if (mWallpapersView.getChildCount() > 0) { - mThumbnailOnClickListener.onClick(mWallpapersView.getChildAt(0)); + if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) { + mThumbnailOnClickListener.onClick( + mWallpapersView.getChildAt(mSelectedIndex)); } v.removeOnLayoutChangeListener(this); } @@ -551,6 +553,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } mSelectedTile = v; v.setSelected(true); + mSelectedIndex = mWallpapersView.indexOfChild(v); // TODO: Remove this once the accessibility framework and // services have better support for selection state. v.announceForAccessibility( @@ -601,13 +604,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { protected void onSaveInstanceState(Bundle outState) { outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles); + outState.putInt(SELECTED_INDEX, mSelectedIndex); } protected void onRestoreInstanceState(Bundle savedInstanceState) { ArrayList uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES); for (Uri uri : uris) { - addTemporaryWallpaperTile(uri); + addTemporaryWallpaperTile(uri, true); } + mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, 0); } private void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, @@ -711,7 +716,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } } - private void addTemporaryWallpaperTile(final Uri uri) { + private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) { mTempWallpaperTiles.add(uri); // Add a tile for the image picked from Gallery final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater(). @@ -735,6 +740,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { image.setImageBitmap(thumb); Drawable thumbDrawable = image.getDrawable(); thumbDrawable.setDither(true); + pickedImageThumbnail.setVisibility(View.VISIBLE); } else { Log.e(TAG, "Error loading thumbnail for uri=" + uri); } @@ -747,14 +753,16 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { addLongPressHandler(pickedImageThumbnail); updateTileIndices(); pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener); - mThumbnailOnClickListener.onClick(pickedImageThumbnail); + if (!fromRestore) { + mThumbnailOnClickListener.onClick(pickedImageThumbnail); + } } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) { if (data != null && data.getData() != null) { Uri uri = data.getData(); - addTemporaryWallpaperTile(uri); + addTemporaryWallpaperTile(uri, false); } } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) { setResult(RESULT_OK); From bc4539df6fd8b0672fbf51a49cba630add5083a3 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Tue, 14 Jan 2014 14:05:32 -0800 Subject: [PATCH 12/24] Ensure that hasFirstRunActivity is only called when necessary Change-Id: I834abcd82f46c6f096f5c92452b34c668a4a3d86 --- src/com/android/launcher3/Launcher.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 765fca4993..93852ef5a4 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4304,12 +4304,13 @@ public class Launcher extends Activity } private boolean shouldRunFirstRunActivity() { - return !ActivityManager.isRunningInTestHarness(); + return !ActivityManager.isRunningInTestHarness() && + !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false); } public void showFirstRunActivity() { - if (shouldRunFirstRunActivity() && hasFirstRunActivity() - && !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false)) { + if (shouldRunFirstRunActivity() && + hasFirstRunActivity()) { Intent firstRunIntent = getFirstRunActivity(); if (firstRunIntent != null) { startActivity(firstRunIntent); From 24ce0b3708b44e738d6ee52477aa4dd917b547e7 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Tue, 14 Jan 2014 16:18:14 -0800 Subject: [PATCH 13/24] Fixing drop targets in phone / small tablet landscape (issue 12192781) Change-Id: I4dc8a82c8cd5ef279506e7868e33a455fba5a3be --- res/layout-land/launcher.xml | 4 ++-- res/layout-land/{search_bar.xml => qsb.xml} | 0 res/layout-port/launcher.xml | 4 ++-- res/layout-port/{search_bar.xml => qsb.xml} | 1 - res/layout-sw720dp/launcher.xml | 4 ++-- res/layout-sw720dp/{search_bar.xml => qsb.xml} | 3 ++- .../{qsb_bar.xml => search_drop_target_bar.xml} | 3 +-- res/values-land/styles.xml | 4 ---- res/values-sw720dp/styles.xml | 5 ----- res/values/styles.xml | 4 ---- src/com/android/launcher3/DeviceProfile.java | 13 +++++-------- src/com/android/launcher3/FocusHelper.java | 2 +- src/com/android/launcher3/Launcher.java | 13 +++++++------ 13 files changed, 22 insertions(+), 38 deletions(-) rename res/layout-land/{search_bar.xml => qsb.xml} (100%) rename res/layout-port/{search_bar.xml => qsb.xml} (98%) rename res/layout-sw720dp/{search_bar.xml => qsb.xml} (97%) rename res/layout/{qsb_bar.xml => search_drop_target_bar.xml} (94%) diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml index abb19f4bb0..2880c18bfb 100644 --- a/res/layout-land/launcher.xml +++ b/res/layout-land/launcher.xml @@ -45,8 +45,8 @@ android:layout_gravity="end" /> + android:id="@+id/search_drop_target_bar" + layout="@layout/search_drop_target_bar" /> + android:id="@+id/search_drop_target_bar" + layout="@layout/search_drop_target_bar" /> - - - - - - - - - diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml index 507af1d16e..71f0304009 100644 --- a/res/values-sw720dp/styles.xml +++ b/res/values-sw720dp/styles.xml @@ -18,38 +18,6 @@ --> - - - - -