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 
17 package com.android.gallery3d.gadget;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.database.sqlite.SQLiteDatabase;
23 import android.database.sqlite.SQLiteException;
24 import android.database.sqlite.SQLiteOpenHelper;
25 import android.graphics.Bitmap;
26 import android.net.Uri;
27 import android.util.Log;
28 
29 import com.android.gallery3d.common.Utils;
30 
31 import java.io.ByteArrayOutputStream;
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 public 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 
Entry()76         private Entry() {}
77 
Entry(int id, Cursor cursor)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 
Entry(Cursor cursor)90         private Entry(Cursor cursor) {
91             this(cursor.getInt(INDEX_APPWIDGET_ID), cursor);
92         }
93     }
94 
WidgetDatabaseHelper(Context context)95     public WidgetDatabaseHelper(Context context) {
96         super(context, DATABASE_NAME, null, DATABASE_VERSION);
97     }
98 
99     @Override
onCreate(SQLiteDatabase db)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 
saveData(SQLiteDatabase db, int oldVersion, ArrayList<Entry> data)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 
restoreData(SQLiteDatabase db, ArrayList<Entry> data)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
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)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      */
setPhoto(int appWidgetId, Uri imageUri, Bitmap bitmap)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 
setWidget(int id, int type, String albumPath, String relativePath)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 
getEntry(int appWidgetId)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 
getEntries(int type)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      */
updateEntry(Entry entry)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      */
deleteEntry(int appWidgetId)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