1/*
2 * Copyright (C) 2009 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.camera;
18
19import com.android.gallery.R;
20
21import android.appwidget.AppWidgetManager;
22import android.appwidget.AppWidgetProvider;
23import android.content.ContentValues;
24import android.content.Context;
25import android.database.Cursor;
26import android.database.sqlite.SQLiteDatabase;
27import android.database.sqlite.SQLiteException;
28import android.database.sqlite.SQLiteOpenHelper;
29import android.graphics.Bitmap;
30import android.graphics.BitmapFactory;
31import android.util.Log;
32import android.widget.RemoteViews;
33
34import java.io.ByteArrayOutputStream;
35import java.io.IOException;
36
37/**
38 * Simple widget to show a user-selected picture.
39 */
40public class PhotoAppWidgetProvider extends AppWidgetProvider {
41    private static final String TAG = "PhotoAppWidgetProvider";
42    private static final boolean LOGD = true;
43
44    @Override
45    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
46                         int[] appWidgetIds) {
47        // Update each requested appWidgetId with its unique photo
48        PhotoDatabaseHelper helper = new PhotoDatabaseHelper(context);
49        for (int appWidgetId : appWidgetIds) {
50            int[] specificAppWidget = new int[] { appWidgetId };
51            RemoteViews views = buildUpdate(context, appWidgetId, helper);
52            if (LOGD) {
53                Log.d(TAG, "sending out views=" + views
54                        + " for id=" + appWidgetId);
55            }
56            appWidgetManager.updateAppWidget(specificAppWidget, views);
57        }
58        helper.close();
59    }
60
61    @Override
62    public void onDeleted(Context context, int[] appWidgetIds) {
63        // Clean deleted photos out of our database
64        PhotoDatabaseHelper helper = new PhotoDatabaseHelper(context);
65        for (int appWidgetId : appWidgetIds) {
66            helper.deletePhoto(appWidgetId);
67        }
68        helper.close();
69    }
70
71    /**
72     * Load photo for given widget and build {@link RemoteViews} for it.
73     */
74    static RemoteViews buildUpdate(Context context, int appWidgetId,
75                                   PhotoDatabaseHelper helper) {
76        RemoteViews views = null;
77        Bitmap bitmap = helper.getPhoto(appWidgetId);
78        if (bitmap != null) {
79            views = new RemoteViews(context.getPackageName(),
80                                    R.layout.photo_frame);
81            views.setImageViewBitmap(R.id.photo, bitmap);
82        }
83        return views;
84    }
85
86    static class PhotoDatabaseHelper extends SQLiteOpenHelper {
87        private static final String DATABASE_NAME = "launcher.db";
88
89        private static final int DATABASE_VERSION = 2;
90
91        static final String TABLE_PHOTOS = "photos";
92        static final String FIELD_APPWIDGET_ID = "appWidgetId";
93        static final String FIELD_PHOTO_BLOB = "photoBlob";
94
95        PhotoDatabaseHelper(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_PHOTOS + " (" +
102                    FIELD_APPWIDGET_ID + " INTEGER PRIMARY KEY," +
103                    FIELD_PHOTO_BLOB + " BLOB" +
104                    ");");
105        }
106
107        @Override
108        public void onUpgrade(SQLiteDatabase db, int oldVersion,
109                              int newVersion) {
110            int version = oldVersion;
111
112            if (version != DATABASE_VERSION) {
113                Log.w(TAG, "Destroying all old data.");
114                db.execSQL("DROP TABLE IF EXISTS " + TABLE_PHOTOS);
115                onCreate(db);
116            }
117        }
118
119        /**
120         * Store the given bitmap in this database for the given appWidgetId.
121         */
122        public boolean setPhoto(int appWidgetId, Bitmap bitmap) {
123            boolean success = false;
124            try {
125                // Try go guesstimate how much space the icon will take when
126                // serialized to avoid unnecessary allocations/copies during
127                // the write.
128                int size = bitmap.getWidth() * bitmap.getHeight() * 4;
129                ByteArrayOutputStream out = new ByteArrayOutputStream(size);
130                bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
131                out.flush();
132                out.close();
133
134                ContentValues values = new ContentValues();
135                values.put(PhotoDatabaseHelper.FIELD_APPWIDGET_ID, appWidgetId);
136                values.put(PhotoDatabaseHelper.FIELD_PHOTO_BLOB,
137                           out.toByteArray());
138
139                SQLiteDatabase db = getWritableDatabase();
140                db.insertOrThrow(PhotoDatabaseHelper.TABLE_PHOTOS, null,
141                                 values);
142
143                success = true;
144            } catch (SQLiteException e) {
145                Log.e(TAG, "Could not open database", e);
146            } catch (IOException e) {
147                Log.e(TAG, "Could not serialize photo", e);
148            }
149            if (LOGD) {
150                Log.d(TAG, "setPhoto success=" + success);
151            }
152            return success;
153        }
154
155        static final String[] PHOTOS_PROJECTION = {
156            FIELD_PHOTO_BLOB,
157        };
158
159        static final int INDEX_PHOTO_BLOB = 0;
160
161        /**
162         * Inflate and return a bitmap for the given appWidgetId.
163         */
164        public Bitmap getPhoto(int appWidgetId) {
165            Cursor c = null;
166            Bitmap bitmap = null;
167            try {
168                SQLiteDatabase db = getReadableDatabase();
169                String selection = String.format("%s=%d", FIELD_APPWIDGET_ID,
170                                                 appWidgetId);
171                c = db.query(TABLE_PHOTOS, PHOTOS_PROJECTION, selection, null,
172                        null, null, null, null);
173
174                if (c != null && LOGD) {
175                    Log.d(TAG, "getPhoto query count=" + c.getCount());
176                }
177
178                if (c != null && c.moveToFirst()) {
179                    byte[] data = c.getBlob(INDEX_PHOTO_BLOB);
180                    if (data != null) {
181                        bitmap = BitmapFactory.decodeByteArray(data, 0,
182                                data.length);
183                    }
184                }
185            } catch (SQLiteException e) {
186                Log.e(TAG, "Could not load photo from database", e);
187            } finally {
188                if (c != null) {
189                    c.close();
190                }
191            }
192            return bitmap;
193        }
194
195        /**
196         * Remove any bitmap associated with the given appWidgetId.
197         */
198        public void deletePhoto(int appWidgetId) {
199            try {
200                SQLiteDatabase db = getWritableDatabase();
201                String whereClause = String.format("%s=%d", FIELD_APPWIDGET_ID,
202                                                   appWidgetId);
203                db.delete(TABLE_PHOTOS, whereClause, null);
204            } catch (SQLiteException e) {
205                Log.e(TAG, "Could not delete photo from database", e);
206            }
207        }
208    }
209
210}
211
212