blob: c8fb09e5801c0820405af5abe0a70beaa0b1b87d [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
Daniel Lehmannc3a80402012-04-23 21:35:11 -070019import com.android.internal.util.XmlUtils;
20import com.android.launcher.R;
21import com.android.launcher2.LauncherSettings.Favorites;
22
23import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000026import android.app.SearchManager;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070027import android.appwidget.AppWidgetHost;
Mike Cleronb87bd162009-10-30 16:36:56 -070028import android.appwidget.AppWidgetManager;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000029import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080030import android.content.ComponentName;
Adam Cohen228da5a2011-07-27 22:23:47 -070031import android.content.ContentProvider;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080032import android.content.ContentResolver;
Adam Cohen228da5a2011-07-27 22:23:47 -070033import android.content.ContentUris;
34import android.content.ContentValues;
35import android.content.Context;
36import android.content.Intent;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080037import android.content.pm.ActivityInfo;
Adam Cohen228da5a2011-07-27 22:23:47 -070038import android.content.pm.PackageManager;
39import android.content.res.Resources;
40import android.content.res.TypedArray;
41import android.content.res.XmlResourceParser;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042import android.database.Cursor;
43import android.database.SQLException;
Adam Cohen228da5a2011-07-27 22:23:47 -070044import android.database.sqlite.SQLiteDatabase;
45import android.database.sqlite.SQLiteOpenHelper;
46import android.database.sqlite.SQLiteQueryBuilder;
47import android.database.sqlite.SQLiteStatement;
Joe Onorato0589f0f2010-02-08 13:44:00 -080048import android.graphics.Bitmap;
49import android.graphics.BitmapFactory;
Adam Cohen228da5a2011-07-27 22:23:47 -070050import android.net.Uri;
51import android.provider.Settings;
52import android.text.TextUtils;
53import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080054import android.util.Log;
55import android.util.Xml;
Adam Cohen228da5a2011-07-27 22:23:47 -070056
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070058import java.net.URISyntaxException;
Adam Cohen228da5a2011-07-27 22:23:47 -070059import java.util.ArrayList;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000060import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080061
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080063 private static final String TAG = "Launcher.LauncherProvider";
64 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065
66 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070067
Daniel Lehmannc3a80402012-04-23 21:35:11 -070068 private static final int DATABASE_VERSION = 10;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080069
Joe Onoratoa5902522009-07-30 13:37:37 -070070 static final String AUTHORITY = "com.android.launcher2.settings";
Winson Chung3d503fb2011-07-13 17:25:49 -070071
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072 static final String TABLE_FAVORITES = "favorites";
73 static final String PARAMETER_NOTIFY = "notify";
74
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070075 /**
Romain Guy73b979d2009-06-09 12:57:21 -070076 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070077 * {@link AppWidgetHost#deleteHost()} is called during database creation.
78 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
79 */
80 static final Uri CONTENT_APPWIDGET_RESET_URI =
81 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
Daniel Lehmannc3a80402012-04-23 21:35:11 -070082
Michael Jurkaa8c760d2011-04-28 14:59:33 -070083 private DatabaseHelper mOpenHelper;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080084
85 @Override
86 public boolean onCreate() {
87 mOpenHelper = new DatabaseHelper(getContext());
Michael Jurkaa8c760d2011-04-28 14:59:33 -070088 ((LauncherApplication) getContext()).setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -080089 return true;
90 }
91
92 @Override
93 public String getType(Uri uri) {
94 SqlArguments args = new SqlArguments(uri, null, null);
95 if (TextUtils.isEmpty(args.where)) {
96 return "vnd.android.cursor.dir/" + args.table;
97 } else {
98 return "vnd.android.cursor.item/" + args.table;
99 }
100 }
101
102 @Override
103 public Cursor query(Uri uri, String[] projection, String selection,
104 String[] selectionArgs, String sortOrder) {
105
106 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
107 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
108 qb.setTables(args.table);
109
Romain Guy73b979d2009-06-09 12:57:21 -0700110 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800111 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
112 result.setNotificationUri(getContext().getContentResolver(), uri);
113
114 return result;
115 }
116
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700117 private static long dbInsertAndCheck(DatabaseHelper helper,
118 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
119 if (!values.containsKey(LauncherSettings.Favorites._ID)) {
120 throw new RuntimeException("Error: attempting to add item without specifying an id");
121 }
122 return db.insert(table, nullColumnHack, values);
123 }
124
Adam Cohen228da5a2011-07-27 22:23:47 -0700125 private static void deleteId(SQLiteDatabase db, long id) {
126 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
127 SqlArguments args = new SqlArguments(uri, null, null);
128 db.delete(args.table, args.where, args.args);
129 }
130
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800131 @Override
132 public Uri insert(Uri uri, ContentValues initialValues) {
133 SqlArguments args = new SqlArguments(uri);
134
135 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700136 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800137 if (rowId <= 0) return null;
138
139 uri = ContentUris.withAppendedId(uri, rowId);
140 sendNotify(uri);
141
142 return uri;
143 }
144
145 @Override
146 public int bulkInsert(Uri uri, ContentValues[] values) {
147 SqlArguments args = new SqlArguments(uri);
148
149 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
150 db.beginTransaction();
151 try {
152 int numValues = values.length;
153 for (int i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700154 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
155 return 0;
156 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800157 }
158 db.setTransactionSuccessful();
159 } finally {
160 db.endTransaction();
161 }
162
163 sendNotify(uri);
164 return values.length;
165 }
166
167 @Override
168 public int delete(Uri uri, String selection, String[] selectionArgs) {
169 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
170
171 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
172 int count = db.delete(args.table, args.where, args.args);
173 if (count > 0) sendNotify(uri);
174
175 return count;
176 }
177
178 @Override
179 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
180 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
181
182 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
183 int count = db.update(args.table, values, args.where, args.args);
184 if (count > 0) sendNotify(uri);
185
186 return count;
187 }
188
189 private void sendNotify(Uri uri) {
190 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
191 if (notify == null || "true".equals(notify)) {
192 getContext().getContentResolver().notifyChange(uri, null);
193 }
194 }
195
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700196 public long generateNewId() {
197 return mOpenHelper.generateNewId();
198 }
199
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800200 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800201 private static final String TAG_FAVORITES = "favorites";
202 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700203 private static final String TAG_CLOCK = "clock";
204 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700205 private static final String TAG_APPWIDGET = "appwidget";
206 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700207 private static final String TAG_FOLDER = "folder";
Winson Chung3d503fb2011-07-13 17:25:49 -0700208
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800209 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700210 private final AppWidgetHost mAppWidgetHost;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700211 private long mMaxId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212
213 DatabaseHelper(Context context) {
214 super(context, DATABASE_NAME, null, DATABASE_VERSION);
215 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700216 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700217
218 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
219 // the DB here
220 if (mMaxId == -1) {
221 mMaxId = initializeMaxId(getWritableDatabase());
222 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800223 }
224
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700225 /**
226 * Send notification that we've deleted the {@link AppWidgetHost},
227 * probably as part of the initial database creation. The receiver may
228 * want to re-call {@link AppWidgetHost#startListening()} to ensure
229 * callbacks are correctly set.
230 */
231 private void sendAppWidgetResetNotify() {
232 final ContentResolver resolver = mContext.getContentResolver();
233 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
234 }
235
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800236 @Override
237 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800238 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700239
240 mMaxId = 1;
241
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800242 db.execSQL("CREATE TABLE favorites (" +
243 "_id INTEGER PRIMARY KEY," +
244 "title TEXT," +
245 "intent TEXT," +
246 "container INTEGER," +
247 "screen INTEGER," +
248 "cellX INTEGER," +
249 "cellY INTEGER," +
250 "spanX INTEGER," +
251 "spanY INTEGER," +
252 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700253 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800254 "isShortcut INTEGER," +
255 "iconType INTEGER," +
256 "iconPackage TEXT," +
257 "iconResource TEXT," +
258 "icon BLOB," +
259 "uri TEXT," +
260 "displayMode INTEGER" +
261 ");");
262
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700263 // Database was just created, so wipe any previous widgets
264 if (mAppWidgetHost != null) {
265 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700266 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800267 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700268
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800269 if (!convertDatabase(db)) {
270 // Populate favorites table with initial favorites
Winson Chung6d092682011-11-16 18:43:26 -0800271 loadFavorites(db, R.xml.default_workspace);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800272 }
273 }
274
275 private boolean convertDatabase(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800276 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277 boolean converted = false;
278
279 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
280 "/old_favorites?notify=true");
281 final ContentResolver resolver = mContext.getContentResolver();
282 Cursor cursor = null;
283
284 try {
285 cursor = resolver.query(uri, null, null, null, null);
286 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700287 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800288 }
289
290 // We already have a favorites database in the old provider
291 if (cursor != null && cursor.getCount() > 0) {
292 try {
293 converted = copyFromCursor(db, cursor) > 0;
294 } finally {
295 cursor.close();
296 }
297
298 if (converted) {
299 resolver.delete(uri, null, null);
300 }
301 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700302
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800303 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700304 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800305 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800306 convertWidgets(db);
307 }
308
309 return converted;
310 }
311
312 private int copyFromCursor(SQLiteDatabase db, Cursor c) {
Romain Guy73b979d2009-06-09 12:57:21 -0700313 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800314 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
315 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
316 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
317 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
318 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
319 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
320 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
321 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
322 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
323 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
324 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
325 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
326 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
327
328 ContentValues[] rows = new ContentValues[c.getCount()];
329 int i = 0;
330 while (c.moveToNext()) {
331 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700332 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800333 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
334 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
335 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
336 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
337 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
338 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
339 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
340 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700341 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800342 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
343 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
344 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
345 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
346 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
347 rows[i++] = values;
348 }
349
350 db.beginTransaction();
351 int total = 0;
352 try {
353 int numValues = rows.length;
354 for (i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700355 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800356 return 0;
357 } else {
358 total++;
359 }
360 }
361 db.setTransactionSuccessful();
362 } finally {
363 db.endTransaction();
364 }
365
366 return total;
367 }
368
369 @Override
370 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800371 if (LOGD) Log.d(TAG, "onUpgrade triggered");
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700372
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800373 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700374 if (version < 3) {
375 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800376 db.beginTransaction();
377 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700378 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800379 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700380 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800381 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700382 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800383 } catch (SQLException ex) {
384 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800385 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800386 } finally {
387 db.endTransaction();
388 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700389
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800390 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700391 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800392 convertWidgets(db);
393 }
394 }
Romain Guy73b979d2009-06-09 12:57:21 -0700395
396 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800397 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700398 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700399
Romain Guy509cd6a2010-03-23 15:10:56 -0700400 // Where's version 5?
401 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
402 // - Passion shipped on 2.1 with version 6 of launcher2
403 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
404 // but version 5 on there was the updateContactsShortcuts change
405 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
406 // The updateContactsShortcuts change is idempotent, so running it twice
407 // is okay so we'll do that when upgrading the devices that shipped with it.
408 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800409 // We went from 3 to 5 screens. Move everything 1 to the right
410 db.beginTransaction();
411 try {
412 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
413 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800414 } catch (SQLException ex) {
415 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800416 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800417 } finally {
418 db.endTransaction();
419 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700420
Romain Guy509cd6a2010-03-23 15:10:56 -0700421 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800422 if (updateContactsShortcuts(db)) {
423 version = 6;
424 }
425 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000426
427 if (version < 7) {
428 // Version 7 gets rid of the special search widget.
429 convertWidgets(db);
430 version = 7;
431 }
432
Joe Onorato0589f0f2010-02-08 13:44:00 -0800433 if (version < 8) {
434 // Version 8 (froyo) has the icons all normalized. This should
435 // already be the case in practice, but we now rely on it and don't
436 // resample the images each time.
437 normalizeIcons(db);
438 version = 8;
439 }
440
Winson Chung3d503fb2011-07-13 17:25:49 -0700441 if (version < 9) {
442 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
443 // before it gets a change to get set, so we need to read it here when we use it)
444 if (mMaxId == -1) {
445 mMaxId = initializeMaxId(db);
446 }
447
448 // Add default hotseat icons
Winson Chung6d092682011-11-16 18:43:26 -0800449 loadFavorites(db, R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700450 version = 9;
451 }
452
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700453 if (version < 10) {
454 // Contact shortcuts need a different set of flags to be launched now
455 // The updateContactsShortcuts change is idempotent, so we can keep using it like
456 // back in the Donut days
457 updateContactsShortcuts(db);
458 version = 10;
459 }
460
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800461 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800462 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800463 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
464 onCreate(db);
465 }
466 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800467
468 private boolean updateContactsShortcuts(SQLiteDatabase db) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800469 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
470 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
471
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700472 Cursor c = null;
473 final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800474 db.beginTransaction();
475 try {
476 // Select and iterate through each matching widget
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700477 c = db.query(TABLE_FAVORITES,
478 new String[] { Favorites._ID, Favorites.INTENT },
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800479 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700480 if (c == null) return false;
481
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800482 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700483
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800484 final int idIndex = c.getColumnIndex(Favorites._ID);
485 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700486
487 while (c.moveToNext()) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800488 long favoriteId = c.getLong(idIndex);
489 final String intentUri = c.getString(intentIndex);
490 if (intentUri != null) {
491 try {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700492 final Intent intent = Intent.parseUri(intentUri, 0);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800493 android.util.Log.d("Home", intent.toString());
494 final Uri uri = intent.getData();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700495 if (uri != null) {
496 final String data = uri.toString();
497 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
498 actionQuickContact.equals(intent.getAction())) &&
499 (data.startsWith("content://contacts/people/") ||
500 data.startsWith("content://com.android.contacts/" +
501 "contacts/lookup/"))) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800502
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700503 final Intent newIntent = new Intent(actionQuickContact);
504 // When starting from the launcher, start in a new, cleared task
505 // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
506 // clear the whole thing preemptively here since
507 // QuickContactActivity will finish itself when launching other
508 // detail activities.
509 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
510 Intent.FLAG_ACTIVITY_CLEAR_TASK);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800511
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700512 newIntent.setData(uri);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800513
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700514 final ContentValues values = new ContentValues();
515 values.put(LauncherSettings.Favorites.INTENT,
516 newIntent.toUri(0));
517
518 String updateWhere = Favorites._ID + "=" + favoriteId;
519 db.update(TABLE_FAVORITES, values, updateWhere, null);
520 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800521 }
522 } catch (RuntimeException ex) {
523 Log.e(TAG, "Problem upgrading shortcut", ex);
524 } catch (URISyntaxException e) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700525 Log.e(TAG, "Problem upgrading shortcut", e);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800526 }
527 }
528 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700529
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800530 db.setTransactionSuccessful();
531 } catch (SQLException ex) {
532 Log.w(TAG, "Problem while upgrading contacts", ex);
533 return false;
534 } finally {
535 db.endTransaction();
536 if (c != null) {
537 c.close();
538 }
539 }
540
541 return true;
542 }
543
Joe Onorato0589f0f2010-02-08 13:44:00 -0800544 private void normalizeIcons(SQLiteDatabase db) {
545 Log.d(TAG, "normalizing icons");
546
Joe Onorato346e1292010-02-18 10:34:24 -0500547 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800548 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400549 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800550 try {
551 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400552 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600553 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800554
555 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
556 Favorites.ICON_TYPE_BITMAP, null);
557
558 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
559 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
560
561 while (c.moveToNext()) {
562 long id = c.getLong(idIndex);
563 byte[] data = c.getBlob(iconIndex);
564 try {
565 Bitmap bitmap = Utilities.resampleIconBitmap(
566 BitmapFactory.decodeByteArray(data, 0, data.length),
567 mContext);
568 if (bitmap != null) {
569 update.bindLong(1, id);
570 data = ItemInfo.flattenBitmap(bitmap);
571 if (data != null) {
572 update.bindBlob(2, data);
573 update.execute();
574 }
575 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800576 }
577 } catch (Exception e) {
578 if (!logged) {
579 Log.e(TAG, "Failed normalizing icon " + id, e);
580 } else {
581 Log.e(TAG, "Also failed normalizing icon " + id);
582 }
583 logged = true;
584 }
585 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000586 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800587 } catch (SQLException ex) {
588 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
589 } finally {
590 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400591 if (update != null) {
592 update.close();
593 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800594 if (c != null) {
595 c.close();
596 }
597 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700598 }
599
600 // Generates a new ID to use for an object in your database. This method should be only
601 // called from the main UI thread. As an exception, we do call it when we call the
602 // constructor from the worker thread; however, this doesn't extend until after the
603 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
604 // after that point
605 public long generateNewId() {
606 if (mMaxId < 0) {
607 throw new RuntimeException("Error: max id was not initialized");
608 }
609 mMaxId += 1;
610 return mMaxId;
611 }
612
613 private long initializeMaxId(SQLiteDatabase db) {
614 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
615
616 // get the result
617 final int maxIdIndex = 0;
618 long id = -1;
619 if (c != null && c.moveToNext()) {
620 id = c.getLong(maxIdIndex);
621 }
Michael Jurka5130e402011-10-13 04:55:35 -0700622 if (c != null) {
623 c.close();
624 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700625
626 if (id == -1) {
627 throw new RuntimeException("Error: could not query max id");
628 }
629
630 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800631 }
632
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800633 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700634 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000635 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800636 */
637 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000638 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800639 final int[] bindSources = new int[] {
640 Favorites.ITEM_TYPE_WIDGET_CLOCK,
641 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000642 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800643 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000644
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800645 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700646
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800647 Cursor c = null;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700648
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800649 db.beginTransaction();
650 try {
651 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000652 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800653 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700654
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800655 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700656
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800657 final ContentValues values = new ContentValues();
658 while (c != null && c.moveToNext()) {
659 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000660 int favoriteType = c.getInt(1);
661
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700662 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800663 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700664 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700665
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800666 if (LOGD) {
667 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
668 + " for favoriteId=" + favoriteId);
669 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800670 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000671 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
672 values.put(Favorites.APPWIDGET_ID, appWidgetId);
673
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800674 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +0000675 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
676 values.put(LauncherSettings.Favorites.SPANX, 4);
677 values.put(LauncherSettings.Favorites.SPANY, 1);
678 } else {
679 values.put(LauncherSettings.Favorites.SPANX, 2);
680 values.put(LauncherSettings.Favorites.SPANY, 2);
681 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800682
683 String updateWhere = Favorites._ID + "=" + favoriteId;
684 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +0000685
Bjorn Bringert34251342009-12-15 13:33:11 +0000686 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
687 appWidgetManager.bindAppWidgetId(appWidgetId,
688 new ComponentName("com.android.alarmclock",
689 "com.android.alarmclock.AnalogAppWidgetProvider"));
690 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
691 appWidgetManager.bindAppWidgetId(appWidgetId,
692 new ComponentName("com.android.camera",
693 "com.android.camera.PhotoAppWidgetProvider"));
694 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
695 appWidgetManager.bindAppWidgetId(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000696 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +0000697 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800698 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800699 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800700 }
701 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700702
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800703 db.setTransactionSuccessful();
704 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800705 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800706 } finally {
707 db.endTransaction();
708 if (c != null) {
709 c.close();
710 }
711 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800712 }
713
714 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800715 * Loads the default set of favorite packages from an xml file.
716 *
717 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -0700718 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800719 */
Winson Chung6d092682011-11-16 18:43:26 -0800720 private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800721 Intent intent = new Intent(Intent.ACTION_MAIN, null);
722 intent.addCategory(Intent.CATEGORY_LAUNCHER);
723 ContentValues values = new ContentValues();
724
725 PackageManager packageManager = mContext.getPackageManager();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -0800726 int allAppsButtonRank =
727 mContext.getResources().getInteger(R.integer.hotseat_all_apps_index);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800728 int i = 0;
729 try {
Winson Chung6d092682011-11-16 18:43:26 -0800730 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700731 AttributeSet attrs = Xml.asAttributeSet(parser);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800732 XmlUtils.beginDocument(parser, TAG_FAVORITES);
733
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700734 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800735
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700736 int type;
737 while (((type = parser.next()) != XmlPullParser.END_TAG ||
738 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
739
740 if (type != XmlPullParser.START_TAG) {
741 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800742 }
743
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700744 boolean added = false;
745 final String name = parser.getName();
746
747 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
748
Winson Chung3d503fb2011-07-13 17:25:49 -0700749 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
750 if (a.hasValue(R.styleable.Favorite_container)) {
751 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
752 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700753
Winson Chung6d092682011-11-16 18:43:26 -0800754 String screen = a.getString(R.styleable.Favorite_screen);
755 String x = a.getString(R.styleable.Favorite_x);
756 String y = a.getString(R.styleable.Favorite_y);
757
758 // If we are adding to the hotseat, the screen is used as the position in the
759 // hotseat. This screen can't be at position 0 because AllApps is in the
760 // zeroth position.
Andrew Flynn0dca1ec2012-02-29 13:33:22 -0800761 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
762 && Integer.valueOf(screen) == allAppsButtonRank) {
Winson Chung6d092682011-11-16 18:43:26 -0800763 throw new RuntimeException("Invalid screen position for hotseat item");
764 }
765
766 values.clear();
767 values.put(LauncherSettings.Favorites.CONTAINER, container);
768 values.put(LauncherSettings.Favorites.SCREEN, screen);
769 values.put(LauncherSettings.Favorites.CELLX, x);
770 values.put(LauncherSettings.Favorites.CELLY, y);
771
772 if (TAG_FAVORITE.equals(name)) {
773 long id = addAppShortcut(db, values, a, packageManager, intent);
774 added = id >= 0;
775 } else if (TAG_SEARCH.equals(name)) {
776 added = addSearchWidget(db, values);
777 } else if (TAG_CLOCK.equals(name)) {
778 added = addClockWidget(db, values);
779 } else if (TAG_APPWIDGET.equals(name)) {
780 added = addAppWidget(db, values, a, packageManager);
781 } else if (TAG_SHORTCUT.equals(name)) {
782 long id = addUriShortcut(db, values, a);
783 added = id >= 0;
784 } else if (TAG_FOLDER.equals(name)) {
785 String title;
786 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
787 if (titleResId != -1) {
788 title = mContext.getResources().getString(titleResId);
789 } else {
790 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -0700791 }
Winson Chung6d092682011-11-16 18:43:26 -0800792 values.put(LauncherSettings.Favorites.TITLE, title);
793 long folderId = addFolder(db, values);
794 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -0700795
Winson Chung6d092682011-11-16 18:43:26 -0800796 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -0700797
Winson Chung6d092682011-11-16 18:43:26 -0800798 int folderDepth = parser.getDepth();
799 while ((type = parser.next()) != XmlPullParser.END_TAG ||
800 parser.getDepth() > folderDepth) {
801 if (type != XmlPullParser.START_TAG) {
802 continue;
803 }
804 final String folder_item_name = parser.getName();
805
806 TypedArray ar = mContext.obtainStyledAttributes(attrs,
807 R.styleable.Favorite);
808 values.clear();
809 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
810
811 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
812 long id =
813 addAppShortcut(db, values, ar, packageManager, intent);
814 if (id >= 0) {
815 folderItems.add(id);
816 }
817 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
818 long id = addUriShortcut(db, values, ar);
819 if (id >= 0) {
820 folderItems.add(id);
821 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700822 } else {
Winson Chung6d092682011-11-16 18:43:26 -0800823 throw new RuntimeException("Folders can " +
824 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -0700825 }
Winson Chung6d092682011-11-16 18:43:26 -0800826 ar.recycle();
827 }
828 // We can only have folders with >= 2 items, so we need to remove the
829 // folder and clean up if less than 2 items were included, or some
830 // failed to add, and less than 2 were actually added
831 if (folderItems.size() < 2 && folderId >= 0) {
832 // We just delete the folder and any items that made it
833 deleteId(db, folderId);
834 if (folderItems.size() > 0) {
835 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -0700836 }
Winson Chung6d092682011-11-16 18:43:26 -0800837 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -0700838 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800839 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700840 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700841 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800842 }
843 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800844 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800845 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800846 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -0700847 } catch (RuntimeException e) {
848 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800849 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700850
851 return i;
852 }
853
Adam Cohen228da5a2011-07-27 22:23:47 -0700854 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700855 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -0700856 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700857 ActivityInfo info;
858 String packageName = a.getString(R.styleable.Favorite_packageName);
859 String className = a.getString(R.styleable.Favorite_className);
860 try {
Romain Guy693599f2010-03-23 10:58:18 -0700861 ComponentName cn;
862 try {
863 cn = new ComponentName(packageName, className);
864 info = packageManager.getActivityInfo(cn, 0);
865 } catch (PackageManager.NameNotFoundException nnfe) {
866 String[] packages = packageManager.currentToCanonicalPackageNames(
867 new String[] { packageName });
868 cn = new ComponentName(packages[0], className);
869 info = packageManager.getActivityInfo(cn, 0);
870 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700871 id = generateNewId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700872 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -0700873 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
874 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700875 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700876 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
877 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
878 values.put(Favorites.SPANX, 1);
879 values.put(Favorites.SPANY, 1);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700880 values.put(Favorites._ID, generateNewId());
Adam Cohen228da5a2011-07-27 22:23:47 -0700881 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
882 return -1;
883 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700884 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800885 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700886 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700887 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700888 return id;
889 }
890
891 private long addFolder(SQLiteDatabase db, ContentValues values) {
892 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
893 values.put(Favorites.SPANX, 1);
894 values.put(Favorites.SPANY, 1);
895 long id = generateNewId();
896 values.put(Favorites._ID, id);
897 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
898 return -1;
899 } else {
900 return id;
901 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700902 }
903
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000904 private ComponentName getSearchWidgetProvider() {
905 SearchManager searchManager =
906 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
907 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
908 if (searchComponent == null) return null;
909 return getProviderInPackage(searchComponent.getPackageName());
910 }
911
912 /**
913 * Gets an appwidget provider from the given package. If the package contains more than
914 * one appwidget provider, an arbitrary one is returned.
915 */
916 private ComponentName getProviderInPackage(String packageName) {
917 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
918 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
919 if (providers == null) return null;
920 final int providerCount = providers.size();
921 for (int i = 0; i < providerCount; i++) {
922 ComponentName provider = providers.get(i).provider;
923 if (provider != null && provider.getPackageName().equals(packageName)) {
924 return provider;
925 }
926 }
927 return null;
928 }
929
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700930 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000931 ComponentName cn = getSearchWidgetProvider();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000932 return addAppWidget(db, values, cn, 4, 1);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700933 }
934
935 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000936 ComponentName cn = new ComponentName("com.android.alarmclock",
937 "com.android.alarmclock.AnalogAppWidgetProvider");
938 return addAppWidget(db, values, cn, 2, 2);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800939 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700940
Romain Guy693599f2010-03-23 10:58:18 -0700941 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a,
942 PackageManager packageManager) {
943
Mike Cleronb87bd162009-10-30 16:36:56 -0700944 String packageName = a.getString(R.styleable.Favorite_packageName);
945 String className = a.getString(R.styleable.Favorite_className);
946
947 if (packageName == null || className == null) {
948 return false;
949 }
Romain Guy693599f2010-03-23 10:58:18 -0700950
951 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -0700952 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -0700953 try {
954 packageManager.getReceiverInfo(cn, 0);
955 } catch (Exception e) {
956 String[] packages = packageManager.currentToCanonicalPackageNames(
957 new String[] { packageName });
958 cn = new ComponentName(packages[0], className);
959 try {
960 packageManager.getReceiverInfo(cn, 0);
961 } catch (Exception e1) {
962 hasPackage = false;
963 }
964 }
965
966 if (hasPackage) {
967 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
968 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
969 return addAppWidget(db, values, cn, spanX, spanY);
970 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700971
Romain Guy693599f2010-03-23 10:58:18 -0700972 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +0000973 }
974
975 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
976 int spanX, int spanY) {
Mike Cleronb87bd162009-10-30 16:36:56 -0700977 boolean allocatedAppWidgets = false;
978 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
979
980 try {
981 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700982
Mike Cleronb87bd162009-10-30 16:36:56 -0700983 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000984 values.put(Favorites.SPANX, spanX);
985 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -0700986 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700987 values.put(Favorites._ID, generateNewId());
988 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -0700989
990 allocatedAppWidgets = true;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700991
Mike Cleronb87bd162009-10-30 16:36:56 -0700992 appWidgetManager.bindAppWidgetId(appWidgetId, cn);
993 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800994 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -0700995 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700996
Mike Cleronb87bd162009-10-30 16:36:56 -0700997 return allocatedAppWidgets;
998 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700999
1000 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -07001001 TypedArray a) {
1002 Resources r = mContext.getResources();
1003
1004 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
1005 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
1006
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001007 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -07001008 String uri = null;
1009 try {
1010 uri = a.getString(R.styleable.Favorite_uri);
1011 intent = Intent.parseUri(uri, 0);
1012 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001013 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -07001014 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -07001015 }
1016
1017 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001018 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001019 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001020 }
1021
Adam Cohen228da5a2011-07-27 22:23:47 -07001022 long id = generateNewId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001023 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1024 values.put(Favorites.INTENT, intent.toUri(0));
1025 values.put(Favorites.TITLE, r.getString(titleResId));
1026 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1027 values.put(Favorites.SPANX, 1);
1028 values.put(Favorites.SPANY, 1);
1029 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1030 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1031 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001032 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001033
Adam Cohen228da5a2011-07-27 22:23:47 -07001034 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1035 return -1;
1036 }
1037 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001038 }
1039 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001040
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001041 /**
1042 * Build a query string that will match any row where the column matches
1043 * anything in the values list.
1044 */
1045 static String buildOrWhereString(String column, int[] values) {
1046 StringBuilder selectWhere = new StringBuilder();
1047 for (int i = values.length - 1; i >= 0; i--) {
1048 selectWhere.append(column).append("=").append(values[i]);
1049 if (i > 0) {
1050 selectWhere.append(" OR ");
1051 }
1052 }
1053 return selectWhere.toString();
1054 }
1055
1056 static class SqlArguments {
1057 public final String table;
1058 public final String where;
1059 public final String[] args;
1060
1061 SqlArguments(Uri url, String where, String[] args) {
1062 if (url.getPathSegments().size() == 1) {
1063 this.table = url.getPathSegments().get(0);
1064 this.where = where;
1065 this.args = args;
1066 } else if (url.getPathSegments().size() != 2) {
1067 throw new IllegalArgumentException("Invalid URI: " + url);
1068 } else if (!TextUtils.isEmpty(where)) {
1069 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1070 } else {
1071 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001072 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001073 this.args = null;
1074 }
1075 }
1076
1077 SqlArguments(Uri url) {
1078 if (url.getPathSegments().size() == 1) {
1079 table = url.getPathSegments().get(0);
1080 where = null;
1081 args = null;
1082 } else {
1083 throw new IllegalArgumentException("Invalid URI: " + url);
1084 }
1085 }
1086 }
1087}