summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--protos/backup.proto2
-rw-r--r--res/drawable-hdpi/ic_allapps.pngbin10642 -> 10781 bytes
-rw-r--r--res/drawable-hdpi/ic_allapps_pressed.pngbin10032 -> 10316 bytes
-rw-r--r--res/drawable-hdpi/widget_preview_tile.pngbin548 -> 0 bytes
-rw-r--r--res/drawable-hdpi/widget_tile.pngbin0 -> 545 bytes
-rw-r--r--res/drawable-mdpi/ic_allapps.pngbin6559 -> 6613 bytes
-rw-r--r--res/drawable-mdpi/ic_allapps_pressed.pngbin6253 -> 6257 bytes
-rw-r--r--res/drawable-mdpi/widget_preview_tile.pngbin406 -> 0 bytes
-rw-r--r--res/drawable-mdpi/widget_tile.pngbin0 -> 405 bytes
-rw-r--r--res/drawable-xhdpi/ic_allapps.pngbin15029 -> 15138 bytes
-rw-r--r--res/drawable-xhdpi/ic_allapps_pressed.pngbin14175 -> 14198 bytes
-rw-r--r--res/drawable-xhdpi/widget_preview_tile.pngbin615 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/widget_tile.pngbin0 -> 614 bytes
-rw-r--r--res/drawable-xxhdpi/ic_allapps.pngbin18386 -> 18589 bytes
-rw-r--r--res/drawable-xxhdpi/ic_allapps_pressed.pngbin17478 -> 17563 bytes
-rw-r--r--res/drawable-xxhdpi/widget_tile.pngbin0 -> 1290 bytes
-rw-r--r--res/drawable-xxhdpi/widget_tile_jb.pngbin1300 -> 0 bytes
-rw-r--r--res/layout-land/first_run_cling.xml19
-rw-r--r--res/layout-land/workspace_cling.xml6
-rw-r--r--res/layout-port/first_run_cling.xml19
-rw-r--r--res/layout-port/workspace_cling.xml5
-rw-r--r--res/layout/apps_customize_pane.xml6
-rw-r--r--res/values/colors.xml2
-rw-r--r--src/com/android/launcher3/Cling.java2
-rw-r--r--src/com/android/launcher3/LauncherBackupAgent.java439
-rw-r--r--src/com/android/launcher3/PagedView.java4
-rw-r--r--src/com/android/launcher3/WidgetPreviewLoader.java4
27 files changed, 332 insertions, 176 deletions
diff --git a/protos/backup.proto b/protos/backup.proto
index 3780bc583..f43f338e5 100644
--- a/protos/backup.proto
+++ b/protos/backup.proto
@@ -23,7 +23,7 @@ message Key {
enum Type {
FAVORITE = 1;
SCREEN = 2;
- IMAGE = 3;
+ ICON = 3;
}
required Type type = 1;
optional string name = 2; // keep this short
diff --git a/res/drawable-hdpi/ic_allapps.png b/res/drawable-hdpi/ic_allapps.png
index f55aabfbf..e7677d5d2 100644
--- a/res/drawable-hdpi/ic_allapps.png
+++ b/res/drawable-hdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_allapps_pressed.png b/res/drawable-hdpi/ic_allapps_pressed.png
index bbc4d7d03..bdc7baab7 100644
--- a/res/drawable-hdpi/ic_allapps_pressed.png
+++ b/res/drawable-hdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_preview_tile.png b/res/drawable-hdpi/widget_preview_tile.png
deleted file mode 100644
index caeddd1ce..000000000
--- a/res/drawable-hdpi/widget_preview_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_tile.png b/res/drawable-hdpi/widget_tile.png
new file mode 100644
index 000000000..310ff8b3d
--- /dev/null
+++ b/res/drawable-hdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps.png b/res/drawable-mdpi/ic_allapps.png
index d95f1bfc4..e0fd9c07a 100644
--- a/res/drawable-mdpi/ic_allapps.png
+++ b/res/drawable-mdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps_pressed.png b/res/drawable-mdpi/ic_allapps_pressed.png
index e24b9d73b..35a96836d 100644
--- a/res/drawable-mdpi/ic_allapps_pressed.png
+++ b/res/drawable-mdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_preview_tile.png b/res/drawable-mdpi/widget_preview_tile.png
deleted file mode 100644
index 9c7c4f77c..000000000
--- a/res/drawable-mdpi/widget_preview_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_tile.png b/res/drawable-mdpi/widget_tile.png
new file mode 100644
index 000000000..1ba559d6c
--- /dev/null
+++ b/res/drawable-mdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps.png b/res/drawable-xhdpi/ic_allapps.png
index 0e4316c90..f71964c2d 100644
--- a/res/drawable-xhdpi/ic_allapps.png
+++ b/res/drawable-xhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps_pressed.png b/res/drawable-xhdpi/ic_allapps_pressed.png
index 2b1333087..20bec4f31 100644
--- a/res/drawable-xhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_preview_tile.png b/res/drawable-xhdpi/widget_preview_tile.png
deleted file mode 100644
index 60238671e..000000000
--- a/res/drawable-xhdpi/widget_preview_tile.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_tile.png b/res/drawable-xhdpi/widget_tile.png
new file mode 100644
index 000000000..9730f35db
--- /dev/null
+++ b/res/drawable-xhdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps.png b/res/drawable-xxhdpi/ic_allapps.png
index 9d5d80cae..624e0ef44 100644
--- a/res/drawable-xxhdpi/ic_allapps.png
+++ b/res/drawable-xxhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps_pressed.png b/res/drawable-xxhdpi/ic_allapps_pressed.png
index b6a34a820..752070d09 100644
--- a/res/drawable-xxhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xxhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_tile.png b/res/drawable-xxhdpi/widget_tile.png
new file mode 100644
index 000000000..3a3790dfc
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_tile_jb.png b/res/drawable-xxhdpi/widget_tile_jb.png
deleted file mode 100644
index 363fe841d..000000000
--- a/res/drawable-xxhdpi/widget_tile_jb.png
+++ /dev/null
Binary files differ
diff --git a/res/layout-land/first_run_cling.xml b/res/layout-land/first_run_cling.xml
index 3b21e14f6..9baee64b7 100644
--- a/res/layout-land/first_run_cling.xml
+++ b/res/layout-land/first_run_cling.xml
@@ -25,26 +25,31 @@
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/bubble_content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:layout_marginLeft="100dp"
+ android:layout_marginRight="100dp"
android:orientation="vertical">
<TextView
style="@style/ClingAltTitleText"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="10dp"
android:text="@string/first_run_cling_title"
android:textColor="#FFFFFFFF"
- android:textSize="30sp" />
+ android:textSize="30sp"
+ android:gravity="center" />
<TextView
style="@style/ClingAltTitleText"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
android:text="@string/first_run_cling_description"
android:textColor="#80000000"
- android:textSize="16sp" />
+ android:textSize="16sp"
+ android:gravity="center" />
</LinearLayout>
<TextView
style="@style/ClingHintText"
@@ -53,7 +58,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:layout_marginEnd="10dp"
- android:layout_marginTop="80dp"
+ android:layout_marginTop="65dp"
android:visibility="gone"
android:drawableTop="@drawable/cling_arrow_up"
android:drawablePadding="5dp"
@@ -76,7 +81,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="10dp"
- android:layout_marginBottom="100dp"
+ android:layout_marginBottom="85dp"
android:drawableEnd="@drawable/cling_arrow_right"
android:drawablePadding="5dp"
android:text="@string/first_run_cling_create_screens_hint" />
diff --git a/res/layout-land/workspace_cling.xml b/res/layout-land/workspace_cling.xml
index 08fb8cf23..db33db06f 100644
--- a/res/layout-land/workspace_cling.xml
+++ b/res/layout-land/workspace_cling.xml
@@ -25,13 +25,11 @@
android:layout_height="match_parent"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
- android:layout_marginTop="20dp"
- android:layout_marginBottom="100dp">
+ android:layout_marginTop="310dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginBottom="40dp"
+ android:layout_gravity="top"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
diff --git a/res/layout-port/first_run_cling.xml b/res/layout-port/first_run_cling.xml
index 3b21e14f6..9baee64b7 100644
--- a/res/layout-port/first_run_cling.xml
+++ b/res/layout-port/first_run_cling.xml
@@ -25,26 +25,31 @@
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/bubble_content"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:layout_marginLeft="100dp"
+ android:layout_marginRight="100dp"
android:orientation="vertical">
<TextView
style="@style/ClingAltTitleText"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="10dp"
android:text="@string/first_run_cling_title"
android:textColor="#FFFFFFFF"
- android:textSize="30sp" />
+ android:textSize="30sp"
+ android:gravity="center" />
<TextView
style="@style/ClingAltTitleText"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
android:text="@string/first_run_cling_description"
android:textColor="#80000000"
- android:textSize="16sp" />
+ android:textSize="16sp"
+ android:gravity="center" />
</LinearLayout>
<TextView
style="@style/ClingHintText"
@@ -53,7 +58,7 @@
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:layout_marginEnd="10dp"
- android:layout_marginTop="80dp"
+ android:layout_marginTop="65dp"
android:visibility="gone"
android:drawableTop="@drawable/cling_arrow_up"
android:drawablePadding="5dp"
@@ -76,7 +81,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="10dp"
- android:layout_marginBottom="100dp"
+ android:layout_marginBottom="85dp"
android:drawableEnd="@drawable/cling_arrow_right"
android:drawablePadding="5dp"
android:text="@string/first_run_cling_create_screens_hint" />
diff --git a/res/layout-port/workspace_cling.xml b/res/layout-port/workspace_cling.xml
index 38d385896..db33db06f 100644
--- a/res/layout-port/workspace_cling.xml
+++ b/res/layout-port/workspace_cling.xml
@@ -25,12 +25,11 @@
android:layout_height="match_parent"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
- android:layout_marginTop="20dp"
- android:layout_marginBottom="120dp">
+ android:layout_marginTop="310dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_gravity="bottom"
+ android:layout_gravity="top"
android:orientation="vertical">
<ImageView
android:layout_width="wrap_content"
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml
index e4886017c..11a938fbb 100644
--- a/res/layout/apps_customize_pane.xml
+++ b/res/layout/apps_customize_pane.xml
@@ -78,10 +78,4 @@
android:layout_marginBottom="@dimen/apps_customize_page_indicator_margin" />
</FrameLayout>
</LinearLayout>
-
- <include layout="@layout/all_apps_cling"
- android:id="@+id/all_apps_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
</com.android.launcher3.AppsCustomizeTabHost>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index e4278d991..dc35a3f68 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -36,5 +36,5 @@
<color name="folder_items_text_color">#FF333333</color>
<color name="outline_color">#FFFFFFFF</color>
- <color name="first_run_cling_circle_background_color">#FF8BB4E9</color>
+ <color name="first_run_cling_circle_background_color">#64b1ea</color>
</resources>
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 2656ad6a8..de92605d9 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -327,7 +327,7 @@ public class Cling extends FrameLayout implements Insettable, View.OnLongClickLi
}
if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
- // Draw the white circle
+ // Draw the circle
View bubbleContent = findViewById(R.id.bubble_content);
Rect bubbleRect = new Rect();
bubbleContent.getGlobalVisibleRect(bubbleRect);
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
index bb15ca184..cbef36b73 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -19,7 +19,6 @@ package com.android.launcher3;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import com.google.protobuf.nano.MessageNano;
-import com.android.launcher3.LauncherSettings.ChangeLogColumns;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
import com.android.launcher3.backup.BackupProtos;
@@ -27,88 +26,110 @@ import com.android.launcher3.backup.BackupProtos.CheckedMessage;
import com.android.launcher3.backup.BackupProtos.Favorite;
import com.android.launcher3.backup.BackupProtos.Journal;
import com.android.launcher3.backup.BackupProtos.Key;
+import com.android.launcher3.backup.BackupProtos.Resource;
import com.android.launcher3.backup.BackupProtos.Screen;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.os.ParcelFileDescriptor;
-import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
+import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.zip.CRC32;
+import static android.graphics.Bitmap.CompressFormat.WEBP;
+
/**
* Persist the launcher home state across calamities.
*/
public class LauncherBackupAgent extends BackupAgent {
private static final String TAG = "LauncherBackupAgent";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
private static final int MAX_JOURNAL_SIZE = 1000000;
+ private static final int MAX_ICONS_PER_PASS = 10;
+
private static BackupManager sBackupManager;
private static final String[] FAVORITE_PROJECTION = {
Favorites._ID, // 0
- Favorites.APPWIDGET_ID, // 1
- Favorites.APPWIDGET_PROVIDER, // 2
- Favorites.CELLX, // 3
- Favorites.CELLY, // 4
- Favorites.CONTAINER, // 5
- Favorites.ICON, // 6
- Favorites.ICON_PACKAGE, // 7
- Favorites.ICON_RESOURCE, // 8
- Favorites.ICON_TYPE, // 9
- Favorites.ITEM_TYPE, // 10
- Favorites.INTENT, // 11
- Favorites.SCREEN, // 12
- Favorites.SPANX, // 13
- Favorites.SPANY, // 14
- Favorites.TITLE, // 15
+ Favorites.MODIFIED, // 1
+ Favorites.INTENT, // 2
+ Favorites.APPWIDGET_PROVIDER, // 3
+ Favorites.APPWIDGET_ID, // 4
+ Favorites.CELLX, // 5
+ Favorites.CELLY, // 6
+ Favorites.CONTAINER, // 7
+ Favorites.ICON, // 8
+ Favorites.ICON_PACKAGE, // 9
+ Favorites.ICON_RESOURCE, // 10
+ Favorites.ICON_TYPE, // 11
+ Favorites.ITEM_TYPE, // 12
+ Favorites.SCREEN, // 13
+ Favorites.SPANX, // 14
+ Favorites.SPANY, // 15
+ Favorites.TITLE, // 16
};
private static final int ID_INDEX = 0;
- private static final int APPWIDGET_ID_INDEX = 1;
- private static final int APPWIDGET_PROVIDER_INDEX = 2;
- private static final int CELLX_INDEX = 3;
- private static final int CELLY_INDEX = 4;
- private static final int CONTAINER_INDEX = 5;
- private static final int ICON_INDEX = 6;
- private static final int ICON_PACKAGE_INDEX = 7;
- private static final int ICON_RESOURCE_INDEX = 8;
- private static final int ICON_TYPE_INDEX = 9;
- private static final int ITEM_TYPE_INDEX = 10;
- private static final int INTENT_INDEX = 11;
- private static final int SCREEN_INDEX = 12;
- private static final int SPANX_INDEX = 13 ;
- private static final int SPANY_INDEX = 14;
- private static final int TITLE_INDEX = 15;
+ private static final int ID_MODIFIED = 1;
+ private static final int INTENT_INDEX = 2;
+ private static final int APPWIDGET_PROVIDER_INDEX = 3;
+ private static final int APPWIDGET_ID_INDEX = 4;
+ private static final int CELLX_INDEX = 5;
+ private static final int CELLY_INDEX = 6;
+ private static final int CONTAINER_INDEX = 7;
+ private static final int ICON_INDEX = 8;
+ private static final int ICON_PACKAGE_INDEX = 9;
+ private static final int ICON_RESOURCE_INDEX = 10;
+ private static final int ICON_TYPE_INDEX = 11;
+ private static final int ITEM_TYPE_INDEX = 12;
+ private static final int SCREEN_INDEX = 13;
+ private static final int SPANX_INDEX = 14;
+ private static final int SPANY_INDEX = 15;
+ private static final int TITLE_INDEX = 16;
private static final String[] SCREEN_PROJECTION = {
WorkspaceScreens._ID, // 0
- WorkspaceScreens.SCREEN_RANK // 1
+ WorkspaceScreens.MODIFIED, // 1
+ WorkspaceScreens.SCREEN_RANK // 2
};
- private static final int SCREEN_RANK_INDEX = 1;
+ private static final int SCREEN_RANK_INDEX = 2;
- private static final String[] ID_ONLY_PROJECTION = {
- BaseColumns._ID
+
+ private static final String[] ICON_PROJECTION = {
+ Favorites._ID, // 0
+ Favorites.MODIFIED, // 1
+ Favorites.INTENT // 2
};
+ private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap;
+
/**
* Notify the backup manager that out database is dirty.
@@ -155,12 +176,11 @@ public class LauncherBackupAgent extends BackupAgent {
ArrayList<Key> keys = new ArrayList<Key>();
backupFavorites(in, data, out, keys);
backupScreens(in, data, out, keys);
+ backupIcons(in, data, out, keys);
out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY);
writeJournal(newState, out);
Log.v(TAG, "onBackup: wrote " + out.bytes + "b in " + out.rows + " rows.");
-
- Log.v(TAG, "onBackup: finished");
}
/**
@@ -205,6 +225,10 @@ public class LauncherBackupAgent extends BackupAgent {
restoreScreen(key, buffer, dataSize, keys);
break;
+ case Key.ICON:
+ restoreIcon(key, buffer, dataSize, keys);
+ break;
+
default:
Log.w(TAG, "unknown restore entity type: " + key.type);
break;
@@ -236,70 +260,35 @@ public class LauncherBackupAgent extends BackupAgent {
ArrayList<Key> keys)
throws IOException {
// read the old ID set
- Set<String> savedIds = new HashSet<String>();
- for(int i = 0; i < in.key.length; i++) {
- Key key = in.key[i];
- if (key.type == Key.FAVORITE) {
- savedIds.add(keyToBackupKey(key));
- }
- }
+ Set<String> savedIds = getSavedIdsByType(Key.FAVORITE, in);
if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
// persist things that have changed since the last backup
ContentResolver cr = getContentResolver();
- String where = ChangeLogColumns.MODIFIED + " > ?";
- String[] args = {Long.toString(in.t)};
- String updateOrder = ChangeLogColumns.MODIFIED;
- Cursor updated = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
- where, args, updateOrder);
- if (DEBUG) Log.d(TAG, "favorite updated.getCount()=" + updated.getCount());
+ Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
+ null, null, null);
+ Set<String> currentIds = new HashSet<String>(cursor.getCount());
try {
- updated.moveToPosition(-1);
- while(updated.moveToNext()) {
- final long id = updated.getLong(ID_INDEX);
+ cursor.moveToPosition(-1);
+ while(cursor.moveToNext()) {
+ final long id = cursor.getLong(ID_INDEX);
+ final long updateTime = cursor.getLong(ID_MODIFIED);
Key key = getKey(Key.FAVORITE, id);
- byte[] blob = packFavorite(updated);
- String backupKey = keyToBackupKey(key);
- data.writeEntityHeader(backupKey, blob.length);
- data.writeEntityData(blob, blob.length);
- out.rows++;
- out.bytes += blob.length;
- Log.v(TAG, "saving favorite " + backupKey + ": " + id + "/" + blob.length);
- if(DEBUG) Log.d(TAG, "wrote " +
- Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP));
- // remember that is was a new column, so we don't delete it.
- savedIds.add(backupKey);
- }
- } finally {
- updated.close();
- }
- if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
-
- // build the current ID set
- String idOrder = BaseColumns._ID;
- Cursor idCursor = cr.query(Favorites.CONTENT_URI, ID_ONLY_PROJECTION,
- null, null, idOrder);
- Set<String> currentIds = new HashSet<String>(idCursor.getCount());
- try {
- idCursor.moveToPosition(-1);
- while(idCursor.moveToNext()) {
- Key key = getKey(Key.FAVORITE, idCursor.getLong(ID_INDEX));
- currentIds.add(keyToBackupKey(key));
- // save the IDs for next time
keys.add(key);
+ currentIds.add(keyToBackupKey(key));
+ if (updateTime > in.t) {
+ byte[] blob = packFavorite(cursor);
+ writeRowToBackup(key, blob, out, data);
+ }
}
} finally {
- idCursor.close();
+ cursor.close();
}
if (DEBUG) Log.d(TAG, "favorite currentIds.size()=" + currentIds.size());
// these IDs must have been deleted
savedIds.removeAll(currentIds);
- for (String deleted : savedIds) {
- Log.v(TAG, "dropping favorite " + deleted);
- data.writeEntityHeader(deleted, -1);
- out.rows++;
- }
+ out.rows += removeDeletedKeysFromBackup(savedIds, data);
}
/**
@@ -332,76 +321,42 @@ public class LauncherBackupAgent extends BackupAgent {
* @param in notes from last backup
* @param data output stream for key/value pairs
* @param out notes about this backup
- * @param keys keys to mark as clean in the notes for next backup @throws IOException
+ * @param keys keys to mark as clean in the notes for next backup
+ * @throws IOException
*/
private void backupScreens(Journal in, BackupDataOutput data, Journal out,
ArrayList<Key> keys)
throws IOException {
// read the old ID set
- Set<String> savedIds = new HashSet<String>();
- for(int i = 0; i < in.key.length; i++) {
- Key key = in.key[i];
- if (key.type == Key.SCREEN) {
- savedIds.add(keyToBackupKey(key));
- }
- }
- if (DEBUG) Log.d(TAG, "screens savedIds.size()=" + savedIds.size());
+ Set<String> savedIds = getSavedIdsByType(Key.SCREEN, in);
+ if (DEBUG) Log.d(TAG, "screen savedIds.size()=" + savedIds.size());
// persist things that have changed since the last backup
ContentResolver cr = getContentResolver();
- String where = ChangeLogColumns.MODIFIED + " > ?";
- String[] args = {Long.toString(in.t)};
- String updateOrder = ChangeLogColumns.MODIFIED;
- Cursor updated = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION,
- where, args, updateOrder);
- updated.moveToPosition(-1);
- if (DEBUG) Log.d(TAG, "screens updated.getCount()=" + updated.getCount());
+ Cursor cursor = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION,
+ null, null, null);
+ Set<String> currentIds = new HashSet<String>(cursor.getCount());
try {
- while(updated.moveToNext()) {
- final long id = updated.getLong(ID_INDEX);
+ cursor.moveToPosition(-1);
+ while(cursor.moveToNext()) {
+ final long id = cursor.getLong(ID_INDEX);
+ final long updateTime = cursor.getLong(ID_MODIFIED);
Key key = getKey(Key.SCREEN, id);
- byte[] blob = packScreen(updated);
- String backupKey = keyToBackupKey(key);
- data.writeEntityHeader(backupKey, blob.length);
- data.writeEntityData(blob, blob.length);
- out.rows++;
- out.bytes += blob.length;
- Log.v(TAG, "saving screen " + backupKey + ": " + id + "/" + blob.length);
- if(DEBUG) Log.d(TAG, "wrote " +
- Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP));
- // remember that is was a new column, so we don't delete it.
- savedIds.add(backupKey);
- }
- } finally {
- updated.close();
- }
- if (DEBUG) Log.d(TAG, "screen savedIds.size()=" + savedIds.size());
-
- // build the current ID set
- String idOrder = BaseColumns._ID;
- Cursor idCursor = cr.query(WorkspaceScreens.CONTENT_URI, ID_ONLY_PROJECTION,
- null, null, idOrder);
- idCursor.moveToPosition(-1);
- Set<String> currentIds = new HashSet<String>(idCursor.getCount());
- try {
- while(idCursor.moveToNext()) {
- Key key = getKey(Key.SCREEN, idCursor.getLong(ID_INDEX));
- currentIds.add(keyToBackupKey(key));
- // save the IDs for next time
keys.add(key);
+ currentIds.add(keyToBackupKey(key));
+ if (updateTime > in.t) {
+ byte[] blob = packScreen(cursor);
+ writeRowToBackup(key, blob, out, data);
+ }
}
} finally {
- idCursor.close();
+ cursor.close();
}
if (DEBUG) Log.d(TAG, "screen currentIds.size()=" + currentIds.size());
// these IDs must have been deleted
savedIds.removeAll(currentIds);
- for(String deleted: savedIds) {
- Log.v(TAG, "dropping screen " + deleted);
- data.writeEntityHeader(deleted, -1);
- out.rows++;
- }
+ out.rows += removeDeletedKeysFromBackup(savedIds, data);
}
/**
@@ -426,7 +381,118 @@ public class LauncherBackupAgent extends BackupAgent {
}
}
- /** create a new key object.
+ /**
+ * Write all the static icon resources we need to render placeholders
+ * for a package that is not installed.
+ *
+ * @param in notes from last backup
+ * @param data output stream for key/value pairs
+ * @param out notes about this backup
+ * @param keys keys to mark as clean in the notes for next backup
+ * @throws IOException
+ */
+ private void backupIcons(Journal in, BackupDataOutput data, Journal out,
+ ArrayList<Key> keys) throws IOException {
+ // persist icons for new shortcuts since the last backup
+ final ContentResolver cr = getContentResolver();
+ final IconCache iconCache = new IconCache(this);
+ final int dpi = getResources().getDisplayMetrics().densityDpi;
+
+ // read the old ID set
+ Set<String> savedIds = getSavedIdsByType(Key.ICON, in);
+ if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size());
+
+ int startRows = out.rows;
+ if (DEBUG) Log.d(TAG, "starting here: " + startRows);
+ String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION;
+ Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
+ where, null, null);
+ Set<String> currentIds = new HashSet<String>(cursor.getCount());
+ try {
+ cursor.moveToPosition(-1);
+ while(cursor.moveToNext()) {
+ final long id = cursor.getLong(ID_INDEX);
+ final String intentDescription = cursor.getString(INTENT_INDEX);
+ try {
+ Intent intent = Intent.parseUri(intentDescription, 0);
+ ComponentName cn = intent.getComponent();
+ Key key = null;
+ String backupKey = null;
+ if (cn != null) {
+ key = getKey(Key.ICON, cn.flattenToShortString());
+ backupKey = keyToBackupKey(key);
+ currentIds.add(backupKey);
+ } else {
+ Log.w(TAG, "empty intent on application favorite: " + id);
+ }
+ if (savedIds.contains(backupKey)) {
+ if (DEBUG) Log.d(TAG, "already saved icon " + backupKey);
+
+ // remember that we already backed this up previously
+ keys.add(key);
+ } else if (backupKey != null) {
+ if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows);
+ if ((out.rows - startRows) < MAX_ICONS_PER_PASS) {
+ if (DEBUG) Log.d(TAG, "saving icon " + backupKey);
+ Bitmap icon = iconCache.getIcon(intent);
+ keys.add(key);
+ if (icon != null && !iconCache.isDefaultIcon(icon)) {
+ byte[] blob = packIcon(dpi, icon);
+ writeRowToBackup(key, blob, out, data);
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "scheduling another rtun for icon " + backupKey);
+ // too many icons for this pass, request another.
+ dataChanged(this);
+ }
+ }
+ } catch (URISyntaxException e) {
+ Log.w(TAG, "invalid URI on application favorite: " + id);
+ } catch (IOException e) {
+ Log.w(TAG, "unable to save application icon for favorite: " + id);
+ }
+
+ }
+ } finally {
+ cursor.close();
+ }
+ if (DEBUG) Log.d(TAG, "icon currentIds.size()=" + currentIds.size());
+
+ // these IDs must have been deleted
+ savedIds.removeAll(currentIds);
+ out.rows += removeDeletedKeysFromBackup(savedIds, data);
+ }
+
+ /**
+ * Read an icon from the stream.
+ *
+ * <P>Keys arrive in any order, so shortcuts that use this screen may already exist.
+ *
+ * @param key identifier for the row
+ * @param buffer the serialized proto from the stream, may be larger than dataSize
+ * @param dataSize the size of the proto from the stream
+ * @param keys keys to mark as clean in the notes for next backup
+ */
+ private void restoreIcon(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+ Log.v(TAG, "unpacking icon " + key.id);
+ if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
+ Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
+ try {
+ Resource res = unpackIcon(buffer, 0, dataSize);
+ if (DEBUG) Log.d(TAG, "unpacked " + res.dpi);
+ if (DEBUG) Log.d(TAG, "read " +
+ Base64.encodeToString(res.data, 0, res.data.length,
+ Base64.NO_WRAP));
+ Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length);
+ if (icon == null) {
+ Log.w(TAG, "failed to unpack icon for " + key.name);
+ }
+ } catch (InvalidProtocolBufferNanoException e) {
+ Log.w(TAG, "failed to decode proto", e);
+ }
+ }
+
+ /** create a new key, with an integer ID.
*
* <P> Keys contain their own checksum instead of using
* the heavy-weight CheckedMessage wrapper.
@@ -439,6 +505,19 @@ public class LauncherBackupAgent extends BackupAgent {
return key;
}
+ /** create a new key for a named object.
+ *
+ * <P> Keys contain their own checksum instead of using
+ * the heavy-weight CheckedMessage wrapper.
+ */
+ private Key getKey(int type, String name) {
+ Key key = new Key();
+ key.type = type;
+ key.name = name;
+ key.checksum = checkKey(key);
+ return key;
+ }
+
/** keys need to be strings, serialize and encode. */
private String keyToBackupKey(Key key) {
return Base64.encodeToString(Key.toByteArray(key), Base64.NO_WRAP | Base64.NO_PADDING);
@@ -460,6 +539,28 @@ public class LauncherBackupAgent extends BackupAgent {
}
}
+ private String getKeyName(Key key) {
+ if (TextUtils.isEmpty(key.name)) {
+ return Long.toString(key.id);
+ } else {
+ return key.name;
+ }
+
+ }
+
+ private String geKeyType(Key key) {
+ switch (key.type) {
+ case Key.FAVORITE:
+ return "favorite";
+ case Key.SCREEN:
+ return "screen";
+ case Key.ICON:
+ return "icon";
+ default:
+ return "anonymous";
+ }
+ }
+
/** Compute the checksum over the important bits of a key. */
private long checkKey(Key key) {
CRC32 checksum = new CRC32();
@@ -544,6 +645,25 @@ public class LauncherBackupAgent extends BackupAgent {
return screen;
}
+ /** Serialize an icon Resource for persistence, including a checksum wrapper. */
+ private byte[] packIcon(int dpi, Bitmap icon) {
+ Resource res = new Resource();
+ res.dpi = dpi;
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ if (icon.compress(WEBP, 100, os)) {
+ res.data = os.toByteArray();
+ }
+ return writeCheckedBytes(res);
+ }
+
+ /** Deserialize an icon resource from persistence, after verifying checksum wrapper. */
+ private Resource unpackIcon(byte[] buffer, int offset, int dataSize)
+ throws InvalidProtocolBufferNanoException {
+ Resource res = new Resource();
+ MessageNano.mergeFrom(res, readCheckedBytes(buffer, offset, dataSize));
+ return res;
+ }
+
/**
* Read the old journal from the input file.
*
@@ -600,6 +720,41 @@ public class LauncherBackupAgent extends BackupAgent {
return journal;
}
+ private void writeRowToBackup(Key key, byte[] blob, Journal out,
+ BackupDataOutput data) throws IOException {
+ String backupKey = keyToBackupKey(key);
+ data.writeEntityHeader(backupKey, blob.length);
+ data.writeEntityData(blob, blob.length);
+ out.rows++;
+ out.bytes += blob.length;
+ Log.v(TAG, "saving " + geKeyType(key) + " " + backupKey + ": " +
+ getKeyName(key) + "/" + blob.length);
+ if(DEBUG) Log.d(TAG, "wrote " +
+ Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP));
+ }
+
+ private Set<String> getSavedIdsByType(int type, Journal in) {
+ Set<String> savedIds = new HashSet<String>();
+ for(int i = 0; i < in.key.length; i++) {
+ Key key = in.key[i];
+ if (key.type == type) {
+ savedIds.add(keyToBackupKey(key));
+ }
+ }
+ return savedIds;
+ }
+
+ private int removeDeletedKeysFromBackup(Set<String> deletedIds, BackupDataOutput data)
+ throws IOException {
+ int rows = 0;
+ for(String deleted: deletedIds) {
+ Log.v(TAG, "dropping icon " + deleted);
+ data.writeEntityHeader(deleted, -1);
+ rows++;
+ }
+ return rows;
+ }
+
/**
* Write the new journal to the output file.
*
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 6d7501865..fcc1aff8a 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1051,7 +1051,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected void getOverviewModePages(int[] range) {
range[0] = 0;
- range[1] = getChildCount() - 1;
+ range[1] = Math.max(0, getChildCount() - 1);
}
protected void getVisiblePages(int[] range) {
@@ -1476,7 +1476,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
public int getScrollForPage(int index) {
- if (mPageScrolls == null || index >= mPageScrolls.length) {
+ if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) {
return 0;
} else {
return mPageScrolls[index];
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 1910ab74a..956fd99cd 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -464,12 +464,12 @@ public class WidgetPreviewLoader {
if (cellVSpan < 1) cellVSpan = 1;
BitmapDrawable previewDrawable = (BitmapDrawable) mContext.getResources()
- .getDrawable(R.drawable.widget_preview_tile);
+ .getDrawable(R.drawable.widget_tile);
final int previewDrawableWidth = previewDrawable
.getIntrinsicWidth();
final int previewDrawableHeight = previewDrawable
.getIntrinsicHeight();
- previewWidth = previewDrawableWidth * cellHSpan; // subtract 2 dips
+ previewWidth = previewDrawableWidth * cellHSpan;
previewHeight = previewDrawableHeight * cellVSpan;
defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight,