blob: 6cec101ab1fd3d94a28fbd36a80c656aceea5414 [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 Chung3d503fb2011-07-13 17:25:49 -0700271 loadFavorites(db, ItemInfo.NO_ID);
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
449 loadFavorites(db, LauncherSettings.Favorites.CONTAINER_HOTSEAT);
450 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 Chung3d503fb2011-07-13 17:25:49 -0700703 private int loadFavorites(SQLiteDatabase db, long filterContainerId) {
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 {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700711 XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace);
712 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 }
734 if (filterContainerId == ItemInfo.NO_ID || filterContainerId == container) {
735 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);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700738
Winson Chung3d503fb2011-07-13 17:25:49 -0700739 // If we are adding to the hotset, the screen is used as the position in the
740 // hotset. This screen can't be at position 0 because AllApps is in the
741 // zeroth position.
742 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
Winson Chungf30ad5f2011-08-08 10:55:42 -0700743 Hotseat.isAllAppsButtonRank(Integer.valueOf(screen))) {
Winson Chung3d503fb2011-07-13 17:25:49 -0700744 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
Winson Chung3d503fb2011-07-13 17:25:49 -0700753 if (TAG_FAVORITE.equals(name)) {
Adam Cohen228da5a2011-07-27 22:23:47 -0700754 long id = addAppShortcut(db, values, a, packageManager, intent);
755 added = id >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -0700756 } 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)) {
Adam Cohen228da5a2011-07-27 22:23:47 -0700763 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);
772 }
773 values.put(LauncherSettings.Favorites.TITLE, title);
774 long folderId = addFolder(db, values);
775 added = folderId >= 0;
776
777 ArrayList<Long> folderItems = new ArrayList<Long>();
778
779 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 }
803 } else {
804 throw new RuntimeException("Folders can " +
805 "contain only shortcuts");
806 }
807 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));
817 }
818 added = false;
819 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700820 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800821 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700822 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700823 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800824 }
825 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800826 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800827 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800828 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -0700829 } catch (RuntimeException e) {
830 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800831 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700832
833 return i;
834 }
835
Adam Cohen228da5a2011-07-27 22:23:47 -0700836 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700837 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -0700838 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700839 ActivityInfo info;
840 String packageName = a.getString(R.styleable.Favorite_packageName);
841 String className = a.getString(R.styleable.Favorite_className);
842 try {
Romain Guy693599f2010-03-23 10:58:18 -0700843 ComponentName cn;
844 try {
845 cn = new ComponentName(packageName, className);
846 info = packageManager.getActivityInfo(cn, 0);
847 } catch (PackageManager.NameNotFoundException nnfe) {
848 String[] packages = packageManager.currentToCanonicalPackageNames(
849 new String[] { packageName });
850 cn = new ComponentName(packages[0], className);
851 info = packageManager.getActivityInfo(cn, 0);
852 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700853 id = generateNewId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700854 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -0700855 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
856 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700857 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700858 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
859 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
860 values.put(Favorites.SPANX, 1);
861 values.put(Favorites.SPANY, 1);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700862 values.put(Favorites._ID, generateNewId());
Adam Cohen228da5a2011-07-27 22:23:47 -0700863 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
864 return -1;
865 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700866 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800867 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700868 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700869 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700870 return id;
871 }
872
873 private long addFolder(SQLiteDatabase db, ContentValues values) {
874 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
875 values.put(Favorites.SPANX, 1);
876 values.put(Favorites.SPANY, 1);
877 long id = generateNewId();
878 values.put(Favorites._ID, id);
879 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
880 return -1;
881 } else {
882 return id;
883 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700884 }
885
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000886 private ComponentName getSearchWidgetProvider() {
887 SearchManager searchManager =
888 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
889 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
890 if (searchComponent == null) return null;
891 return getProviderInPackage(searchComponent.getPackageName());
892 }
893
894 /**
895 * Gets an appwidget provider from the given package. If the package contains more than
896 * one appwidget provider, an arbitrary one is returned.
897 */
898 private ComponentName getProviderInPackage(String packageName) {
899 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
900 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
901 if (providers == null) return null;
902 final int providerCount = providers.size();
903 for (int i = 0; i < providerCount; i++) {
904 ComponentName provider = providers.get(i).provider;
905 if (provider != null && provider.getPackageName().equals(packageName)) {
906 return provider;
907 }
908 }
909 return null;
910 }
911
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700912 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000913 ComponentName cn = getSearchWidgetProvider();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000914 return addAppWidget(db, values, cn, 4, 1);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700915 }
916
917 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000918 ComponentName cn = new ComponentName("com.android.alarmclock",
919 "com.android.alarmclock.AnalogAppWidgetProvider");
920 return addAppWidget(db, values, cn, 2, 2);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800921 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700922
Romain Guy693599f2010-03-23 10:58:18 -0700923 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
924 PackageManager packageManager) {
925
Mike Cleronb87bd162009-10-30 16:36:56 -0700926 String packageName = a.getString(R.styleable.Favorite_packageName);
927 String className = a.getString(R.styleable.Favorite_className);
928
929 if (packageName == null || className == null) {
930 return false;
931 }
Romain Guy693599f2010-03-23 10:58:18 -0700932
933 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -0700934 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -0700935 try {
936 packageManager.getReceiverInfo(cn, 0);
937 } catch (Exception e) {
938 String[] packages = packageManager.currentToCanonicalPackageNames(
939 new String[] { packageName });
940 cn = new ComponentName(packages[0], className);
941 try {
942 packageManager.getReceiverInfo(cn, 0);
943 } catch (Exception e1) {
944 hasPackage = false;
945 }
946 }
947
948 if (hasPackage) {
949 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
950 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
951 return addAppWidget(db, values, cn, spanX, spanY);
952 }
953
954 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +0000955 }
956
957 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
958 int spanX, int spanY) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700959 boolean allocatedAppWidgets = false;
960 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
961
962 try {
963 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
964
965 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000966 values.put(Favorites.SPANX, spanX);
967 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -0700968 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700969 values.put(Favorites._ID, generateNewId());
970 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700971
972 allocatedAppWidgets = true;
973
974 appWidgetManager.bindAppWidgetId(appWidgetId, cn);
975 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800976 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -0700977 }
978
979 return allocatedAppWidgets;
980 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700981
982 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -0700983 TypedArray a) {
984 Resources r = mContext.getResources();
985
986 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
987 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
988
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800989 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -0700990 String uri = null;
991 try {
992 uri = a.getString(R.styleable.Favorite_uri);
993 intent = Intent.parseUri(uri, 0);
994 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800995 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -0700996 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -0700997 }
998
999 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001000 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001001 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001002 }
1003
Adam Cohen228da5a2011-07-27 22:23:47 -07001004 long id = generateNewId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001005 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1006 values.put(Favorites.INTENT, intent.toUri(0));
1007 values.put(Favorites.TITLE, r.getString(titleResId));
1008 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1009 values.put(Favorites.SPANX, 1);
1010 values.put(Favorites.SPANY, 1);
1011 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1012 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1013 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001014 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001015
Adam Cohen228da5a2011-07-27 22:23:47 -07001016 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1017 return -1;
1018 }
1019 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001020 }
1021 }
1022
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001023 /**
1024 * Build a query string that will match any row where the column matches
1025 * anything in the values list.
1026 */
1027 static String buildOrWhereString(String column, int[] values) {
1028 StringBuilder selectWhere = new StringBuilder();
1029 for (int i = values.length - 1; i >= 0; i--) {
1030 selectWhere.append(column).append("=").append(values[i]);
1031 if (i > 0) {
1032 selectWhere.append(" OR ");
1033 }
1034 }
1035 return selectWhere.toString();
1036 }
1037
1038 static class SqlArguments {
1039 public final String table;
1040 public final String where;
1041 public final String[] args;
1042
1043 SqlArguments(Uri url, String where, String[] args) {
1044 if (url.getPathSegments().size() == 1) {
1045 this.table = url.getPathSegments().get(0);
1046 this.where = where;
1047 this.args = args;
1048 } else if (url.getPathSegments().size() != 2) {
1049 throw new IllegalArgumentException("Invalid URI: " + url);
1050 } else if (!TextUtils.isEmpty(where)) {
1051 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1052 } else {
1053 this.table = url.getPathSegments().get(0);
1054 this.where = "_id=" + ContentUris.parseId(url);
1055 this.args = null;
1056 }
1057 }
1058
1059 SqlArguments(Uri url) {
1060 if (url.getPathSegments().size() == 1) {
1061 table = url.getPathSegments().get(0);
1062 where = null;
1063 args = null;
1064 } else {
1065 throw new IllegalArgumentException("Invalid URI: " + url);
1066 }
1067 }
1068 }
1069}