blob: 5ffd9844f504aef49ed5ffbeb80e9292eac8e3ac [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";
68
Joe Onorato0589f0f2010-02-08 13:44:00 -080069 private static final int DATABASE_VERSION = 8;
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";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072
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";
202
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);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700211 mMaxId = initializeMaxId(getWritableDatabase());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212 }
213
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700214 /**
215 * Send notification that we've deleted the {@link AppWidgetHost},
216 * probably as part of the initial database creation. The receiver may
217 * want to re-call {@link AppWidgetHost#startListening()} to ensure
218 * callbacks are correctly set.
219 */
220 private void sendAppWidgetResetNotify() {
221 final ContentResolver resolver = mContext.getContentResolver();
222 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
223 }
224
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800225 @Override
226 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800227 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700228
229 mMaxId = 1;
230
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800231 db.execSQL("CREATE TABLE favorites (" +
232 "_id INTEGER PRIMARY KEY," +
233 "title TEXT," +
234 "intent TEXT," +
235 "container INTEGER," +
236 "screen INTEGER," +
237 "cellX INTEGER," +
238 "cellY INTEGER," +
239 "spanX INTEGER," +
240 "spanY INTEGER," +
241 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700242 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800243 "isShortcut INTEGER," +
244 "iconType INTEGER," +
245 "iconPackage TEXT," +
246 "iconResource TEXT," +
247 "icon BLOB," +
248 "uri TEXT," +
249 "displayMode INTEGER" +
250 ");");
251
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700252 // Database was just created, so wipe any previous widgets
253 if (mAppWidgetHost != null) {
254 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700255 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800256 }
257
258 if (!convertDatabase(db)) {
259 // Populate favorites table with initial favorites
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700260 loadFavorites(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800261 }
262 }
263
264 private boolean convertDatabase(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800265 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800266 boolean converted = false;
267
268 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
269 "/old_favorites?notify=true");
270 final ContentResolver resolver = mContext.getContentResolver();
271 Cursor cursor = null;
272
273 try {
274 cursor = resolver.query(uri, null, null, null, null);
275 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700276 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277 }
278
279 // We already have a favorites database in the old provider
280 if (cursor != null && cursor.getCount() > 0) {
281 try {
282 converted = copyFromCursor(db, cursor) > 0;
283 } finally {
284 cursor.close();
285 }
286
287 if (converted) {
288 resolver.delete(uri, null, null);
289 }
290 }
291
292 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700293 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800294 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800295 convertWidgets(db);
296 }
297
298 return converted;
299 }
300
301 private int copyFromCursor(SQLiteDatabase db, Cursor c) {
Romain Guy73b979d2009-06-09 12:57:21 -0700302 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800303 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
304 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
305 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
306 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
307 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
308 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
309 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
310 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
311 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
312 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
313 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
314 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
315 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
316
317 ContentValues[] rows = new ContentValues[c.getCount()];
318 int i = 0;
319 while (c.moveToNext()) {
320 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700321 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800322 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
323 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
324 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
325 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
326 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
327 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
328 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
329 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700330 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800331 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
332 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
333 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
334 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
335 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
336 rows[i++] = values;
337 }
338
339 db.beginTransaction();
340 int total = 0;
341 try {
342 int numValues = rows.length;
343 for (i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700344 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800345 return 0;
346 } else {
347 total++;
348 }
349 }
350 db.setTransactionSuccessful();
351 } finally {
352 db.endTransaction();
353 }
354
355 return total;
356 }
357
358 @Override
359 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800360 if (LOGD) Log.d(TAG, "onUpgrade triggered");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800361
362 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700363 if (version < 3) {
364 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800365 db.beginTransaction();
366 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700367 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800368 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700369 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800370 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700371 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800372 } catch (SQLException ex) {
373 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800374 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800375 } finally {
376 db.endTransaction();
377 }
378
379 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700380 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800381 convertWidgets(db);
382 }
383 }
Romain Guy73b979d2009-06-09 12:57:21 -0700384
385 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800386 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700387 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800388
Romain Guy509cd6a2010-03-23 15:10:56 -0700389 // Where's version 5?
390 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
391 // - Passion shipped on 2.1 with version 6 of launcher2
392 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
393 // but version 5 on there was the updateContactsShortcuts change
394 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
395 // The updateContactsShortcuts change is idempotent, so running it twice
396 // is okay so we'll do that when upgrading the devices that shipped with it.
397 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800398 // We went from 3 to 5 screens. Move everything 1 to the right
399 db.beginTransaction();
400 try {
401 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
402 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800403 } catch (SQLException ex) {
404 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800405 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800406 } finally {
407 db.endTransaction();
408 }
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800409
Romain Guy509cd6a2010-03-23 15:10:56 -0700410 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800411 if (updateContactsShortcuts(db)) {
412 version = 6;
413 }
414 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000415
416 if (version < 7) {
417 // Version 7 gets rid of the special search widget.
418 convertWidgets(db);
419 version = 7;
420 }
421
Joe Onorato0589f0f2010-02-08 13:44:00 -0800422 if (version < 8) {
423 // Version 8 (froyo) has the icons all normalized. This should
424 // already be the case in practice, but we now rely on it and don't
425 // resample the images each time.
426 normalizeIcons(db);
427 version = 8;
428 }
429
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800430 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800431 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800432 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
433 onCreate(db);
434 }
435 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800436
437 private boolean updateContactsShortcuts(SQLiteDatabase db) {
438 Cursor c = null;
439 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
440 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
441
442 db.beginTransaction();
443 try {
444 // Select and iterate through each matching widget
445 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.INTENT },
446 selectWhere, null, null, null, null);
447
448 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
449
450 final ContentValues values = new ContentValues();
451 final int idIndex = c.getColumnIndex(Favorites._ID);
452 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
453
454 while (c != null && c.moveToNext()) {
455 long favoriteId = c.getLong(idIndex);
456 final String intentUri = c.getString(intentIndex);
457 if (intentUri != null) {
458 try {
459 Intent intent = Intent.parseUri(intentUri, 0);
460 android.util.Log.d("Home", intent.toString());
461 final Uri uri = intent.getData();
462 final String data = uri.toString();
463 if (Intent.ACTION_VIEW.equals(intent.getAction()) &&
464 (data.startsWith("content://contacts/people/") ||
465 data.startsWith("content://com.android.contacts/contacts/lookup/"))) {
466
467 intent = new Intent("com.android.contacts.action.QUICK_CONTACT");
468 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
469 Intent.FLAG_ACTIVITY_CLEAR_TOP |
470 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
471
472 intent.setData(uri);
473 intent.putExtra("mode", 3);
474 intent.putExtra("exclude_mimes", (String[]) null);
475
476 values.clear();
477 values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0));
478
479 String updateWhere = Favorites._ID + "=" + favoriteId;
480 db.update(TABLE_FAVORITES, values, updateWhere, null);
481 }
482 } catch (RuntimeException ex) {
483 Log.e(TAG, "Problem upgrading shortcut", ex);
484 } catch (URISyntaxException e) {
485 Log.e(TAG, "Problem upgrading shortcut", e);
486 }
487 }
488 }
489
490 db.setTransactionSuccessful();
491 } catch (SQLException ex) {
492 Log.w(TAG, "Problem while upgrading contacts", ex);
493 return false;
494 } finally {
495 db.endTransaction();
496 if (c != null) {
497 c.close();
498 }
499 }
500
501 return true;
502 }
503
Joe Onorato0589f0f2010-02-08 13:44:00 -0800504 private void normalizeIcons(SQLiteDatabase db) {
505 Log.d(TAG, "normalizing icons");
506
Joe Onorato346e1292010-02-18 10:34:24 -0500507 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800508 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400509 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800510 try {
511 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400512 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600513 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800514
515 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
516 Favorites.ICON_TYPE_BITMAP, null);
517
518 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
519 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
520
521 while (c.moveToNext()) {
522 long id = c.getLong(idIndex);
523 byte[] data = c.getBlob(iconIndex);
524 try {
525 Bitmap bitmap = Utilities.resampleIconBitmap(
526 BitmapFactory.decodeByteArray(data, 0, data.length),
527 mContext);
528 if (bitmap != null) {
529 update.bindLong(1, id);
530 data = ItemInfo.flattenBitmap(bitmap);
531 if (data != null) {
532 update.bindBlob(2, data);
533 update.execute();
534 }
535 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800536 }
537 } catch (Exception e) {
538 if (!logged) {
539 Log.e(TAG, "Failed normalizing icon " + id, e);
540 } else {
541 Log.e(TAG, "Also failed normalizing icon " + id);
542 }
543 logged = true;
544 }
545 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000546 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800547 } catch (SQLException ex) {
548 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
549 } finally {
550 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400551 if (update != null) {
552 update.close();
553 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800554 if (c != null) {
555 c.close();
556 }
557 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700558 }
559
560 // Generates a new ID to use for an object in your database. This method should be only
561 // called from the main UI thread. As an exception, we do call it when we call the
562 // constructor from the worker thread; however, this doesn't extend until after the
563 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
564 // after that point
565 public long generateNewId() {
566 if (mMaxId < 0) {
567 throw new RuntimeException("Error: max id was not initialized");
568 }
569 mMaxId += 1;
570 return mMaxId;
571 }
572
573 private long initializeMaxId(SQLiteDatabase db) {
574 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
575
576 // get the result
577 final int maxIdIndex = 0;
578 long id = -1;
579 if (c != null && c.moveToNext()) {
580 id = c.getLong(maxIdIndex);
581 }
582
583 if (id == -1) {
584 throw new RuntimeException("Error: could not query max id");
585 }
586
587 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800588 }
589
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800590 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700591 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000592 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800593 */
594 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000595 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800596 final int[] bindSources = new int[] {
597 Favorites.ITEM_TYPE_WIDGET_CLOCK,
598 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000599 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800600 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000601
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800602 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
603
604 Cursor c = null;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800605
606 db.beginTransaction();
607 try {
608 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000609 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800610 selectWhere, null, null, null, null);
611
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800612 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800613
614 final ContentValues values = new ContentValues();
615 while (c != null && c.moveToNext()) {
616 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000617 int favoriteType = c.getInt(1);
618
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700619 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800620 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700621 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800622
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800623 if (LOGD) {
624 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
625 + " for favoriteId=" + favoriteId);
626 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800627 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000628 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
629 values.put(Favorites.APPWIDGET_ID, appWidgetId);
630
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800631 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +0000632 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
633 values.put(LauncherSettings.Favorites.SPANX, 4);
634 values.put(LauncherSettings.Favorites.SPANY, 1);
635 } else {
636 values.put(LauncherSettings.Favorites.SPANX, 2);
637 values.put(LauncherSettings.Favorites.SPANY, 2);
638 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800639
640 String updateWhere = Favorites._ID + "=" + favoriteId;
641 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +0000642
Bjorn Bringert34251342009-12-15 13:33:11 +0000643 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
644 appWidgetManager.bindAppWidgetId(appWidgetId,
645 new ComponentName("com.android.alarmclock",
646 "com.android.alarmclock.AnalogAppWidgetProvider"));
647 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
648 appWidgetManager.bindAppWidgetId(appWidgetId,
649 new ComponentName("com.android.camera",
650 "com.android.camera.PhotoAppWidgetProvider"));
651 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
652 appWidgetManager.bindAppWidgetId(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000653 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +0000654 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800655 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800656 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800657 }
658 }
659
660 db.setTransactionSuccessful();
661 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800662 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800663 } finally {
664 db.endTransaction();
665 if (c != null) {
666 c.close();
667 }
668 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800669 }
670
671 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800672 * Loads the default set of favorite packages from an xml file.
673 *
674 * @param db The database to write the values into
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800675 */
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700676 private int loadFavorites(SQLiteDatabase db) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800677 Intent intent = new Intent(Intent.ACTION_MAIN, null);
678 intent.addCategory(Intent.CATEGORY_LAUNCHER);
679 ContentValues values = new ContentValues();
680
681 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800682 int i = 0;
683 try {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700684 XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace);
685 AttributeSet attrs = Xml.asAttributeSet(parser);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800686 XmlUtils.beginDocument(parser, TAG_FAVORITES);
687
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700688 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800689
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700690 int type;
691 while (((type = parser.next()) != XmlPullParser.END_TAG ||
692 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
693
694 if (type != XmlPullParser.START_TAG) {
695 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800696 }
697
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700698 boolean added = false;
699 final String name = parser.getName();
700
701 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
702
703 values.clear();
704 values.put(LauncherSettings.Favorites.CONTAINER,
705 LauncherSettings.Favorites.CONTAINER_DESKTOP);
706 values.put(LauncherSettings.Favorites.SCREEN,
707 a.getString(R.styleable.Favorite_screen));
708 values.put(LauncherSettings.Favorites.CELLX,
709 a.getString(R.styleable.Favorite_x));
710 values.put(LauncherSettings.Favorites.CELLY,
711 a.getString(R.styleable.Favorite_y));
712
713 if (TAG_FAVORITE.equals(name)) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700714 added = addAppShortcut(db, values, a, packageManager, intent);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700715 } else if (TAG_SEARCH.equals(name)) {
716 added = addSearchWidget(db, values);
717 } else if (TAG_CLOCK.equals(name)) {
718 added = addClockWidget(db, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700719 } else if (TAG_APPWIDGET.equals(name)) {
Romain Guy693599f2010-03-23 10:58:18 -0700720 added = addAppWidget(db, values, a, packageManager);
Mike Cleronb87bd162009-10-30 16:36:56 -0700721 } else if (TAG_SHORTCUT.equals(name)) {
722 added = addUriShortcut(db, values, a);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800723 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700724
725 if (added) i++;
726
727 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800728 }
729 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800730 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800731 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800732 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800733 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700734
735 return i;
736 }
737
Mike Cleronb87bd162009-10-30 16:36:56 -0700738 private boolean addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700739 PackageManager packageManager, Intent intent) {
740
741 ActivityInfo info;
742 String packageName = a.getString(R.styleable.Favorite_packageName);
743 String className = a.getString(R.styleable.Favorite_className);
744 try {
Romain Guy693599f2010-03-23 10:58:18 -0700745 ComponentName cn;
746 try {
747 cn = new ComponentName(packageName, className);
748 info = packageManager.getActivityInfo(cn, 0);
749 } catch (PackageManager.NameNotFoundException nnfe) {
750 String[] packages = packageManager.currentToCanonicalPackageNames(
751 new String[] { packageName });
752 cn = new ComponentName(packages[0], className);
753 info = packageManager.getActivityInfo(cn, 0);
754 }
755
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700756 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -0700757 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
758 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700759 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700760 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
761 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
762 values.put(Favorites.SPANX, 1);
763 values.put(Favorites.SPANY, 1);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700764 values.put(Favorites._ID, generateNewId());
765 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700766 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800767 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700768 "/" + className, e);
769 return false;
770 }
771 return true;
772 }
773
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000774 private ComponentName getSearchWidgetProvider() {
775 SearchManager searchManager =
776 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
777 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
778 if (searchComponent == null) return null;
779 return getProviderInPackage(searchComponent.getPackageName());
780 }
781
782 /**
783 * Gets an appwidget provider from the given package. If the package contains more than
784 * one appwidget provider, an arbitrary one is returned.
785 */
786 private ComponentName getProviderInPackage(String packageName) {
787 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
788 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
789 if (providers == null) return null;
790 final int providerCount = providers.size();
791 for (int i = 0; i < providerCount; i++) {
792 ComponentName provider = providers.get(i).provider;
793 if (provider != null && provider.getPackageName().equals(packageName)) {
794 return provider;
795 }
796 }
797 return null;
798 }
799
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700800 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000801 ComponentName cn = getSearchWidgetProvider();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000802 return addAppWidget(db, values, cn, 4, 1);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700803 }
804
805 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000806 ComponentName cn = new ComponentName("com.android.alarmclock",
807 "com.android.alarmclock.AnalogAppWidgetProvider");
808 return addAppWidget(db, values, cn, 2, 2);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800809 }
Mike Cleronb87bd162009-10-30 16:36:56 -0700810
Romain Guy693599f2010-03-23 10:58:18 -0700811 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
812 PackageManager packageManager) {
813
Mike Cleronb87bd162009-10-30 16:36:56 -0700814 String packageName = a.getString(R.styleable.Favorite_packageName);
815 String className = a.getString(R.styleable.Favorite_className);
816
817 if (packageName == null || className == null) {
818 return false;
819 }
Romain Guy693599f2010-03-23 10:58:18 -0700820
821 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -0700822 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -0700823 try {
824 packageManager.getReceiverInfo(cn, 0);
825 } catch (Exception e) {
826 String[] packages = packageManager.currentToCanonicalPackageNames(
827 new String[] { packageName });
828 cn = new ComponentName(packages[0], className);
829 try {
830 packageManager.getReceiverInfo(cn, 0);
831 } catch (Exception e1) {
832 hasPackage = false;
833 }
834 }
835
836 if (hasPackage) {
837 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
838 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
839 return addAppWidget(db, values, cn, spanX, spanY);
840 }
841
842 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +0000843 }
844
845 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
846 int spanX, int spanY) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700847 boolean allocatedAppWidgets = false;
848 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
849
850 try {
851 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
852
853 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000854 values.put(Favorites.SPANX, spanX);
855 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -0700856 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700857 values.put(Favorites._ID, generateNewId());
858 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700859
860 allocatedAppWidgets = true;
861
862 appWidgetManager.bindAppWidgetId(appWidgetId, cn);
863 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800864 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -0700865 }
866
867 return allocatedAppWidgets;
868 }
869
870 private boolean addUriShortcut(SQLiteDatabase db, ContentValues values,
871 TypedArray a) {
872 Resources r = mContext.getResources();
873
874 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
875 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
876
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800877 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -0700878 String uri = null;
879 try {
880 uri = a.getString(R.styleable.Favorite_uri);
881 intent = Intent.parseUri(uri, 0);
882 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800883 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Mike Cleronb87bd162009-10-30 16:36:56 -0700884 return false; // Oh well
885 }
886
887 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800888 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Mike Cleronb87bd162009-10-30 16:36:56 -0700889 return false;
890 }
891
892 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
893 values.put(Favorites.INTENT, intent.toUri(0));
894 values.put(Favorites.TITLE, r.getString(titleResId));
895 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
896 values.put(Favorites.SPANX, 1);
897 values.put(Favorites.SPANY, 1);
898 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
899 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
900 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700901 values.put(Favorites._ID, generateNewId());
902 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700903
904 return true;
905 }
906 }
907
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800908 /**
909 * Build a query string that will match any row where the column matches
910 * anything in the values list.
911 */
912 static String buildOrWhereString(String column, int[] values) {
913 StringBuilder selectWhere = new StringBuilder();
914 for (int i = values.length - 1; i >= 0; i--) {
915 selectWhere.append(column).append("=").append(values[i]);
916 if (i > 0) {
917 selectWhere.append(" OR ");
918 }
919 }
920 return selectWhere.toString();
921 }
922
923 static class SqlArguments {
924 public final String table;
925 public final String where;
926 public final String[] args;
927
928 SqlArguments(Uri url, String where, String[] args) {
929 if (url.getPathSegments().size() == 1) {
930 this.table = url.getPathSegments().get(0);
931 this.where = where;
932 this.args = args;
933 } else if (url.getPathSegments().size() != 2) {
934 throw new IllegalArgumentException("Invalid URI: " + url);
935 } else if (!TextUtils.isEmpty(where)) {
936 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
937 } else {
938 this.table = url.getPathSegments().get(0);
939 this.where = "_id=" + ContentUris.parseId(url);
940 this.args = null;
941 }
942 }
943
944 SqlArguments(Uri url) {
945 if (url.getPathSegments().size() == 1) {
946 table = url.getPathSegments().get(0);
947 where = null;
948 args = null;
949 } else {
950 throw new IllegalArgumentException("Invalid URI: " + url);
951 }
952 }
953 }
954}