blob: 96cc63ceebd6cba46346f716e91f10bc035bbea0 [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.ContentProvider;
24import android.content.Context;
25import android.content.ContentValues;
26import android.content.Intent;
27import android.content.ComponentName;
28import android.content.ContentUris;
29import android.content.ContentResolver;
Mike Cleronb87bd162009-10-30 16:36:56 -070030import android.content.res.Resources;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070031import android.content.res.XmlResourceParser;
32import android.content.res.TypedArray;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080033import android.content.pm.PackageManager;
34import android.content.pm.ActivityInfo;
Michael Jurkaa8c760d2011-04-28 14:59:33 -070035import android.content.SharedPreferences;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.database.sqlite.SQLiteOpenHelper;
37import android.database.sqlite.SQLiteDatabase;
Joe Onorato0589f0f2010-02-08 13:44:00 -080038import android.database.sqlite.SQLiteStatement;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.database.sqlite.SQLiteQueryBuilder;
40import android.database.Cursor;
41import android.database.SQLException;
Joe Onorato0589f0f2010-02-08 13:44:00 -080042import android.graphics.Bitmap;
43import android.graphics.BitmapFactory;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044import android.util.Log;
45import android.util.Xml;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070046import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import android.net.Uri;
48import android.text.TextUtils;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049import android.provider.Settings;
50
The Android Open Source Project31dd5032009-03-03 19:32:27 -080051import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070052import java.net.URISyntaxException;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000053import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055import org.xmlpull.v1.XmlPullParserException;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070056import org.xmlpull.v1.XmlPullParser;
Dianne Hackborn094fc7a2010-02-24 20:01:46 -080057
58import com.android.internal.util.XmlUtils;
Joe Onoratoa5902522009-07-30 13:37:37 -070059import com.android.launcher2.LauncherSettings.Favorites;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080060
Romain Guyedcce092010-03-04 13:03:17 -080061import com.android.launcher.R;
62
The Android Open Source Project31dd5032009-03-03 19:32:27 -080063public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080064 private static final String TAG = "Launcher.LauncherProvider";
65 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080066
67 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070068
69 private static final int DATABASE_VERSION = 9;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080070
Joe Onoratoa5902522009-07-30 13:37:37 -070071 static final String AUTHORITY = "com.android.launcher2.settings";
Winson Chung3d503fb2011-07-13 17:25:49 -070072
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073 static final String TABLE_FAVORITES = "favorites";
74 static final String PARAMETER_NOTIFY = "notify";
75
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070076 /**
Romain Guy73b979d2009-06-09 12:57:21 -070077 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070078 * {@link AppWidgetHost#deleteHost()} is called during database creation.
79 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
80 */
81 static final Uri CONTENT_APPWIDGET_RESET_URI =
82 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
83
Michael Jurkaa8c760d2011-04-28 14:59:33 -070084 private DatabaseHelper mOpenHelper;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080085
86 @Override
87 public boolean onCreate() {
88 mOpenHelper = new DatabaseHelper(getContext());
Michael Jurkaa8c760d2011-04-28 14:59:33 -070089 ((LauncherApplication) getContext()).setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080090 return true;
91 }
92
93 @Override
94 public String getType(Uri uri) {
95 SqlArguments args = new SqlArguments(uri, null, null);
96 if (TextUtils.isEmpty(args.where)) {
97 return "vnd.android.cursor.dir/" + args.table;
98 } else {
99 return "vnd.android.cursor.item/" + args.table;
100 }
101 }
102
103 @Override
104 public Cursor query(Uri uri, String[] projection, String selection,
105 String[] selectionArgs, String sortOrder) {
106
107 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
108 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
109 qb.setTables(args.table);
110
Romain Guy73b979d2009-06-09 12:57:21 -0700111 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800112 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
113 result.setNotificationUri(getContext().getContentResolver(), uri);
114
115 return result;
116 }
117
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700118 private static long dbInsertAndCheck(DatabaseHelper helper,
119 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
120 if (!values.containsKey(LauncherSettings.Favorites._ID)) {
121 throw new RuntimeException("Error: attempting to add item without specifying an id");
122 }
123 return db.insert(table, nullColumnHack, values);
124 }
125
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800126 @Override
127 public Uri insert(Uri uri, ContentValues initialValues) {
128 SqlArguments args = new SqlArguments(uri);
129
130 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700131 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800132 if (rowId <= 0) return null;
133
134 uri = ContentUris.withAppendedId(uri, rowId);
135 sendNotify(uri);
136
137 return uri;
138 }
139
140 @Override
141 public int bulkInsert(Uri uri, ContentValues[] values) {
142 SqlArguments args = new SqlArguments(uri);
143
144 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
145 db.beginTransaction();
146 try {
147 int numValues = values.length;
148 for (int i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700149 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
150 return 0;
151 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800152 }
153 db.setTransactionSuccessful();
154 } finally {
155 db.endTransaction();
156 }
157
158 sendNotify(uri);
159 return values.length;
160 }
161
162 @Override
163 public int delete(Uri uri, String selection, String[] selectionArgs) {
164 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
165
166 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
167 int count = db.delete(args.table, args.where, args.args);
168 if (count > 0) sendNotify(uri);
169
170 return count;
171 }
172
173 @Override
174 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
175 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
176
177 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
178 int count = db.update(args.table, values, args.where, args.args);
179 if (count > 0) sendNotify(uri);
180
181 return count;
182 }
183
184 private void sendNotify(Uri uri) {
185 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
186 if (notify == null || "true".equals(notify)) {
187 getContext().getContentResolver().notifyChange(uri, null);
188 }
189 }
190
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700191 public long generateNewId() {
192 return mOpenHelper.generateNewId();
193 }
194
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800195 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800196 private static final String TAG_FAVORITES = "favorites";
197 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700198 private static final String TAG_CLOCK = "clock";
199 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700200 private static final String TAG_APPWIDGET = "appwidget";
201 private static final String TAG_SHORTCUT = "shortcut";
Winson Chung3d503fb2011-07-13 17:25:49 -0700202
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800203 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700204 private final AppWidgetHost mAppWidgetHost;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700205 private long mMaxId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800206
207 DatabaseHelper(Context context) {
208 super(context, DATABASE_NAME, null, DATABASE_VERSION);
209 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700210 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700211
212 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
213 // the DB here
214 if (mMaxId == -1) {
215 mMaxId = initializeMaxId(getWritableDatabase());
216 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800217 }
218
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700219 /**
220 * Send notification that we've deleted the {@link AppWidgetHost},
221 * probably as part of the initial database creation. The receiver may
222 * want to re-call {@link AppWidgetHost#startListening()} to ensure
223 * callbacks are correctly set.
224 */
225 private void sendAppWidgetResetNotify() {
226 final ContentResolver resolver = mContext.getContentResolver();
227 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
228 }
229
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800230 @Override
231 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800232 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700233
234 mMaxId = 1;
235
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800236 db.execSQL("CREATE TABLE favorites (" +
237 "_id INTEGER PRIMARY KEY," +
238 "title TEXT," +
239 "intent TEXT," +
240 "container INTEGER," +
241 "screen INTEGER," +
242 "cellX INTEGER," +
243 "cellY INTEGER," +
244 "spanX INTEGER," +
245 "spanY INTEGER," +
246 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700247 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800248 "isShortcut INTEGER," +
249 "iconType INTEGER," +
250 "iconPackage TEXT," +
251 "iconResource TEXT," +
252 "icon BLOB," +
253 "uri TEXT," +
254 "displayMode INTEGER" +
255 ");");
256
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700257 // Database was just created, so wipe any previous widgets
258 if (mAppWidgetHost != null) {
259 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700260 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800261 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700262
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800263 if (!convertDatabase(db)) {
264 // Populate favorites table with initial favorites
Winson Chung3d503fb2011-07-13 17:25:49 -0700265 loadFavorites(db, ItemInfo.NO_ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800266 }
267 }
268
269 private boolean convertDatabase(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800270 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800271 boolean converted = false;
272
273 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
274 "/old_favorites?notify=true");
275 final ContentResolver resolver = mContext.getContentResolver();
276 Cursor cursor = null;
277
278 try {
279 cursor = resolver.query(uri, null, null, null, null);
280 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700281 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800282 }
283
284 // We already have a favorites database in the old provider
285 if (cursor != null && cursor.getCount() > 0) {
286 try {
287 converted = copyFromCursor(db, cursor) > 0;
288 } finally {
289 cursor.close();
290 }
291
292 if (converted) {
293 resolver.delete(uri, null, null);
294 }
295 }
296
297 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700298 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800299 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800300 convertWidgets(db);
301 }
302
303 return converted;
304 }
305
306 private int copyFromCursor(SQLiteDatabase db, Cursor c) {
Romain Guy73b979d2009-06-09 12:57:21 -0700307 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800308 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
309 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
310 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
311 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
312 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
313 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
314 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
315 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
316 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
317 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
318 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
319 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
320 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
321
322 ContentValues[] rows = new ContentValues[c.getCount()];
323 int i = 0;
324 while (c.moveToNext()) {
325 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700326 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800327 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
328 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
329 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
330 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
331 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
332 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
333 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
334 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700335 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800336 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
337 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
338 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
339 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
340 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
341 rows[i++] = values;
342 }
343
344 db.beginTransaction();
345 int total = 0;
346 try {
347 int numValues = rows.length;
348 for (i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700349 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800350 return 0;
351 } else {
352 total++;
353 }
354 }
355 db.setTransactionSuccessful();
356 } finally {
357 db.endTransaction();
358 }
359
360 return total;
361 }
362
363 @Override
364 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800365 if (LOGD) Log.d(TAG, "onUpgrade triggered");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800366
367 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700368 if (version < 3) {
369 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800370 db.beginTransaction();
371 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700372 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800373 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700374 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800375 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700376 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800377 } catch (SQLException ex) {
378 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800379 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800380 } finally {
381 db.endTransaction();
382 }
383
384 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700385 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800386 convertWidgets(db);
387 }
388 }
Romain Guy73b979d2009-06-09 12:57:21 -0700389
390 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800391 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700392 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800393
Romain Guy509cd6a2010-03-23 15:10:56 -0700394 // Where's version 5?
395 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
396 // - Passion shipped on 2.1 with version 6 of launcher2
397 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
398 // but version 5 on there was the updateContactsShortcuts change
399 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
400 // The updateContactsShortcuts change is idempotent, so running it twice
401 // is okay so we'll do that when upgrading the devices that shipped with it.
402 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800403 // We went from 3 to 5 screens. Move everything 1 to the right
404 db.beginTransaction();
405 try {
406 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
407 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800408 } catch (SQLException ex) {
409 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800410 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800411 } finally {
412 db.endTransaction();
413 }
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800414
Romain Guy509cd6a2010-03-23 15:10:56 -0700415 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800416 if (updateContactsShortcuts(db)) {
417 version = 6;
418 }
419 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000420
421 if (version < 7) {
422 // Version 7 gets rid of the special search widget.
423 convertWidgets(db);
424 version = 7;
425 }
426
Joe Onorato0589f0f2010-02-08 13:44:00 -0800427 if (version < 8) {
428 // Version 8 (froyo) has the icons all normalized. This should
429 // already be the case in practice, but we now rely on it and don't
430 // resample the images each time.
431 normalizeIcons(db);
432 version = 8;
433 }
434
Winson Chung3d503fb2011-07-13 17:25:49 -0700435 if (version < 9) {
436 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
437 // before it gets a change to get set, so we need to read it here when we use it)
438 if (mMaxId == -1) {
439 mMaxId = initializeMaxId(db);
440 }
441
442 // Add default hotseat icons
443 loadFavorites(db, LauncherSettings.Favorites.CONTAINER_HOTSEAT);
444 version = 9;
445 }
446
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800447 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800448 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800449 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
450 onCreate(db);
451 }
452 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800453
454 private boolean updateContactsShortcuts(SQLiteDatabase db) {
455 Cursor c = null;
456 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
457 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
458
459 db.beginTransaction();
460 try {
461 // Select and iterate through each matching widget
462 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.INTENT },
463 selectWhere, null, null, null, null);
464
465 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
466
467 final ContentValues values = new ContentValues();
468 final int idIndex = c.getColumnIndex(Favorites._ID);
469 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
470
471 while (c != null && c.moveToNext()) {
472 long favoriteId = c.getLong(idIndex);
473 final String intentUri = c.getString(intentIndex);
474 if (intentUri != null) {
475 try {
476 Intent intent = Intent.parseUri(intentUri, 0);
477 android.util.Log.d("Home", intent.toString());
478 final Uri uri = intent.getData();
479 final String data = uri.toString();
480 if (Intent.ACTION_VIEW.equals(intent.getAction()) &&
481 (data.startsWith("content://contacts/people/") ||
482 data.startsWith("content://com.android.contacts/contacts/lookup/"))) {
483
484 intent = new Intent("com.android.contacts.action.QUICK_CONTACT");
485 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
486 Intent.FLAG_ACTIVITY_CLEAR_TOP |
487 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
488
489 intent.setData(uri);
490 intent.putExtra("mode", 3);
491 intent.putExtra("exclude_mimes", (String[]) null);
492
493 values.clear();
494 values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0));
495
496 String updateWhere = Favorites._ID + "=" + favoriteId;
497 db.update(TABLE_FAVORITES, values, updateWhere, null);
498 }
499 } catch (RuntimeException ex) {
500 Log.e(TAG, "Problem upgrading shortcut", ex);
501 } catch (URISyntaxException e) {
502 Log.e(TAG, "Problem upgrading shortcut", e);
503 }
504 }
505 }
506
507 db.setTransactionSuccessful();
508 } catch (SQLException ex) {
509 Log.w(TAG, "Problem while upgrading contacts", ex);
510 return false;
511 } finally {
512 db.endTransaction();
513 if (c != null) {
514 c.close();
515 }
516 }
517
518 return true;
519 }
520
Joe Onorato0589f0f2010-02-08 13:44:00 -0800521 private void normalizeIcons(SQLiteDatabase db) {
522 Log.d(TAG, "normalizing icons");
523
Joe Onorato346e1292010-02-18 10:34:24 -0500524 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800525 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400526 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800527 try {
528 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400529 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600530 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800531
532 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
533 Favorites.ICON_TYPE_BITMAP, null);
534
535 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
536 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
537
538 while (c.moveToNext()) {
539 long id = c.getLong(idIndex);
540 byte[] data = c.getBlob(iconIndex);
541 try {
542 Bitmap bitmap = Utilities.resampleIconBitmap(
543 BitmapFactory.decodeByteArray(data, 0, data.length),
544 mContext);
545 if (bitmap != null) {
546 update.bindLong(1, id);
547 data = ItemInfo.flattenBitmap(bitmap);
548 if (data != null) {
549 update.bindBlob(2, data);
550 update.execute();
551 }
552 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800553 }
554 } catch (Exception e) {
555 if (!logged) {
556 Log.e(TAG, "Failed normalizing icon " + id, e);
557 } else {
558 Log.e(TAG, "Also failed normalizing icon " + id);
559 }
560 logged = true;
561 }
562 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000563 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800564 } catch (SQLException ex) {
565 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
566 } finally {
567 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400568 if (update != null) {
569 update.close();
570 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800571 if (c != null) {
572 c.close();
573 }
574 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700575 }
576
577 // Generates a new ID to use for an object in your database. This method should be only
578 // called from the main UI thread. As an exception, we do call it when we call the
579 // constructor from the worker thread; however, this doesn't extend until after the
580 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
581 // after that point
582 public long generateNewId() {
583 if (mMaxId < 0) {
584 throw new RuntimeException("Error: max id was not initialized");
585 }
586 mMaxId += 1;
587 return mMaxId;
588 }
589
590 private long initializeMaxId(SQLiteDatabase db) {
591 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
592
593 // get the result
594 final int maxIdIndex = 0;
595 long id = -1;
596 if (c != null && c.moveToNext()) {
597 id = c.getLong(maxIdIndex);
598 }
599
600 if (id == -1) {
601 throw new RuntimeException("Error: could not query max id");
602 }
603
604 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800605 }
606
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800607 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700608 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000609 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800610 */
611 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000612 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800613 final int[] bindSources = new int[] {
614 Favorites.ITEM_TYPE_WIDGET_CLOCK,
615 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000616 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800617 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000618
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800619 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
620
621 Cursor c = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800622
623 db.beginTransaction();
624 try {
625 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000626 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800627 selectWhere, null, null, null, null);
628
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800629 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800630
631 final ContentValues values = new ContentValues();
632 while (c != null && c.moveToNext()) {
633 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000634 int favoriteType = c.getInt(1);
635
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700636 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800637 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700638 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800639
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800640 if (LOGD) {
641 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
642 + " for favoriteId=" + favoriteId);
643 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800644 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000645 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
646 values.put(Favorites.APPWIDGET_ID, appWidgetId);
647
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800648 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +0000649 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
650 values.put(LauncherSettings.Favorites.SPANX, 4);
651 values.put(LauncherSettings.Favorites.SPANY, 1);
652 } else {
653 values.put(LauncherSettings.Favorites.SPANX, 2);
654 values.put(LauncherSettings.Favorites.SPANY, 2);
655 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800656
657 String updateWhere = Favorites._ID + "=" + favoriteId;
658 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +0000659
Bjorn Bringert34251342009-12-15 13:33:11 +0000660 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
661 appWidgetManager.bindAppWidgetId(appWidgetId,
662 new ComponentName("com.android.alarmclock",
663 "com.android.alarmclock.AnalogAppWidgetProvider"));
664 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
665 appWidgetManager.bindAppWidgetId(appWidgetId,
666 new ComponentName("com.android.camera",
667 "com.android.camera.PhotoAppWidgetProvider"));
668 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
669 appWidgetManager.bindAppWidgetId(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000670 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +0000671 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800672 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800673 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800674 }
675 }
676
677 db.setTransactionSuccessful();
678 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800679 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800680 } finally {
681 db.endTransaction();
682 if (c != null) {
683 c.close();
684 }
685 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800686 }
687
688 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800689 * Loads the default set of favorite packages from an xml file.
690 *
691 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -0700692 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800693 */
Winson Chung3d503fb2011-07-13 17:25:49 -0700694 private int loadFavorites(SQLiteDatabase db, long filterContainerId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800695 Intent intent = new Intent(Intent.ACTION_MAIN, null);
696 intent.addCategory(Intent.CATEGORY_LAUNCHER);
697 ContentValues values = new ContentValues();
698
699 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800700 int i = 0;
701 try {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700702 XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace);
703 AttributeSet attrs = Xml.asAttributeSet(parser);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800704 XmlUtils.beginDocument(parser, TAG_FAVORITES);
705
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700706 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800707
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700708 int type;
709 while (((type = parser.next()) != XmlPullParser.END_TAG ||
710 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
711
712 if (type != XmlPullParser.START_TAG) {
713 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800714 }
715
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700716 boolean added = false;
717 final String name = parser.getName();
718
719 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
720
Winson Chung3d503fb2011-07-13 17:25:49 -0700721 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
722 if (a.hasValue(R.styleable.Favorite_container)) {
723 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
724 }
725 if (filterContainerId == ItemInfo.NO_ID || filterContainerId == container) {
726 String screen = a.getString(R.styleable.Favorite_screen);
727 String x = a.getString(R.styleable.Favorite_x);
728 String y = a.getString(R.styleable.Favorite_y);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700729
Winson Chung3d503fb2011-07-13 17:25:49 -0700730 // If we are adding to the hotset, the screen is used as the position in the
731 // hotset. This screen can't be at position 0 because AllApps is in the
732 // zeroth position.
733 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT &&
734 Integer.valueOf(screen) <= 0) {
735 throw new RuntimeException("Invalid screen position for hotseat item");
736 }
737
738 values.clear();
739 values.put(LauncherSettings.Favorites.CONTAINER, container);
740 values.put(LauncherSettings.Favorites.SCREEN, screen);
741 values.put(LauncherSettings.Favorites.CELLX, x);
742 values.put(LauncherSettings.Favorites.CELLY, y);
743
744 System.out.println("Adding item to container: " + container);
745
746 if (TAG_FAVORITE.equals(name)) {
747 added = addAppShortcut(db, values, a, packageManager, intent);
748 } else if (TAG_SEARCH.equals(name)) {
749 added = addSearchWidget(db, values);
750 } else if (TAG_CLOCK.equals(name)) {
751 added = addClockWidget(db, values);
752 } else if (TAG_APPWIDGET.equals(name)) {
753 added = addAppWidget(db, values, a, packageManager);
754 } else if (TAG_SHORTCUT.equals(name)) {
755 added = addUriShortcut(db, values, a);
756 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800757 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700758
759 if (added) i++;
760
761 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800762 }
763 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800764 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800765 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800766 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -0700767 } catch (RuntimeException e) {
768 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800769 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700770
771 return i;
772 }
773
Mike Cleronb87bd162009-10-30 16:36:56 -0700774 private boolean addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700775 PackageManager packageManager, Intent intent) {
776
777 ActivityInfo info;
778 String packageName = a.getString(R.styleable.Favorite_packageName);
779 String className = a.getString(R.styleable.Favorite_className);
780 try {
Romain Guy693599f2010-03-23 10:58:18 -0700781 ComponentName cn;
782 try {
783 cn = new ComponentName(packageName, className);
784 info = packageManager.getActivityInfo(cn, 0);
785 } catch (PackageManager.NameNotFoundException nnfe) {
786 String[] packages = packageManager.currentToCanonicalPackageNames(
787 new String[] { packageName });
788 cn = new ComponentName(packages[0], className);
789 info = packageManager.getActivityInfo(cn, 0);
790 }
791
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700792 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -0700793 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
794 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700795 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700796 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
797 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
798 values.put(Favorites.SPANX, 1);
799 values.put(Favorites.SPANY, 1);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700800 values.put(Favorites._ID, generateNewId());
801 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700802 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800803 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700804 "/" + className, e);
805 return false;
806 }
807 return true;
808 }
809
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000810 private ComponentName getSearchWidgetProvider() {
811 SearchManager searchManager =
812 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
813 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
814 if (searchComponent == null) return null;
815 return getProviderInPackage(searchComponent.getPackageName());
816 }
817
818 /**
819 * Gets an appwidget provider from the given package. If the package contains more than
820 * one appwidget provider, an arbitrary one is returned.
821 */
822 private ComponentName getProviderInPackage(String packageName) {
823 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
824 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
825 if (providers == null) return null;
826 final int providerCount = providers.size();
827 for (int i = 0; i < providerCount; i++) {
828 ComponentName provider = providers.get(i).provider;
829 if (provider != null && provider.getPackageName().equals(packageName)) {
830 return provider;
831 }
832 }
833 return null;
834 }
835
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700836 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000837 ComponentName cn = getSearchWidgetProvider();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000838 return addAppWidget(db, values, cn, 4, 1);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700839 }
840
841 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000842 ComponentName cn = new ComponentName("com.android.alarmclock",
843 "com.android.alarmclock.AnalogAppWidgetProvider");
844 return addAppWidget(db, values, cn, 2, 2);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800845 }
Mike Cleronb87bd162009-10-30 16:36:56 -0700846
Romain Guy693599f2010-03-23 10:58:18 -0700847 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
848 PackageManager packageManager) {
849
Mike Cleronb87bd162009-10-30 16:36:56 -0700850 String packageName = a.getString(R.styleable.Favorite_packageName);
851 String className = a.getString(R.styleable.Favorite_className);
852
853 if (packageName == null || className == null) {
854 return false;
855 }
Romain Guy693599f2010-03-23 10:58:18 -0700856
857 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -0700858 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -0700859 try {
860 packageManager.getReceiverInfo(cn, 0);
861 } catch (Exception e) {
862 String[] packages = packageManager.currentToCanonicalPackageNames(
863 new String[] { packageName });
864 cn = new ComponentName(packages[0], className);
865 try {
866 packageManager.getReceiverInfo(cn, 0);
867 } catch (Exception e1) {
868 hasPackage = false;
869 }
870 }
871
872 if (hasPackage) {
873 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
874 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
875 return addAppWidget(db, values, cn, spanX, spanY);
876 }
877
878 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +0000879 }
880
881 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
882 int spanX, int spanY) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700883 boolean allocatedAppWidgets = false;
884 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
885
886 try {
887 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
888
889 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000890 values.put(Favorites.SPANX, spanX);
891 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -0700892 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700893 values.put(Favorites._ID, generateNewId());
894 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700895
896 allocatedAppWidgets = true;
897
898 appWidgetManager.bindAppWidgetId(appWidgetId, cn);
899 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800900 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -0700901 }
902
903 return allocatedAppWidgets;
904 }
905
906 private boolean addUriShortcut(SQLiteDatabase db, ContentValues values,
907 TypedArray a) {
908 Resources r = mContext.getResources();
909
910 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
911 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
912
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800913 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -0700914 String uri = null;
915 try {
916 uri = a.getString(R.styleable.Favorite_uri);
917 intent = Intent.parseUri(uri, 0);
918 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800919 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Mike Cleronb87bd162009-10-30 16:36:56 -0700920 return false; // Oh well
921 }
922
923 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800924 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Mike Cleronb87bd162009-10-30 16:36:56 -0700925 return false;
926 }
927
928 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
929 values.put(Favorites.INTENT, intent.toUri(0));
930 values.put(Favorites.TITLE, r.getString(titleResId));
931 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
932 values.put(Favorites.SPANX, 1);
933 values.put(Favorites.SPANY, 1);
934 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
935 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
936 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700937 values.put(Favorites._ID, generateNewId());
938 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700939
940 return true;
941 }
942 }
943
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800944 /**
945 * Build a query string that will match any row where the column matches
946 * anything in the values list.
947 */
948 static String buildOrWhereString(String column, int[] values) {
949 StringBuilder selectWhere = new StringBuilder();
950 for (int i = values.length - 1; i >= 0; i--) {
951 selectWhere.append(column).append("=").append(values[i]);
952 if (i > 0) {
953 selectWhere.append(" OR ");
954 }
955 }
956 return selectWhere.toString();
957 }
958
959 static class SqlArguments {
960 public final String table;
961 public final String where;
962 public final String[] args;
963
964 SqlArguments(Uri url, String where, String[] args) {
965 if (url.getPathSegments().size() == 1) {
966 this.table = url.getPathSegments().get(0);
967 this.where = where;
968 this.args = args;
969 } else if (url.getPathSegments().size() != 2) {
970 throw new IllegalArgumentException("Invalid URI: " + url);
971 } else if (!TextUtils.isEmpty(where)) {
972 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
973 } else {
974 this.table = url.getPathSegments().get(0);
975 this.where = "_id=" + ContentUris.parseId(url);
976 this.args = null;
977 }
978 }
979
980 SqlArguments(Uri url) {
981 if (url.getPathSegments().size() == 1) {
982 table = url.getPathSegments().get(0);
983 where = null;
984 args = null;
985 } else {
986 throw new IllegalArgumentException("Invalid URI: " + url);
987 }
988 }
989 }
990}