blob: a01cc479b7d2c4e23a8f8daa782714bb034f0023 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 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
Joe Onoratoa5902522009-07-30 13:37:37 -070017package com.android.launcher2;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000019import android.app.SearchManager;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070020import android.appwidget.AppWidgetHost;
Mike Cleronb87bd162009-10-30 16:36:56 -070021import android.appwidget.AppWidgetManager;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000022import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080023import android.content.ComponentName;
Adam Cohen228da5a2011-07-27 22:23:47 -070024import android.content.ContentProvider;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.ContentResolver;
Adam Cohen228da5a2011-07-27 22:23:47 -070026import android.content.ContentUris;
27import android.content.ContentValues;
28import android.content.Context;
29import android.content.Intent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.pm.ActivityInfo;
Adam Cohen228da5a2011-07-27 22:23:47 -070031import android.content.pm.PackageManager;
32import android.content.res.Resources;
33import android.content.res.TypedArray;
34import android.content.res.XmlResourceParser;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080035import android.database.Cursor;
36import android.database.SQLException;
Adam Cohen228da5a2011-07-27 22:23:47 -070037import android.database.sqlite.SQLiteDatabase;
38import android.database.sqlite.SQLiteOpenHelper;
39import android.database.sqlite.SQLiteQueryBuilder;
40import android.database.sqlite.SQLiteStatement;
Joe Onorato0589f0f2010-02-08 13:44:00 -080041import android.graphics.Bitmap;
42import android.graphics.BitmapFactory;
Adam Cohen228da5a2011-07-27 22:23:47 -070043import android.net.Uri;
44import android.provider.Settings;
45import android.text.TextUtils;
46import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import android.util.Log;
48import android.util.Xml;
Adam Cohen228da5a2011-07-27 22:23:47 -070049
50import com.android.internal.util.XmlUtils;
51import com.android.launcher.R;
52import com.android.launcher2.LauncherSettings.Favorites;
53
54import org.xmlpull.v1.XmlPullParser;
55import org.xmlpull.v1.XmlPullParserException;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080056
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070058import java.net.URISyntaxException;
Adam Cohen228da5a2011-07-27 22:23:47 -070059import java.util.ArrayList;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000060import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080061
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080063 private static final String TAG = "Launcher.LauncherProvider";
64 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065
66 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070067
68 private static final int DATABASE_VERSION = 9;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080069
Joe Onoratoa5902522009-07-30 13:37:37 -070070 static final String AUTHORITY = "com.android.launcher2.settings";
Winson Chung3d503fb2011-07-13 17:25:49 -070071
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072 static final String TABLE_FAVORITES = "favorites";
73 static final String PARAMETER_NOTIFY = "notify";
74
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070075 /**
Romain Guy73b979d2009-06-09 12:57:21 -070076 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070077 * {@link AppWidgetHost#deleteHost()} is called during database creation.
78 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
79 */
80 static final Uri CONTENT_APPWIDGET_RESET_URI =
81 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
82
Michael Jurkaa8c760d2011-04-28 14:59:33 -070083 private DatabaseHelper mOpenHelper;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084
85 @Override
86 public boolean onCreate() {
87 mOpenHelper = new DatabaseHelper(getContext());
Michael Jurkaa8c760d2011-04-28 14:59:33 -070088 ((LauncherApplication) getContext()).setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080089 return true;
90 }
91
92 @Override
93 public String getType(Uri uri) {
94 SqlArguments args = new SqlArguments(uri, null, null);
95 if (TextUtils.isEmpty(args.where)) {
96 return "vnd.android.cursor.dir/" + args.table;
97 } else {
98 return "vnd.android.cursor.item/" + args.table;
99 }
100 }
101
102 @Override
103 public Cursor query(Uri uri, String[] projection, String selection,
104 String[] selectionArgs, String sortOrder) {
105
106 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
107 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
108 qb.setTables(args.table);
109
Romain Guy73b979d2009-06-09 12:57:21 -0700110 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800111 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
112 result.setNotificationUri(getContext().getContentResolver(), uri);
113
114 return result;
115 }
116
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700117 private static long dbInsertAndCheck(DatabaseHelper helper,
118 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
119 if (!values.containsKey(LauncherSettings.Favorites._ID)) {
120 throw new RuntimeException("Error: attempting to add item without specifying an id");
121 }
122 return db.insert(table, nullColumnHack, values);
123 }
124
Adam Cohen228da5a2011-07-27 22:23:47 -0700125 private static void deleteId(SQLiteDatabase db, long id) {
126 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
127 SqlArguments args = new SqlArguments(uri, null, null);
128 db.delete(args.table, args.where, args.args);
129 }
130
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800131 @Override
132 public Uri insert(Uri uri, ContentValues initialValues) {
133 SqlArguments args = new SqlArguments(uri);
134
135 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700136 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800137 if (rowId <= 0) return null;
138
139 uri = ContentUris.withAppendedId(uri, rowId);
140 sendNotify(uri);
141
142 return uri;
143 }
144
145 @Override
146 public int bulkInsert(Uri uri, ContentValues[] values) {
147 SqlArguments args = new SqlArguments(uri);
148
149 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
150 db.beginTransaction();
151 try {
152 int numValues = values.length;
153 for (int i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700154 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
155 return 0;
156 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800157 }
158 db.setTransactionSuccessful();
159 } finally {
160 db.endTransaction();
161 }
162
163 sendNotify(uri);
164 return values.length;
165 }
166
167 @Override
168 public int delete(Uri uri, String selection, String[] selectionArgs) {
169 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
170
171 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
172 int count = db.delete(args.table, args.where, args.args);
173 if (count > 0) sendNotify(uri);
174
175 return count;
176 }
177
178 @Override
179 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
180 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
181
182 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
183 int count = db.update(args.table, values, args.where, args.args);
184 if (count > 0) sendNotify(uri);
185
186 return count;
187 }
188
189 private void sendNotify(Uri uri) {
190 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
191 if (notify == null || "true".equals(notify)) {
192 getContext().getContentResolver().notifyChange(uri, null);
193 }
194 }
195
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700196 public long generateNewId() {
197 return mOpenHelper.generateNewId();
198 }
199
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800200 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800201 private static final String TAG_FAVORITES = "favorites";
202 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700203 private static final String TAG_CLOCK = "clock";
204 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700205 private static final String TAG_APPWIDGET = "appwidget";
206 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700207 private static final String TAG_FOLDER = "folder";
Winson Chung3d503fb2011-07-13 17:25:49 -0700208
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800209 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700210 private final AppWidgetHost mAppWidgetHost;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700211 private long mMaxId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212
213 DatabaseHelper(Context context) {
214 super(context, DATABASE_NAME, null, DATABASE_VERSION);
215 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700216 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700217
218 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
219 // the DB here
220 if (mMaxId == -1) {
221 mMaxId = initializeMaxId(getWritableDatabase());
222 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800223 }
224
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700225 /**
226 * Send notification that we've deleted the {@link AppWidgetHost},
227 * probably as part of the initial database creation. The receiver may
228 * want to re-call {@link AppWidgetHost#startListening()} to ensure
229 * callbacks are correctly set.
230 */
231 private void sendAppWidgetResetNotify() {
232 final ContentResolver resolver = mContext.getContentResolver();
233 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
234 }
235
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800236 @Override
237 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800238 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700239
240 mMaxId = 1;
241
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800242 db.execSQL("CREATE TABLE favorites (" +
243 "_id INTEGER PRIMARY KEY," +
244 "title TEXT," +
245 "intent TEXT," +
246 "container INTEGER," +
247 "screen INTEGER," +
248 "cellX INTEGER," +
249 "cellY INTEGER," +
250 "spanX INTEGER," +
251 "spanY INTEGER," +
252 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700253 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800254 "isShortcut INTEGER," +
255 "iconType INTEGER," +
256 "iconPackage TEXT," +
257 "iconResource TEXT," +
258 "icon BLOB," +
259 "uri TEXT," +
260 "displayMode INTEGER" +
261 ");");
262
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700263 // Database was just created, so wipe any previous widgets
264 if (mAppWidgetHost != null) {
265 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700266 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800267 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700268
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800269 if (!convertDatabase(db)) {
270 // Populate favorites table with initial favorites
Winson Chung6d092682011-11-16 18:43:26 -0800271 loadFavorites(db, R.xml.default_workspace);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800272 }
273 }
274
275 private boolean convertDatabase(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800276 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277 boolean converted = false;
278
279 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
280 "/old_favorites?notify=true");
281 final ContentResolver resolver = mContext.getContentResolver();
282 Cursor cursor = null;
283
284 try {
285 cursor = resolver.query(uri, null, null, null, null);
286 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700287 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800288 }
289
290 // We already have a favorites database in the old provider
291 if (cursor != null && cursor.getCount() > 0) {
292 try {
293 converted = copyFromCursor(db, cursor) > 0;
294 } finally {
295 cursor.close();
296 }
297
298 if (converted) {
299 resolver.delete(uri, null, null);
300 }
301 }
302
303 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700304 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800305 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800306 convertWidgets(db);
307 }
308
309 return converted;
310 }
311
312 private int copyFromCursor(SQLiteDatabase db, Cursor c) {
Romain Guy73b979d2009-06-09 12:57:21 -0700313 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800314 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
315 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
316 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
317 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
318 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
319 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
320 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
321 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
322 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
323 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
324 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
325 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
326 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
327
328 ContentValues[] rows = new ContentValues[c.getCount()];
329 int i = 0;
330 while (c.moveToNext()) {
331 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700332 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800333 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
334 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
335 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
336 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
337 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
338 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
339 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
340 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700341 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800342 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
343 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
344 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
345 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
346 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
347 rows[i++] = values;
348 }
349
350 db.beginTransaction();
351 int total = 0;
352 try {
353 int numValues = rows.length;
354 for (i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700355 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800356 return 0;
357 } else {
358 total++;
359 }
360 }
361 db.setTransactionSuccessful();
362 } finally {
363 db.endTransaction();
364 }
365
366 return total;
367 }
368
369 @Override
370 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800371 if (LOGD) Log.d(TAG, "onUpgrade triggered");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800372
373 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700374 if (version < 3) {
375 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800376 db.beginTransaction();
377 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700378 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800379 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700380 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800381 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700382 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800383 } catch (SQLException ex) {
384 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800385 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800386 } finally {
387 db.endTransaction();
388 }
389
390 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700391 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800392 convertWidgets(db);
393 }
394 }
Romain Guy73b979d2009-06-09 12:57:21 -0700395
396 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800397 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700398 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800399
Romain Guy509cd6a2010-03-23 15:10:56 -0700400 // Where's version 5?
401 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
402 // - Passion shipped on 2.1 with version 6 of launcher2
403 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
404 // but version 5 on there was the updateContactsShortcuts change
405 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
406 // The updateContactsShortcuts change is idempotent, so running it twice
407 // is okay so we'll do that when upgrading the devices that shipped with it.
408 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800409 // We went from 3 to 5 screens. Move everything 1 to the right
410 db.beginTransaction();
411 try {
412 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
413 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800414 } catch (SQLException ex) {
415 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800416 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800417 } finally {
418 db.endTransaction();
419 }
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800420
Romain Guy509cd6a2010-03-23 15:10:56 -0700421 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800422 if (updateContactsShortcuts(db)) {
423 version = 6;
424 }
425 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000426
427 if (version < 7) {
428 // Version 7 gets rid of the special search widget.
429 convertWidgets(db);
430 version = 7;
431 }
432
Joe Onorato0589f0f2010-02-08 13:44:00 -0800433 if (version < 8) {
434 // Version 8 (froyo) has the icons all normalized. This should
435 // already be the case in practice, but we now rely on it and don't
436 // resample the images each time.
437 normalizeIcons(db);
438 version = 8;
439 }
440
Winson Chung3d503fb2011-07-13 17:25:49 -0700441 if (version < 9) {
442 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
443 // before it gets a change to get set, so we need to read it here when we use it)
444 if (mMaxId == -1) {
445 mMaxId = initializeMaxId(db);
446 }
447
448 // Add default hotseat icons
Winson Chung6d092682011-11-16 18:43:26 -0800449 loadFavorites(db, R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700450 version = 9;
451 }
452
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800453 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800454 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800455 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
456 onCreate(db);
457 }
458 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800459
460 private boolean updateContactsShortcuts(SQLiteDatabase db) {
461 Cursor c = null;
462 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
463 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
464
465 db.beginTransaction();
466 try {
467 // Select and iterate through each matching widget
468 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.INTENT },
469 selectWhere, null, null, null, null);
470
471 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
472
473 final ContentValues values = new ContentValues();
474 final int idIndex = c.getColumnIndex(Favorites._ID);
475 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
476
477 while (c != null && c.moveToNext()) {
478 long favoriteId = c.getLong(idIndex);
479 final String intentUri = c.getString(intentIndex);
480 if (intentUri != null) {
481 try {
482 Intent intent = Intent.parseUri(intentUri, 0);
483 android.util.Log.d("Home", intent.toString());
484 final Uri uri = intent.getData();
485 final String data = uri.toString();
486 if (Intent.ACTION_VIEW.equals(intent.getAction()) &&
487 (data.startsWith("content://contacts/people/") ||
488 data.startsWith("content://com.android.contacts/contacts/lookup/"))) {
489
490 intent = new Intent("com.android.contacts.action.QUICK_CONTACT");
491 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
492 Intent.FLAG_ACTIVITY_CLEAR_TOP |
493 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
494
495 intent.setData(uri);
496 intent.putExtra("mode", 3);
497 intent.putExtra("exclude_mimes", (String[]) null);
498
499 values.clear();
500 values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0));
501
502 String updateWhere = Favorites._ID + "=" + favoriteId;
503 db.update(TABLE_FAVORITES, values, updateWhere, null);
504 }
505 } catch (RuntimeException ex) {
506 Log.e(TAG, "Problem upgrading shortcut", ex);
507 } catch (URISyntaxException e) {
508 Log.e(TAG, "Problem upgrading shortcut", e);
509 }
510 }
511 }
512
513 db.setTransactionSuccessful();
514 } catch (SQLException ex) {
515 Log.w(TAG, "Problem while upgrading contacts", ex);
516 return false;
517 } finally {
518 db.endTransaction();
519 if (c != null) {
520 c.close();
521 }
522 }
523
524 return true;
525 }
526
Joe Onorato0589f0f2010-02-08 13:44:00 -0800527 private void normalizeIcons(SQLiteDatabase db) {
528 Log.d(TAG, "normalizing icons");
529
Joe Onorato346e1292010-02-18 10:34:24 -0500530 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800531 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400532 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800533 try {
534 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400535 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600536 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800537
538 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
539 Favorites.ICON_TYPE_BITMAP, null);
540
541 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
542 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
543
544 while (c.moveToNext()) {
545 long id = c.getLong(idIndex);
546 byte[] data = c.getBlob(iconIndex);
547 try {
548 Bitmap bitmap = Utilities.resampleIconBitmap(
549 BitmapFactory.decodeByteArray(data, 0, data.length),
550 mContext);
551 if (bitmap != null) {
552 update.bindLong(1, id);
553 data = ItemInfo.flattenBitmap(bitmap);
554 if (data != null) {
555 update.bindBlob(2, data);
556 update.execute();
557 }
558 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800559 }
560 } catch (Exception e) {
561 if (!logged) {
562 Log.e(TAG, "Failed normalizing icon " + id, e);
563 } else {
564 Log.e(TAG, "Also failed normalizing icon " + id);
565 }
566 logged = true;
567 }
568 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000569 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800570 } catch (SQLException ex) {
571 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
572 } finally {
573 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400574 if (update != null) {
575 update.close();
576 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800577 if (c != null) {
578 c.close();
579 }
580 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700581 }
582
583 // Generates a new ID to use for an object in your database. This method should be only
584 // called from the main UI thread. As an exception, we do call it when we call the
585 // constructor from the worker thread; however, this doesn't extend until after the
586 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
587 // after that point
588 public long generateNewId() {
589 if (mMaxId < 0) {
590 throw new RuntimeException("Error: max id was not initialized");
591 }
592 mMaxId += 1;
593 return mMaxId;
594 }
595
596 private long initializeMaxId(SQLiteDatabase db) {
597 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
598
599 // get the result
600 final int maxIdIndex = 0;
601 long id = -1;
602 if (c != null && c.moveToNext()) {
603 id = c.getLong(maxIdIndex);
604 }
Michael Jurka5130e402011-10-13 04:55:35 -0700605 if (c != null) {
606 c.close();
607 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700608
609 if (id == -1) {
610 throw new RuntimeException("Error: could not query max id");
611 }
612
613 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800614 }
615
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800616 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700617 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000618 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800619 */
620 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000621 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800622 final int[] bindSources = new int[] {
623 Favorites.ITEM_TYPE_WIDGET_CLOCK,
624 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000625 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800626 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000627
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800628 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
629
630 Cursor c = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800631
632 db.beginTransaction();
633 try {
634 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000635 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800636 selectWhere, null, null, null, null);
637
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800638 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800639
640 final ContentValues values = new ContentValues();
641 while (c != null && c.moveToNext()) {
642 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000643 int favoriteType = c.getInt(1);
644
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700645 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800646 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700647 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800648
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800649 if (LOGD) {
650 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
651 + " for favoriteId=" + favoriteId);
652 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800653 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000654 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
655 values.put(Favorites.APPWIDGET_ID, appWidgetId);
656
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800657 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +0000658 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
659 values.put(LauncherSettings.Favorites.SPANX, 4);
660 values.put(LauncherSettings.Favorites.SPANY, 1);
661 } else {
662 values.put(LauncherSettings.Favorites.SPANX, 2);
663 values.put(LauncherSettings.Favorites.SPANY, 2);
664 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800665
666 String updateWhere = Favorites._ID + "=" + favoriteId;
667 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +0000668
Bjorn Bringert34251342009-12-15 13:33:11 +0000669 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
670 appWidgetManager.bindAppWidgetId(appWidgetId,
671 new ComponentName("com.android.alarmclock",
672 "com.android.alarmclock.AnalogAppWidgetProvider"));
673 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
674 appWidgetManager.bindAppWidgetId(appWidgetId,
675 new ComponentName("com.android.camera",
676 "com.android.camera.PhotoAppWidgetProvider"));
677 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
678 appWidgetManager.bindAppWidgetId(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000679 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +0000680 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800681 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800682 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800683 }
684 }
685
686 db.setTransactionSuccessful();
687 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800688 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800689 } finally {
690 db.endTransaction();
691 if (c != null) {
692 c.close();
693 }
694 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800695 }
696
697 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800698 * Loads the default set of favorite packages from an xml file.
699 *
700 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -0700701 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800702 */
Winson Chung6d092682011-11-16 18:43:26 -0800703 private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800704 Intent intent = new Intent(Intent.ACTION_MAIN, null);
705 intent.addCategory(Intent.CATEGORY_LAUNCHER);
706 ContentValues values = new ContentValues();
707
708 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800709 int i = 0;
710 try {
Winson Chung6d092682011-11-16 18:43:26 -0800711 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700712 AttributeSet attrs = Xml.asAttributeSet(parser);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800713 XmlUtils.beginDocument(parser, TAG_FAVORITES);
714
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700715 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800716
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700717 int type;
718 while (((type = parser.next()) != XmlPullParser.END_TAG ||
719 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
720
721 if (type != XmlPullParser.START_TAG) {
722 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800723 }
724
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700725 boolean added = false;
726 final String name = parser.getName();
727
728 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
729
Winson Chung3d503fb2011-07-13 17:25:49 -0700730 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
731 if (a.hasValue(R.styleable.Favorite_container)) {
732 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
733 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700734
Winson Chung6d092682011-11-16 18:43:26 -0800735 String screen = a.getString(R.styleable.Favorite_screen);
736 String x = a.getString(R.styleable.Favorite_x);
737 String y = a.getString(R.styleable.Favorite_y);
738
739 // If we are adding to the hotseat, the screen is used as the position in the
740 // hotseat. This screen can't be at position 0 because AllApps is in the
741 // zeroth position.
742 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
743 Hotseat.isAllAppsButtonRank(Integer.valueOf(screen))) {
744 throw new RuntimeException("Invalid screen position for hotseat item");
745 }
746
747 values.clear();
748 values.put(LauncherSettings.Favorites.CONTAINER, container);
749 values.put(LauncherSettings.Favorites.SCREEN, screen);
750 values.put(LauncherSettings.Favorites.CELLX, x);
751 values.put(LauncherSettings.Favorites.CELLY, y);
752
753 if (TAG_FAVORITE.equals(name)) {
754 long id = addAppShortcut(db, values, a, packageManager, intent);
755 added = id >= 0;
756 } else if (TAG_SEARCH.equals(name)) {
757 added = addSearchWidget(db, values);
758 } else if (TAG_CLOCK.equals(name)) {
759 added = addClockWidget(db, values);
760 } else if (TAG_APPWIDGET.equals(name)) {
761 added = addAppWidget(db, values, a, packageManager);
762 } else if (TAG_SHORTCUT.equals(name)) {
763 long id = addUriShortcut(db, values, a);
764 added = id >= 0;
765 } else if (TAG_FOLDER.equals(name)) {
766 String title;
767 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
768 if (titleResId != -1) {
769 title = mContext.getResources().getString(titleResId);
770 } else {
771 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -0700772 }
Winson Chung6d092682011-11-16 18:43:26 -0800773 values.put(LauncherSettings.Favorites.TITLE, title);
774 long folderId = addFolder(db, values);
775 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -0700776
Winson Chung6d092682011-11-16 18:43:26 -0800777 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -0700778
Winson Chung6d092682011-11-16 18:43:26 -0800779 int folderDepth = parser.getDepth();
780 while ((type = parser.next()) != XmlPullParser.END_TAG ||
781 parser.getDepth() > folderDepth) {
782 if (type != XmlPullParser.START_TAG) {
783 continue;
784 }
785 final String folder_item_name = parser.getName();
786
787 TypedArray ar = mContext.obtainStyledAttributes(attrs,
788 R.styleable.Favorite);
789 values.clear();
790 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
791
792 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
793 long id =
794 addAppShortcut(db, values, ar, packageManager, intent);
795 if (id >= 0) {
796 folderItems.add(id);
797 }
798 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
799 long id = addUriShortcut(db, values, ar);
800 if (id >= 0) {
801 folderItems.add(id);
802 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700803 } else {
Winson Chung6d092682011-11-16 18:43:26 -0800804 throw new RuntimeException("Folders can " +
805 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -0700806 }
Winson Chung6d092682011-11-16 18:43:26 -0800807 ar.recycle();
808 }
809 // We can only have folders with >= 2 items, so we need to remove the
810 // folder and clean up if less than 2 items were included, or some
811 // failed to add, and less than 2 were actually added
812 if (folderItems.size() < 2 && folderId >= 0) {
813 // We just delete the folder and any items that made it
814 deleteId(db, folderId);
815 if (folderItems.size() > 0) {
816 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -0700817 }
Winson Chung6d092682011-11-16 18:43:26 -0800818 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -0700819 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800820 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700821 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700822 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800823 }
824 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800825 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800826 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800827 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -0700828 } catch (RuntimeException e) {
829 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800830 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700831
832 return i;
833 }
834
Adam Cohen228da5a2011-07-27 22:23:47 -0700835 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700836 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -0700837 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700838 ActivityInfo info;
839 String packageName = a.getString(R.styleable.Favorite_packageName);
840 String className = a.getString(R.styleable.Favorite_className);
841 try {
Romain Guy693599f2010-03-23 10:58:18 -0700842 ComponentName cn;
843 try {
844 cn = new ComponentName(packageName, className);
845 info = packageManager.getActivityInfo(cn, 0);
846 } catch (PackageManager.NameNotFoundException nnfe) {
847 String[] packages = packageManager.currentToCanonicalPackageNames(
848 new String[] { packageName });
849 cn = new ComponentName(packages[0], className);
850 info = packageManager.getActivityInfo(cn, 0);
851 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700852 id = generateNewId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700853 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -0700854 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
855 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700856 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700857 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
858 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
859 values.put(Favorites.SPANX, 1);
860 values.put(Favorites.SPANY, 1);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700861 values.put(Favorites._ID, generateNewId());
Adam Cohen228da5a2011-07-27 22:23:47 -0700862 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
863 return -1;
864 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700865 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800866 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700867 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700868 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700869 return id;
870 }
871
872 private long addFolder(SQLiteDatabase db, ContentValues values) {
873 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
874 values.put(Favorites.SPANX, 1);
875 values.put(Favorites.SPANY, 1);
876 long id = generateNewId();
877 values.put(Favorites._ID, id);
878 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
879 return -1;
880 } else {
881 return id;
882 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700883 }
884
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000885 private ComponentName getSearchWidgetProvider() {
886 SearchManager searchManager =
887 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
888 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
889 if (searchComponent == null) return null;
890 return getProviderInPackage(searchComponent.getPackageName());
891 }
892
893 /**
894 * Gets an appwidget provider from the given package. If the package contains more than
895 * one appwidget provider, an arbitrary one is returned.
896 */
897 private ComponentName getProviderInPackage(String packageName) {
898 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
899 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
900 if (providers == null) return null;
901 final int providerCount = providers.size();
902 for (int i = 0; i < providerCount; i++) {
903 ComponentName provider = providers.get(i).provider;
904 if (provider != null && provider.getPackageName().equals(packageName)) {
905 return provider;
906 }
907 }
908 return null;
909 }
910
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700911 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000912 ComponentName cn = getSearchWidgetProvider();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000913 return addAppWidget(db, values, cn, 4, 1);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700914 }
915
916 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000917 ComponentName cn = new ComponentName("com.android.alarmclock",
918 "com.android.alarmclock.AnalogAppWidgetProvider");
919 return addAppWidget(db, values, cn, 2, 2);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800920 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700921
Romain Guy693599f2010-03-23 10:58:18 -0700922 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
923 PackageManager packageManager) {
924
Mike Cleronb87bd162009-10-30 16:36:56 -0700925 String packageName = a.getString(R.styleable.Favorite_packageName);
926 String className = a.getString(R.styleable.Favorite_className);
927
928 if (packageName == null || className == null) {
929 return false;
930 }
Romain Guy693599f2010-03-23 10:58:18 -0700931
932 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -0700933 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -0700934 try {
935 packageManager.getReceiverInfo(cn, 0);
936 } catch (Exception e) {
937 String[] packages = packageManager.currentToCanonicalPackageNames(
938 new String[] { packageName });
939 cn = new ComponentName(packages[0], className);
940 try {
941 packageManager.getReceiverInfo(cn, 0);
942 } catch (Exception e1) {
943 hasPackage = false;
944 }
945 }
946
947 if (hasPackage) {
948 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
949 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
950 return addAppWidget(db, values, cn, spanX, spanY);
951 }
952
953 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +0000954 }
955
956 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
957 int spanX, int spanY) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700958 boolean allocatedAppWidgets = false;
959 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
960
961 try {
962 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
963
964 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000965 values.put(Favorites.SPANX, spanX);
966 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -0700967 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700968 values.put(Favorites._ID, generateNewId());
969 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700970
971 allocatedAppWidgets = true;
972
973 appWidgetManager.bindAppWidgetId(appWidgetId, cn);
974 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800975 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -0700976 }
977
978 return allocatedAppWidgets;
979 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700980
981 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -0700982 TypedArray a) {
983 Resources r = mContext.getResources();
984
985 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
986 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
987
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800988 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -0700989 String uri = null;
990 try {
991 uri = a.getString(R.styleable.Favorite_uri);
992 intent = Intent.parseUri(uri, 0);
993 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800994 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -0700995 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -0700996 }
997
998 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800999 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001000 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001001 }
1002
Adam Cohen228da5a2011-07-27 22:23:47 -07001003 long id = generateNewId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001004 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1005 values.put(Favorites.INTENT, intent.toUri(0));
1006 values.put(Favorites.TITLE, r.getString(titleResId));
1007 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1008 values.put(Favorites.SPANX, 1);
1009 values.put(Favorites.SPANY, 1);
1010 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1011 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1012 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001013 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001014
Adam Cohen228da5a2011-07-27 22:23:47 -07001015 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1016 return -1;
1017 }
1018 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001019 }
1020 }
1021
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001022 /**
1023 * Build a query string that will match any row where the column matches
1024 * anything in the values list.
1025 */
1026 static String buildOrWhereString(String column, int[] values) {
1027 StringBuilder selectWhere = new StringBuilder();
1028 for (int i = values.length - 1; i >= 0; i--) {
1029 selectWhere.append(column).append("=").append(values[i]);
1030 if (i > 0) {
1031 selectWhere.append(" OR ");
1032 }
1033 }
1034 return selectWhere.toString();
1035 }
1036
1037 static class SqlArguments {
1038 public final String table;
1039 public final String where;
1040 public final String[] args;
1041
1042 SqlArguments(Uri url, String where, String[] args) {
1043 if (url.getPathSegments().size() == 1) {
1044 this.table = url.getPathSegments().get(0);
1045 this.where = where;
1046 this.args = args;
1047 } else if (url.getPathSegments().size() != 2) {
1048 throw new IllegalArgumentException("Invalid URI: " + url);
1049 } else if (!TextUtils.isEmpty(where)) {
1050 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1051 } else {
1052 this.table = url.getPathSegments().get(0);
1053 this.where = "_id=" + ContentUris.parseId(url);
1054 this.args = null;
1055 }
1056 }
1057
1058 SqlArguments(Uri url) {
1059 if (url.getPathSegments().size() == 1) {
1060 table = url.getPathSegments().get(0);
1061 where = null;
1062 args = null;
1063 } else {
1064 throw new IllegalArgumentException("Invalid URI: " + url);
1065 }
1066 }
1067 }
1068}