1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.gadget;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.database.sqlite.SQLiteDatabase;
23import android.database.sqlite.SQLiteException;
24import android.database.sqlite.SQLiteOpenHelper;
25import android.graphics.Bitmap;
26import android.net.Uri;
27import android.util.Log;
28
29import com.android.gallery3d.common.Utils;
30
31import java.io.ByteArrayOutputStream;
32import java.util.ArrayList;
33import java.util.List;
34
35public class WidgetDatabaseHelper extends SQLiteOpenHelper {
36    private static final String TAG = "PhotoDatabaseHelper";
37    private static final String DATABASE_NAME = "launcher.db";
38
39    // Increment the database version to 5. In version 5, we
40    // add a column in widgets table to record relative paths.
41    private static final int DATABASE_VERSION = 5;
42
43    private static final String TABLE_WIDGETS = "widgets";
44
45    private static final String FIELD_APPWIDGET_ID = "appWidgetId";
46    private static final String FIELD_IMAGE_URI = "imageUri";
47    private static final String FIELD_PHOTO_BLOB = "photoBlob";
48    private static final String FIELD_WIDGET_TYPE = "widgetType";
49    private static final String FIELD_ALBUM_PATH = "albumPath";
50    private static final String FIELD_RELATIVE_PATH = "relativePath";
51
52    public static final int TYPE_SINGLE_PHOTO = 0;
53    public static final int TYPE_SHUFFLE = 1;
54    public static final int TYPE_ALBUM = 2;
55
56    private static final String[] PROJECTION = {
57            FIELD_WIDGET_TYPE, FIELD_IMAGE_URI, FIELD_PHOTO_BLOB, FIELD_ALBUM_PATH,
58            FIELD_APPWIDGET_ID, FIELD_RELATIVE_PATH};
59    private static final int INDEX_WIDGET_TYPE = 0;
60    private static final int INDEX_IMAGE_URI = 1;
61    private static final int INDEX_PHOTO_BLOB = 2;
62    private static final int INDEX_ALBUM_PATH = 3;
63    private static final int INDEX_APPWIDGET_ID = 4;
64    private static final int INDEX_RELATIVE_PATH = 5;
65    private static final String WHERE_APPWIDGET_ID = FIELD_APPWIDGET_ID + " = ?";
66    private static final String WHERE_WIDGET_TYPE = FIELD_WIDGET_TYPE + " = ?";
67
68    public static class Entry {
69        public int widgetId;
70        public int type;
71        public String imageUri;
72        public byte imageData[];
73        public String albumPath;
74        public String relativePath;
75
76        private Entry() {}
77
78        private Entry(int id, Cursor cursor) {
79            widgetId = id;
80            type = cursor.getInt(INDEX_WIDGET_TYPE);
81            if (type == TYPE_SINGLE_PHOTO) {
82                imageUri = cursor.getString(INDEX_IMAGE_URI);
83                imageData = cursor.getBlob(INDEX_PHOTO_BLOB);
84            } else if (type == TYPE_ALBUM) {
85                albumPath = cursor.getString(INDEX_ALBUM_PATH);
86                relativePath = cursor.getString(INDEX_RELATIVE_PATH);
87            }
88        }
89
90        private Entry(Cursor cursor) {
91            this(cursor.getInt(INDEX_APPWIDGET_ID), cursor);
92        }
93    }
94
95    public WidgetDatabaseHelper(Context context) {
96        super(context, DATABASE_NAME, null, DATABASE_VERSION);
97    }
98
99    @Override
100    public void onCreate(SQLiteDatabase db) {
101        db.execSQL("CREATE TABLE " + TABLE_WIDGETS + " ("
102                + FIELD_APPWIDGET_ID + " INTEGER PRIMARY KEY, "
103                + FIELD_WIDGET_TYPE + " INTEGER DEFAULT 0, "
104                + FIELD_IMAGE_URI + " TEXT, "
105                + FIELD_ALBUM_PATH + " TEXT, "
106                + FIELD_PHOTO_BLOB + " BLOB, "
107                + FIELD_RELATIVE_PATH + " TEXT)");
108    }
109
110    private void saveData(SQLiteDatabase db, int oldVersion, ArrayList<Entry> data) {
111        if (oldVersion <= 2) {
112            Cursor cursor = db.query("photos",
113                    new String[] {FIELD_APPWIDGET_ID, FIELD_PHOTO_BLOB},
114                    null, null, null, null, null);
115            if (cursor == null) return;
116            try {
117                while (cursor.moveToNext()) {
118                    Entry entry = new Entry();
119                    entry.type = TYPE_SINGLE_PHOTO;
120                    entry.widgetId = cursor.getInt(0);
121                    entry.imageData = cursor.getBlob(1);
122                    data.add(entry);
123                }
124            } finally {
125                cursor.close();
126            }
127        } else if (oldVersion == 3) {
128            Cursor cursor = db.query("photos",
129                    new String[] {FIELD_APPWIDGET_ID, FIELD_PHOTO_BLOB, FIELD_IMAGE_URI},
130                    null, null, null, null, null);
131            if (cursor == null) return;
132            try {
133                while (cursor.moveToNext()) {
134                    Entry entry = new Entry();
135                    entry.type = TYPE_SINGLE_PHOTO;
136                    entry.widgetId = cursor.getInt(0);
137                    entry.imageData = cursor.getBlob(1);
138                    entry.imageUri = cursor.getString(2);
139                    data.add(entry);
140                }
141            } finally {
142                cursor.close();
143            }
144        }
145    }
146
147    private void restoreData(SQLiteDatabase db, ArrayList<Entry> data) {
148        db.beginTransaction();
149        try {
150            for (Entry entry : data) {
151                ContentValues values = new ContentValues();
152                values.put(FIELD_APPWIDGET_ID, entry.widgetId);
153                values.put(FIELD_WIDGET_TYPE, entry.type);
154                values.put(FIELD_IMAGE_URI, entry.imageUri);
155                values.put(FIELD_PHOTO_BLOB, entry.imageData);
156                values.put(FIELD_ALBUM_PATH, entry.albumPath);
157                db.insert(TABLE_WIDGETS, null, values);
158            }
159            db.setTransactionSuccessful();
160        } finally {
161            db.endTransaction();
162        }
163    }
164
165    @Override
166    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
167        if (oldVersion < 4) {
168            // Table "photos" is renamed to "widget" in version 4
169            ArrayList<Entry> data = new ArrayList<Entry>();
170            saveData(db, oldVersion, data);
171
172            Log.w(TAG, "destroying all old data.");
173            db.execSQL("DROP TABLE IF EXISTS photos");
174            db.execSQL("DROP TABLE IF EXISTS " + TABLE_WIDGETS);
175            onCreate(db);
176
177            restoreData(db, data);
178        }
179        // Add a column for relative path
180        if (oldVersion < DATABASE_VERSION) {
181            try {
182                db.execSQL("ALTER TABLE widgets ADD COLUMN relativePath TEXT");
183            } catch (Throwable t) {
184                Log.e(TAG, "Failed to add the column for relative path.");
185                return;
186            }
187        }
188    }
189
190    /**
191     * Store the given bitmap in this database for the given appWidgetId.
192     */
193    public boolean setPhoto(int appWidgetId, Uri imageUri, Bitmap bitmap) {
194        try {
195            // Try go guesstimate how much space the icon will take when
196            // serialized to avoid unnecessary allocations/copies during
197            // the write.
198            int size = bitmap.getWidth() * bitmap.getHeight() * 4;
199            ByteArrayOutputStream out = new ByteArrayOutputStream(size);
200            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
201            out.close();
202
203            ContentValues values = new ContentValues();
204            values.put(FIELD_APPWIDGET_ID, appWidgetId);
205            values.put(FIELD_WIDGET_TYPE, TYPE_SINGLE_PHOTO);
206            values.put(FIELD_IMAGE_URI, imageUri.toString());
207            values.put(FIELD_PHOTO_BLOB, out.toByteArray());
208
209            SQLiteDatabase db = getWritableDatabase();
210            db.replaceOrThrow(TABLE_WIDGETS, null, values);
211            return true;
212        } catch (Throwable e) {
213            Log.e(TAG, "set widget photo fail", e);
214            return false;
215        }
216    }
217
218    public boolean setWidget(int id, int type, String albumPath, String relativePath) {
219        try {
220            ContentValues values = new ContentValues();
221            values.put(FIELD_APPWIDGET_ID, id);
222            values.put(FIELD_WIDGET_TYPE, type);
223            values.put(FIELD_ALBUM_PATH, Utils.ensureNotNull(albumPath));
224            values.put(FIELD_RELATIVE_PATH, relativePath);
225            getWritableDatabase().replaceOrThrow(TABLE_WIDGETS, null, values);
226            return true;
227        } catch (Throwable e) {
228            Log.e(TAG, "set widget fail", e);
229            return false;
230        }
231    }
232
233    public Entry getEntry(int appWidgetId) {
234        Cursor cursor = null;
235        try {
236            SQLiteDatabase db = getReadableDatabase();
237            cursor = db.query(TABLE_WIDGETS, PROJECTION,
238                    WHERE_APPWIDGET_ID, new String[] {String.valueOf(appWidgetId)},
239                    null, null, null);
240            if (cursor == null || !cursor.moveToNext()) {
241                Log.e(TAG, "query fail: empty cursor: " + cursor + " appWidgetId: "
242                        + appWidgetId);
243                return null;
244            }
245            return new Entry(appWidgetId, cursor);
246        } catch (Throwable e) {
247            Log.e(TAG, "Could not load photo from database", e);
248            return null;
249        } finally {
250            Utils.closeSilently(cursor);
251        }
252    }
253
254    public List<Entry> getEntries(int type) {
255        Cursor cursor = null;
256        try {
257            SQLiteDatabase db = getReadableDatabase();
258            cursor = db.query(TABLE_WIDGETS, PROJECTION,
259                    WHERE_WIDGET_TYPE, new String[] {String.valueOf(type)},
260                    null, null, null);
261            if (cursor == null) {
262                Log.e(TAG, "query fail: null cursor: " + cursor);
263                return null;
264            }
265            ArrayList<Entry> result = new ArrayList<Entry>(cursor.getCount());
266            while (cursor.moveToNext()) {
267                result.add(new Entry(cursor));
268            }
269            return result;
270        } catch (Throwable e) {
271            Log.e(TAG, "Could not load widget from database", e);
272            return null;
273        } finally {
274            Utils.closeSilently(cursor);
275        }
276    }
277
278    /**
279     * Updates the entry in the widget database.
280     */
281    public void updateEntry(Entry entry) {
282        deleteEntry(entry.widgetId);
283        try {
284            ContentValues values = new ContentValues();
285            values.put(FIELD_APPWIDGET_ID, entry.widgetId);
286            values.put(FIELD_WIDGET_TYPE, entry.type);
287            values.put(FIELD_ALBUM_PATH, entry.albumPath);
288            values.put(FIELD_IMAGE_URI, entry.imageUri);
289            values.put(FIELD_PHOTO_BLOB, entry.imageData);
290            values.put(FIELD_RELATIVE_PATH, entry.relativePath);
291            getWritableDatabase().insert(TABLE_WIDGETS, null, values);
292        } catch (Throwable e) {
293            Log.e(TAG, "set widget fail", e);
294        }
295    }
296
297    /**
298     * Remove any bitmap associated with the given appWidgetId.
299     */
300    public void deleteEntry(int appWidgetId) {
301        try {
302            SQLiteDatabase db = getWritableDatabase();
303            db.delete(TABLE_WIDGETS, WHERE_APPWIDGET_ID,
304                    new String[] {String.valueOf(appWidgetId)});
305        } catch (SQLiteException e) {
306            Log.e(TAG, "Could not delete photo from database", e);
307        }
308    }
309}
310