blob: de9a9b2de2245238137dcaea0ca87fb139f3060f [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();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -0800709 int allAppsButtonRank =
710 mContext.getResources().getInteger(R.integer.hotseat_all_apps_index);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800711 int i = 0;
712 try {
Winson Chung6d092682011-11-16 18:43:26 -0800713 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700714 AttributeSet attrs = Xml.asAttributeSet(parser);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800715 XmlUtils.beginDocument(parser, TAG_FAVORITES);
716
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700717 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800718
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700719 int type;
720 while (((type = parser.next()) != XmlPullParser.END_TAG ||
721 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
722
723 if (type != XmlPullParser.START_TAG) {
724 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800725 }
726
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700727 boolean added = false;
728 final String name = parser.getName();
729
730 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
731
Winson Chung3d503fb2011-07-13 17:25:49 -0700732 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
733 if (a.hasValue(R.styleable.Favorite_container)) {
734 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
735 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700736
Winson Chung6d092682011-11-16 18:43:26 -0800737 String screen = a.getString(R.styleable.Favorite_screen);
738 String x = a.getString(R.styleable.Favorite_x);
739 String y = a.getString(R.styleable.Favorite_y);
740
741 // If we are adding to the hotseat, the screen is used as the position in the
742 // hotseat. This screen can't be at position 0 because AllApps is in the
743 // zeroth position.
Andrew Flynn0dca1ec2012-02-29 13:33:22 -0800744 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
745 && Integer.valueOf(screen) == allAppsButtonRank) {
Winson Chung6d092682011-11-16 18:43:26 -0800746 throw new RuntimeException("Invalid screen position for hotseat item");
747 }
748
749 values.clear();
750 values.put(LauncherSettings.Favorites.CONTAINER, container);
751 values.put(LauncherSettings.Favorites.SCREEN, screen);
752 values.put(LauncherSettings.Favorites.CELLX, x);
753 values.put(LauncherSettings.Favorites.CELLY, y);
754
755 if (TAG_FAVORITE.equals(name)) {
756 long id = addAppShortcut(db, values, a, packageManager, intent);
757 added = id >= 0;
758 } else if (TAG_SEARCH.equals(name)) {
759 added = addSearchWidget(db, values);
760 } else if (TAG_CLOCK.equals(name)) {
761 added = addClockWidget(db, values);
762 } else if (TAG_APPWIDGET.equals(name)) {
763 added = addAppWidget(db, values, a, packageManager);
764 } else if (TAG_SHORTCUT.equals(name)) {
765 long id = addUriShortcut(db, values, a);
766 added = id >= 0;
767 } else if (TAG_FOLDER.equals(name)) {
768 String title;
769 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
770 if (titleResId != -1) {
771 title = mContext.getResources().getString(titleResId);
772 } else {
773 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -0700774 }
Winson Chung6d092682011-11-16 18:43:26 -0800775 values.put(LauncherSettings.Favorites.TITLE, title);
776 long folderId = addFolder(db, values);
777 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -0700778
Winson Chung6d092682011-11-16 18:43:26 -0800779 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -0700780
Winson Chung6d092682011-11-16 18:43:26 -0800781 int folderDepth = parser.getDepth();
782 while ((type = parser.next()) != XmlPullParser.END_TAG ||
783 parser.getDepth() > folderDepth) {
784 if (type != XmlPullParser.START_TAG) {
785 continue;
786 }
787 final String folder_item_name = parser.getName();
788
789 TypedArray ar = mContext.obtainStyledAttributes(attrs,
790 R.styleable.Favorite);
791 values.clear();
792 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
793
794 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
795 long id =
796 addAppShortcut(db, values, ar, packageManager, intent);
797 if (id >= 0) {
798 folderItems.add(id);
799 }
800 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
801 long id = addUriShortcut(db, values, ar);
802 if (id >= 0) {
803 folderItems.add(id);
804 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700805 } else {
Winson Chung6d092682011-11-16 18:43:26 -0800806 throw new RuntimeException("Folders can " +
807 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -0700808 }
Winson Chung6d092682011-11-16 18:43:26 -0800809 ar.recycle();
810 }
811 // We can only have folders with >= 2 items, so we need to remove the
812 // folder and clean up if less than 2 items were included, or some
813 // failed to add, and less than 2 were actually added
814 if (folderItems.size() < 2 && folderId >= 0) {
815 // We just delete the folder and any items that made it
816 deleteId(db, folderId);
817 if (folderItems.size() > 0) {
818 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -0700819 }
Winson Chung6d092682011-11-16 18:43:26 -0800820 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -0700821 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800822 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700823 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700824 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800825 }
826 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800827 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800828 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800829 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -0700830 } catch (RuntimeException e) {
831 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800832 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700833
834 return i;
835 }
836
Adam Cohen228da5a2011-07-27 22:23:47 -0700837 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700838 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -0700839 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700840 ActivityInfo info;
841 String packageName = a.getString(R.styleable.Favorite_packageName);
842 String className = a.getString(R.styleable.Favorite_className);
843 try {
Romain Guy693599f2010-03-23 10:58:18 -0700844 ComponentName cn;
845 try {
846 cn = new ComponentName(packageName, className);
847 info = packageManager.getActivityInfo(cn, 0);
848 } catch (PackageManager.NameNotFoundException nnfe) {
849 String[] packages = packageManager.currentToCanonicalPackageNames(
850 new String[] { packageName });
851 cn = new ComponentName(packages[0], className);
852 info = packageManager.getActivityInfo(cn, 0);
853 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700854 id = generateNewId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700855 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -0700856 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
857 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700858 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700859 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
860 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
861 values.put(Favorites.SPANX, 1);
862 values.put(Favorites.SPANY, 1);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700863 values.put(Favorites._ID, generateNewId());
Adam Cohen228da5a2011-07-27 22:23:47 -0700864 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
865 return -1;
866 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700867 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800868 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700869 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700870 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700871 return id;
872 }
873
874 private long addFolder(SQLiteDatabase db, ContentValues values) {
875 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
876 values.put(Favorites.SPANX, 1);
877 values.put(Favorites.SPANY, 1);
878 long id = generateNewId();
879 values.put(Favorites._ID, id);
880 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
881 return -1;
882 } else {
883 return id;
884 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700885 }
886
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000887 private ComponentName getSearchWidgetProvider() {
888 SearchManager searchManager =
889 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
890 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
891 if (searchComponent == null) return null;
892 return getProviderInPackage(searchComponent.getPackageName());
893 }
894
895 /**
896 * Gets an appwidget provider from the given package. If the package contains more than
897 * one appwidget provider, an arbitrary one is returned.
898 */
899 private ComponentName getProviderInPackage(String packageName) {
900 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
901 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
902 if (providers == null) return null;
903 final int providerCount = providers.size();
904 for (int i = 0; i < providerCount; i++) {
905 ComponentName provider = providers.get(i).provider;
906 if (provider != null && provider.getPackageName().equals(packageName)) {
907 return provider;
908 }
909 }
910 return null;
911 }
912
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700913 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000914 ComponentName cn = getSearchWidgetProvider();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000915 return addAppWidget(db, values, cn, 4, 1);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700916 }
917
918 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000919 ComponentName cn = new ComponentName("com.android.alarmclock",
920 "com.android.alarmclock.AnalogAppWidgetProvider");
921 return addAppWidget(db, values, cn, 2, 2);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800922 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700923
Romain Guy693599f2010-03-23 10:58:18 -0700924 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
925 PackageManager packageManager) {
926
Mike Cleronb87bd162009-10-30 16:36:56 -0700927 String packageName = a.getString(R.styleable.Favorite_packageName);
928 String className = a.getString(R.styleable.Favorite_className);
929
930 if (packageName == null || className == null) {
931 return false;
932 }
Romain Guy693599f2010-03-23 10:58:18 -0700933
934 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -0700935 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -0700936 try {
937 packageManager.getReceiverInfo(cn, 0);
938 } catch (Exception e) {
939 String[] packages = packageManager.currentToCanonicalPackageNames(
940 new String[] { packageName });
941 cn = new ComponentName(packages[0], className);
942 try {
943 packageManager.getReceiverInfo(cn, 0);
944 } catch (Exception e1) {
945 hasPackage = false;
946 }
947 }
948
949 if (hasPackage) {
950 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
951 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
952 return addAppWidget(db, values, cn, spanX, spanY);
953 }
954
955 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +0000956 }
957
958 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
959 int spanX, int spanY) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700960 boolean allocatedAppWidgets = false;
961 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
962
963 try {
964 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
965
966 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000967 values.put(Favorites.SPANX, spanX);
968 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -0700969 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700970 values.put(Favorites._ID, generateNewId());
971 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700972
973 allocatedAppWidgets = true;
974
975 appWidgetManager.bindAppWidgetId(appWidgetId, cn);
976 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800977 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -0700978 }
979
980 return allocatedAppWidgets;
981 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700982
983 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -0700984 TypedArray a) {
985 Resources r = mContext.getResources();
986
987 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
988 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
989
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800990 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -0700991 String uri = null;
992 try {
993 uri = a.getString(R.styleable.Favorite_uri);
994 intent = Intent.parseUri(uri, 0);
995 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800996 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -0700997 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -0700998 }
999
1000 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001001 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001002 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001003 }
1004
Adam Cohen228da5a2011-07-27 22:23:47 -07001005 long id = generateNewId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001006 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1007 values.put(Favorites.INTENT, intent.toUri(0));
1008 values.put(Favorites.TITLE, r.getString(titleResId));
1009 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1010 values.put(Favorites.SPANX, 1);
1011 values.put(Favorites.SPANY, 1);
1012 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1013 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1014 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001015 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001016
Adam Cohen228da5a2011-07-27 22:23:47 -07001017 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1018 return -1;
1019 }
1020 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001021 }
1022 }
1023
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001024 /**
1025 * Build a query string that will match any row where the column matches
1026 * anything in the values list.
1027 */
1028 static String buildOrWhereString(String column, int[] values) {
1029 StringBuilder selectWhere = new StringBuilder();
1030 for (int i = values.length - 1; i >= 0; i--) {
1031 selectWhere.append(column).append("=").append(values[i]);
1032 if (i > 0) {
1033 selectWhere.append(" OR ");
1034 }
1035 }
1036 return selectWhere.toString();
1037 }
1038
1039 static class SqlArguments {
1040 public final String table;
1041 public final String where;
1042 public final String[] args;
1043
1044 SqlArguments(Uri url, String where, String[] args) {
1045 if (url.getPathSegments().size() == 1) {
1046 this.table = url.getPathSegments().get(0);
1047 this.where = where;
1048 this.args = args;
1049 } else if (url.getPathSegments().size() != 2) {
1050 throw new IllegalArgumentException("Invalid URI: " + url);
1051 } else if (!TextUtils.isEmpty(where)) {
1052 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1053 } else {
1054 this.table = url.getPathSegments().get(0);
1055 this.where = "_id=" + ContentUris.parseId(url);
1056 this.args = null;
1057 }
1058 }
1059
1060 SqlArguments(Uri url) {
1061 if (url.getPathSegments().size() == 1) {
1062 table = url.getPathSegments().get(0);
1063 where = null;
1064 args = null;
1065 } else {
1066 throw new IllegalArgumentException("Invalid URI: " + url);
1067 }
1068 }
1069 }
1070}