import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
+import java.util.List;
public class WidgetDatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "PhotoDatabaseHelper";
public static final int TYPE_ALBUM = 2;
private static final String[] PROJECTION = {
- FIELD_WIDGET_TYPE, FIELD_IMAGE_URI, FIELD_PHOTO_BLOB, FIELD_ALBUM_PATH};
+ FIELD_WIDGET_TYPE, FIELD_IMAGE_URI, FIELD_PHOTO_BLOB, FIELD_ALBUM_PATH,
+ FIELD_APPWIDGET_ID};
private static final int INDEX_WIDGET_TYPE = 0;
private static final int INDEX_IMAGE_URI = 1;
private static final int INDEX_PHOTO_BLOB = 2;
private static final int INDEX_ALBUM_PATH = 3;
- private static final String WHERE_CLAUSE = FIELD_APPWIDGET_ID + " = ?";
+ private static final int INDEX_APPWIDGET_ID = 4;
+ private static final String WHERE_APPWIDGET_ID = FIELD_APPWIDGET_ID + " = ?";
+ private static final String WHERE_WIDGET_TYPE = FIELD_WIDGET_TYPE + " = ?";
public static class Entry {
public int widgetId;
albumPath = cursor.getString(INDEX_ALBUM_PATH);
}
}
+
+ private Entry(Cursor cursor) {
+ this(cursor.getInt(INDEX_APPWIDGET_ID), cursor);
+ }
}
public WidgetDatabaseHelper(Context context) {
try {
SQLiteDatabase db = getReadableDatabase();
cursor = db.query(TABLE_WIDGETS, PROJECTION,
- WHERE_CLAUSE, new String[] {String.valueOf(appWidgetId)},
+ WHERE_APPWIDGET_ID, new String[] {String.valueOf(appWidgetId)},
null, null, null);
if (cursor == null || !cursor.moveToNext()) {
Log.e(TAG, "query fail: empty cursor: " + cursor);
}
}
+ public List<Entry> getEntries(int type) {
+ Cursor cursor = null;
+ try {
+ SQLiteDatabase db = getReadableDatabase();
+ cursor = db.query(TABLE_WIDGETS, PROJECTION,
+ WHERE_WIDGET_TYPE, new String[] {String.valueOf(type)},
+ null, null, null);
+ if (cursor == null) {
+ Log.e(TAG, "query fail: null cursor: " + cursor);
+ return null;
+ }
+ ArrayList<Entry> result = new ArrayList<Entry>(cursor.getCount());
+ while (cursor.moveToNext()) {
+ result.add(new Entry(cursor));
+ }
+ return result;
+ } catch (Throwable e) {
+ Log.e(TAG, "Could not load widget from database", e);
+ return null;
+ } finally {
+ Utils.closeSilently(cursor);
+ }
+ }
+
+ /**
+ * Updates the entry in the widget database.
+ */
+ public void updateEntry(Entry entry) {
+ deleteEntry(entry.widgetId);
+ try {
+ ContentValues values = new ContentValues();
+ values.put(FIELD_APPWIDGET_ID, entry.widgetId);
+ values.put(FIELD_WIDGET_TYPE, entry.type);
+ values.put(FIELD_ALBUM_PATH, entry.albumPath);
+ values.put(FIELD_IMAGE_URI, entry.imageUri);
+ values.put(FIELD_PHOTO_BLOB, entry.imageData);
+ getWritableDatabase().insert(TABLE_WIDGETS, null, values);
+ } catch (Throwable e) {
+ Log.e(TAG, "set widget fail", e);
+ }
+ }
+
/**
* Remove any bitmap associated with the given appWidgetId.
*/
public void deleteEntry(int appWidgetId) {
try {
SQLiteDatabase db = getWritableDatabase();
- db.delete(TABLE_WIDGETS, WHERE_CLAUSE,
+ db.delete(TABLE_WIDGETS, WHERE_APPWIDGET_ID,
new String[] {String.valueOf(appWidgetId)});
} catch (SQLiteException e) {
Log.e(TAG, "Could not delete photo from database", e);
}
}
-}
\ No newline at end of file
+}
--- /dev/null
+/*
+ * Copyright (C) 2012 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.gallery3d.onetimeinitializer;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.LocalAlbum;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.Path;
+import com.android.gallery3d.gadget.WidgetDatabaseHelper;
+import com.android.gallery3d.gadget.WidgetDatabaseHelper.Entry;
+import com.android.gallery3d.util.GalleryUtils;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This one-timer migrates local-album gallery app widgets from pre-JB releases to JB (or later)
+ * due to bucket ID (i.e., directory hash) change in JB (as the external storage path is changed
+ * from /mnt/sdcard to /storage/sdcard0).
+ */
+public class GalleryWidgetMigrator {
+ private static final String TAG = "GalleryWidgetMigrator";
+ private static final String OLD_EXT_PATH = "/mnt/sdcard";
+ private static final String NEW_EXT_PATH =
+ Environment.getExternalStorageDirectory().getAbsolutePath();
+ private static final int RELATIVE_PATH_START = NEW_EXT_PATH.length();
+ private static final String KEY_MIGRATION_DONE = "gallery_widget_migration_done";
+
+ /**
+ * Migrates local-album gallery widgets from pre-JB releases to JB (or later) due to bucket ID
+ * (i.e., directory hash) change in JB.
+ */
+ public static void migrateGalleryWidgets(Context context) {
+ // no migration needed if path of external storage is not changed
+ if (OLD_EXT_PATH.equals(NEW_EXT_PATH)) return;
+
+ // only need to migrate once; the "done" bit is saved to SharedPreferences
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean isDone = prefs.getBoolean(KEY_MIGRATION_DONE, false);
+ if (isDone) return;
+
+ try {
+ migrateGalleryWidgetsInternal(context);
+ prefs.edit().putBoolean(KEY_MIGRATION_DONE, true).commit();
+ } catch (Throwable t) {
+ // exception may be thrown if external storage is not available(?)
+ Log.w(TAG, "migrateGalleryWidgets", t);
+ }
+ }
+
+ private static void migrateGalleryWidgetsInternal(Context context) {
+ GalleryApp galleryApp = (GalleryApp) context.getApplicationContext();
+ DataManager manager = galleryApp.getDataManager();
+ WidgetDatabaseHelper dbHelper = new WidgetDatabaseHelper(context);
+
+ // only need to migrate local-album entries of type TYPE_ALBUM
+ List<Entry> entries = dbHelper.getEntries(WidgetDatabaseHelper.TYPE_ALBUM);
+ if (entries != null) {
+ HashMap<Integer, Entry> localEntries = new HashMap<Integer, Entry>(entries.size());
+ for (Entry entry : entries) {
+ Path path = Path.fromString(entry.albumPath);
+ MediaSet mediaSet = (MediaSet) manager.getMediaObject(path);
+ if (mediaSet instanceof LocalAlbum) {
+ int bucketId = Integer.parseInt(path.getSuffix());
+ localEntries.put(bucketId, entry);
+ }
+ }
+ if (!localEntries.isEmpty()) migrateLocalEntries(localEntries, dbHelper);
+ }
+ }
+
+ private static void migrateLocalEntries(
+ HashMap<Integer, Entry> entries, WidgetDatabaseHelper dbHelper) {
+ File root = Environment.getExternalStorageDirectory();
+
+ // check the DCIM directory first; this should take care of 99% use cases
+ updatePath(new File(root, "DCIM"), entries, dbHelper);
+
+ // check other directories if DCIM doesn't cut it
+ if (!entries.isEmpty()) updatePath(root, entries, dbHelper);
+ }
+
+ private static void updatePath(
+ File root, HashMap<Integer, Entry> entries, WidgetDatabaseHelper dbHelper) {
+ File[] files = root.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory() && !entries.isEmpty()) {
+ String path = file.getAbsolutePath();
+ String oldPath = OLD_EXT_PATH + path.substring(RELATIVE_PATH_START);
+ int oldBucketId = GalleryUtils.getBucketId(oldPath);
+ Entry entry = entries.remove(oldBucketId);
+ if (entry != null) {
+ int newBucketId = GalleryUtils.getBucketId(path);
+ String newAlbumPath = Path.fromString(entry.albumPath)
+ .getParent()
+ .getChild(newBucketId)
+ .toString();
+ Log.d(TAG, "migrate from " + entry.albumPath + " to " + newAlbumPath);
+ entry.albumPath = newAlbumPath;
+ dbHelper.updateEntry(entry);
+ }
+ updatePath(file, entries, dbHelper); // recursion
+ }
+ }
+ }
+ }
+}