blob: fc38cdd7fb8256f060e11511dd61bc013af9b9e6 [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
17package com.android.launcher;
18
The Android Open Source Project7376fae2009-03-11 12:11:58 -070019import android.appwidget.AppWidgetHost;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080020import android.content.ContentProvider;
21import android.content.Context;
22import android.content.ContentValues;
23import android.content.Intent;
24import android.content.ComponentName;
25import android.content.ContentUris;
26import android.content.ContentResolver;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070027import android.content.res.XmlResourceParser;
28import android.content.res.TypedArray;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080029import android.content.pm.PackageManager;
30import android.content.pm.ActivityInfo;
31import android.database.sqlite.SQLiteOpenHelper;
32import android.database.sqlite.SQLiteDatabase;
33import android.database.sqlite.SQLiteQueryBuilder;
34import android.database.Cursor;
35import android.database.SQLException;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.util.Log;
37import android.util.Xml;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070038import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080039import android.net.Uri;
40import android.text.TextUtils;
41import android.os.*;
42import android.provider.Settings;
43
The Android Open Source Project31dd5032009-03-03 19:32:27 -080044import java.io.IOException;
45import java.util.ArrayList;
46
The Android Open Source Project31dd5032009-03-03 19:32:27 -080047import org.xmlpull.v1.XmlPullParserException;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -070048import org.xmlpull.v1.XmlPullParser;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049import com.android.internal.util.XmlUtils;
50import com.android.launcher.LauncherSettings.Favorites;
51
52public class LauncherProvider extends ContentProvider {
53 private static final String LOG_TAG = "LauncherProvider";
54 private static final boolean LOGD = true;
55
56 private static final String DATABASE_NAME = "launcher.db";
57
Romain Guy73b979d2009-06-09 12:57:21 -070058 private static final int DATABASE_VERSION = 4;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059
60 static final String AUTHORITY = "com.android.launcher.settings";
61
62 static final String EXTRA_BIND_SOURCES = "com.android.launcher.settings.bindsources";
63 static final String EXTRA_BIND_TARGETS = "com.android.launcher.settings.bindtargets";
64
65 static final String TABLE_FAVORITES = "favorites";
Romain Guy73b979d2009-06-09 12:57:21 -070066 static final String TABLE_GESTURES = "gestures";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080067 static final String PARAMETER_NOTIFY = "notify";
68
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070069 /**
Romain Guy73b979d2009-06-09 12:57:21 -070070 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070071 * {@link AppWidgetHost#deleteHost()} is called during database creation.
72 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
73 */
74 static final Uri CONTENT_APPWIDGET_RESET_URI =
75 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
76
The Android Open Source Project31dd5032009-03-03 19:32:27 -080077 private SQLiteOpenHelper mOpenHelper;
78
79 @Override
80 public boolean onCreate() {
81 mOpenHelper = new DatabaseHelper(getContext());
82 return true;
83 }
84
85 @Override
86 public String getType(Uri uri) {
87 SqlArguments args = new SqlArguments(uri, null, null);
88 if (TextUtils.isEmpty(args.where)) {
89 return "vnd.android.cursor.dir/" + args.table;
90 } else {
91 return "vnd.android.cursor.item/" + args.table;
92 }
93 }
94
95 @Override
96 public Cursor query(Uri uri, String[] projection, String selection,
97 String[] selectionArgs, String sortOrder) {
98
99 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
100 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
101 qb.setTables(args.table);
102
Romain Guy73b979d2009-06-09 12:57:21 -0700103 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800104 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
105 result.setNotificationUri(getContext().getContentResolver(), uri);
106
107 return result;
108 }
109
110 @Override
111 public Uri insert(Uri uri, ContentValues initialValues) {
112 SqlArguments args = new SqlArguments(uri);
113
114 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
115 final long rowId = db.insert(args.table, null, initialValues);
116 if (rowId <= 0) return null;
117
118 uri = ContentUris.withAppendedId(uri, rowId);
119 sendNotify(uri);
120
121 return uri;
122 }
123
124 @Override
125 public int bulkInsert(Uri uri, ContentValues[] values) {
126 SqlArguments args = new SqlArguments(uri);
127
128 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
129 db.beginTransaction();
130 try {
131 int numValues = values.length;
132 for (int i = 0; i < numValues; i++) {
133 if (db.insert(args.table, null, values[i]) < 0) return 0;
134 }
135 db.setTransactionSuccessful();
136 } finally {
137 db.endTransaction();
138 }
139
140 sendNotify(uri);
141 return values.length;
142 }
143
144 @Override
145 public int delete(Uri uri, String selection, String[] selectionArgs) {
146 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
147
148 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
149 int count = db.delete(args.table, args.where, args.args);
150 if (count > 0) sendNotify(uri);
151
152 return count;
153 }
154
155 @Override
156 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
157 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
158
159 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
160 int count = db.update(args.table, values, args.where, args.args);
161 if (count > 0) sendNotify(uri);
162
163 return count;
164 }
165
166 private void sendNotify(Uri uri) {
167 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
168 if (notify == null || "true".equals(notify)) {
169 getContext().getContentResolver().notifyChange(uri, null);
170 }
171 }
172
173 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800174 private static final String TAG_FAVORITES = "favorites";
175 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700176 private static final String TAG_CLOCK = "clock";
177 private static final String TAG_SEARCH = "search";
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800178
179 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700180 private final AppWidgetHost mAppWidgetHost;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800181
182 DatabaseHelper(Context context) {
183 super(context, DATABASE_NAME, null, DATABASE_VERSION);
184 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700185 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800186 }
187
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700188 /**
189 * Send notification that we've deleted the {@link AppWidgetHost},
190 * probably as part of the initial database creation. The receiver may
191 * want to re-call {@link AppWidgetHost#startListening()} to ensure
192 * callbacks are correctly set.
193 */
194 private void sendAppWidgetResetNotify() {
195 final ContentResolver resolver = mContext.getContentResolver();
196 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
197 }
198
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800199 @Override
200 public void onCreate(SQLiteDatabase db) {
201 if (LOGD) Log.d(LOG_TAG, "creating new launcher database");
202
203 db.execSQL("CREATE TABLE favorites (" +
204 "_id INTEGER PRIMARY KEY," +
205 "title TEXT," +
206 "intent TEXT," +
207 "container INTEGER," +
208 "screen INTEGER," +
209 "cellX INTEGER," +
210 "cellY INTEGER," +
211 "spanX INTEGER," +
212 "spanY INTEGER," +
213 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700214 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800215 "isShortcut INTEGER," +
216 "iconType INTEGER," +
217 "iconPackage TEXT," +
218 "iconResource TEXT," +
219 "icon BLOB," +
220 "uri TEXT," +
221 "displayMode INTEGER" +
222 ");");
223
Romain Guy73b979d2009-06-09 12:57:21 -0700224 db.execSQL("CREATE TABLE gestures (" +
225 "_id INTEGER PRIMARY KEY," +
226 "title TEXT," +
227 "intent TEXT," +
228 "itemType INTEGER," +
229 "iconType INTEGER," +
230 "iconPackage TEXT," +
231 "iconResource TEXT," +
232 "icon BLOB" +
233 ");");
234
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700235 // Database was just created, so wipe any previous widgets
236 if (mAppWidgetHost != null) {
237 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700238 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800239 }
240
241 if (!convertDatabase(db)) {
242 // Populate favorites table with initial favorites
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700243 loadFavorites(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800244 }
245 }
246
247 private boolean convertDatabase(SQLiteDatabase db) {
248 if (LOGD) Log.d(LOG_TAG, "converting database from an older format, but not onUpgrade");
249 boolean converted = false;
250
251 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
252 "/old_favorites?notify=true");
253 final ContentResolver resolver = mContext.getContentResolver();
254 Cursor cursor = null;
255
256 try {
257 cursor = resolver.query(uri, null, null, null, null);
258 } catch (Exception e) {
259 // Ignore
260 }
261
262 // We already have a favorites database in the old provider
263 if (cursor != null && cursor.getCount() > 0) {
264 try {
265 converted = copyFromCursor(db, cursor) > 0;
266 } finally {
267 cursor.close();
268 }
269
270 if (converted) {
271 resolver.delete(uri, null, null);
272 }
273 }
274
275 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700276 // Convert widgets from this import into widgets
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800277 if (LOGD) Log.d(LOG_TAG, "converted and now triggering widget upgrade");
278 convertWidgets(db);
279 }
280
281 return converted;
282 }
283
284 private int copyFromCursor(SQLiteDatabase db, Cursor c) {
Romain Guy73b979d2009-06-09 12:57:21 -0700285 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800286 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
287 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
288 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
289 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
290 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
291 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
292 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
293 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
294 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
295 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
296 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
297 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
298 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
299
300 ContentValues[] rows = new ContentValues[c.getCount()];
301 int i = 0;
302 while (c.moveToNext()) {
303 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700304 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800305 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
306 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
307 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
308 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
309 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
310 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
311 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
312 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700313 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800314 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
315 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
316 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
317 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
318 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
319 rows[i++] = values;
320 }
321
322 db.beginTransaction();
323 int total = 0;
324 try {
325 int numValues = rows.length;
326 for (i = 0; i < numValues; i++) {
327 if (db.insert(TABLE_FAVORITES, null, rows[i]) < 0) {
328 return 0;
329 } else {
330 total++;
331 }
332 }
333 db.setTransactionSuccessful();
334 } finally {
335 db.endTransaction();
336 }
337
338 return total;
339 }
340
341 @Override
342 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
343 if (LOGD) Log.d(LOG_TAG, "onUpgrade triggered");
344
345 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700346 if (version < 3) {
347 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800348 db.beginTransaction();
349 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700350 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800351 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700352 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800353 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700354 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800355 } catch (SQLException ex) {
356 // Old version remains, which means we wipe old data
357 Log.e(LOG_TAG, ex.getMessage(), ex);
358 } finally {
359 db.endTransaction();
360 }
361
362 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700363 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800364 convertWidgets(db);
365 }
366 }
Romain Guy73b979d2009-06-09 12:57:21 -0700367
368 if (version < 4) {
369 db.beginTransaction();
370 try {
371 db.execSQL("CREATE TABLE gestures (" +
372 "_id INTEGER PRIMARY KEY," +
373 "title TEXT," +
374 "intent TEXT," +
375 "itemType INTEGER," +
376 "iconType INTEGER," +
377 "iconPackage TEXT," +
378 "iconResource TEXT," +
379 "icon BLOB" +
380 ");");
381 db.setTransactionSuccessful();
382 version = 4;
383 } catch (SQLException ex) {
384 // Old version remains, which means we wipe old data
385 Log.e(LOG_TAG, ex.getMessage(), ex);
386 } finally {
387 db.endTransaction();
388 }
389 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800390
391 if (version != DATABASE_VERSION) {
392 Log.w(LOG_TAG, "Destroying all old data.");
393 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
Romain Guy417d2342009-06-19 14:28:51 -0700394 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GESTURES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800395 onCreate(db);
396 }
397 }
398
399 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700400 * Upgrade existing clock and photo frame widgets into their new widget
401 * equivalents. This method allocates appWidgetIds, and then hands off to
402 * LauncherAppWidgetBinder to finish the actual binding.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800403 */
404 private void convertWidgets(SQLiteDatabase db) {
405 final int[] bindSources = new int[] {
406 Favorites.ITEM_TYPE_WIDGET_CLOCK,
407 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
408 };
409
410 final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
411 bindTargets.add(new ComponentName("com.android.alarmclock",
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700412 "com.android.alarmclock.AnalogAppWidgetProvider"));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800413 bindTargets.add(new ComponentName("com.android.camera",
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700414 "com.android.camera.PhotoAppWidgetProvider"));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800415
416 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
417
418 Cursor c = null;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700419 boolean allocatedAppWidgets = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800420
421 db.beginTransaction();
422 try {
423 // Select and iterate through each matching widget
424 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID },
425 selectWhere, null, null, null, null);
426
427 if (LOGD) Log.d(LOG_TAG, "found upgrade cursor count="+c.getCount());
428
429 final ContentValues values = new ContentValues();
430 while (c != null && c.moveToNext()) {
431 long favoriteId = c.getLong(0);
432
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700433 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800434 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700435 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800436
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700437 if (LOGD) Log.d(LOG_TAG, "allocated appWidgetId="+appWidgetId+" for favoriteId="+favoriteId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800438
439 values.clear();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700440 values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800441
442 // Original widgets might not have valid spans when upgrading
443 values.put(LauncherSettings.Favorites.SPANX, 2);
444 values.put(LauncherSettings.Favorites.SPANY, 2);
445
446 String updateWhere = Favorites._ID + "=" + favoriteId;
447 db.update(TABLE_FAVORITES, values, updateWhere, null);
448
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700449 allocatedAppWidgets = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800450 } catch (RuntimeException ex) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700451 Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800452 }
453 }
454
455 db.setTransactionSuccessful();
456 } catch (SQLException ex) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700457 Log.w(LOG_TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800458 } finally {
459 db.endTransaction();
460 if (c != null) {
461 c.close();
462 }
463 }
464
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700465 // If any appWidgetIds allocated, then launch over to binder
466 if (allocatedAppWidgets) {
467 launchAppWidgetBinder(bindSources, bindTargets);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800468 }
469 }
470
471 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700472 * Launch the widget binder that walks through the Launcher database,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800473 * binding any matching widgets to the corresponding targets. We can't
474 * bind ourselves because our parent process can't obtain the
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700475 * BIND_APPWIDGET permission.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800476 */
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700477 private void launchAppWidgetBinder(int[] bindSources, ArrayList<ComponentName> bindTargets) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800478 final Intent intent = new Intent();
479 intent.setComponent(new ComponentName("com.android.settings",
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700480 "com.android.settings.LauncherAppWidgetBinder"));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800481 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
482
483 final Bundle extras = new Bundle();
484 extras.putIntArray(EXTRA_BIND_SOURCES, bindSources);
485 extras.putParcelableArrayList(EXTRA_BIND_TARGETS, bindTargets);
486 intent.putExtras(extras);
487
488 mContext.startActivity(intent);
489 }
490
491 /**
492 * Loads the default set of favorite packages from an xml file.
493 *
494 * @param db The database to write the values into
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800495 */
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700496 private int loadFavorites(SQLiteDatabase db) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800497 Intent intent = new Intent(Intent.ACTION_MAIN, null);
498 intent.addCategory(Intent.CATEGORY_LAUNCHER);
499 ContentValues values = new ContentValues();
500
501 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800502 int i = 0;
503 try {
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700504 XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace);
505 AttributeSet attrs = Xml.asAttributeSet(parser);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800506 XmlUtils.beginDocument(parser, TAG_FAVORITES);
507
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700508 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800509
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700510 int type;
511 while (((type = parser.next()) != XmlPullParser.END_TAG ||
512 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
513
514 if (type != XmlPullParser.START_TAG) {
515 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800516 }
517
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700518 boolean added = false;
519 final String name = parser.getName();
520
521 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
522
523 values.clear();
524 values.put(LauncherSettings.Favorites.CONTAINER,
525 LauncherSettings.Favorites.CONTAINER_DESKTOP);
526 values.put(LauncherSettings.Favorites.SCREEN,
527 a.getString(R.styleable.Favorite_screen));
528 values.put(LauncherSettings.Favorites.CELLX,
529 a.getString(R.styleable.Favorite_x));
530 values.put(LauncherSettings.Favorites.CELLY,
531 a.getString(R.styleable.Favorite_y));
532
533 if (TAG_FAVORITE.equals(name)) {
534 added = addShortcut(db, values, a, packageManager, intent);
535 } else if (TAG_SEARCH.equals(name)) {
536 added = addSearchWidget(db, values);
537 } else if (TAG_CLOCK.equals(name)) {
538 added = addClockWidget(db, values);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800539 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700540
541 if (added) i++;
542
543 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800544 }
545 } catch (XmlPullParserException e) {
546 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
547 } catch (IOException e) {
548 Log.w(LOG_TAG, "Got exception parsing favorites.", e);
549 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700550
551 return i;
552 }
553
554 private boolean addShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
555 PackageManager packageManager, Intent intent) {
556
557 ActivityInfo info;
558 String packageName = a.getString(R.styleable.Favorite_packageName);
559 String className = a.getString(R.styleable.Favorite_className);
560 try {
561 ComponentName cn = new ComponentName(packageName, className);
562 info = packageManager.getActivityInfo(cn, 0);
563 intent.setComponent(cn);
Dianne Hackbornbd2e54d2009-03-24 21:00:33 -0700564 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
565 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -0700566 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700567 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
568 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
569 values.put(Favorites.SPANX, 1);
570 values.put(Favorites.SPANY, 1);
571 db.insert(TABLE_FAVORITES, null, values);
572 } catch (PackageManager.NameNotFoundException e) {
573 Log.w(LOG_TAG, "Unable to add favorite: " + packageName +
574 "/" + className, e);
575 return false;
576 }
577 return true;
578 }
579
580 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800581 // Add a search box
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700582 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_WIDGET_SEARCH);
583 values.put(Favorites.SPANX, 4);
584 values.put(Favorites.SPANY, 1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800585 db.insert(TABLE_FAVORITES, null, values);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700586
587 return true;
588 }
589
590 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800591 final int[] bindSources = new int[] {
592 Favorites.ITEM_TYPE_WIDGET_CLOCK,
593 };
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700594
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800595 final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>();
596 bindTargets.add(new ComponentName("com.android.alarmclock",
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700597 "com.android.alarmclock.AnalogAppWidgetProvider"));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700598
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700599 boolean allocatedAppWidgets = false;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700600
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700601 // Try binding to an analog clock widget
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800602 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700603 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700604
605 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_WIDGET_CLOCK);
606 values.put(Favorites.SPANX, 2);
607 values.put(Favorites.SPANY, 2);
608 values.put(Favorites.APPWIDGET_ID, appWidgetId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800609 db.insert(TABLE_FAVORITES, null, values);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700610
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700611 allocatedAppWidgets = true;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800612 } catch (RuntimeException ex) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700613 Log.e(LOG_TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800614 }
615
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700616 // If any appWidgetIds allocated, then launch over to binder
617 if (allocatedAppWidgets) {
618 launchAppWidgetBinder(bindSources, bindTargets);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800619 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700620
621 return allocatedAppWidgets;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800622 }
623 }
624
625 /**
626 * Build a query string that will match any row where the column matches
627 * anything in the values list.
628 */
629 static String buildOrWhereString(String column, int[] values) {
630 StringBuilder selectWhere = new StringBuilder();
631 for (int i = values.length - 1; i >= 0; i--) {
632 selectWhere.append(column).append("=").append(values[i]);
633 if (i > 0) {
634 selectWhere.append(" OR ");
635 }
636 }
637 return selectWhere.toString();
638 }
639
640 static class SqlArguments {
641 public final String table;
642 public final String where;
643 public final String[] args;
644
645 SqlArguments(Uri url, String where, String[] args) {
646 if (url.getPathSegments().size() == 1) {
647 this.table = url.getPathSegments().get(0);
648 this.where = where;
649 this.args = args;
650 } else if (url.getPathSegments().size() != 2) {
651 throw new IllegalArgumentException("Invalid URI: " + url);
652 } else if (!TextUtils.isEmpty(where)) {
653 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
654 } else {
655 this.table = url.getPathSegments().get(0);
656 this.where = "_id=" + ContentUris.parseId(url);
657 this.args = null;
658 }
659 }
660
661 SqlArguments(Uri url) {
662 if (url.getPathSegments().size() == 1) {
663 table = url.getPathSegments().get(0);
664 where = null;
665 args = null;
666 } else {
667 throw new IllegalArgumentException("Invalid URI: " + url);
668 }
669 }
670 }
671}