LauncherProvider.java revision 346e129456951b5db36a663d5601421e8019632e
1/* 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.launcher2; 18 19import android.app.SearchManager; 20import android.appwidget.AppWidgetHost; 21import android.appwidget.AppWidgetManager; 22import android.appwidget.AppWidgetProviderInfo; 23import 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; 30import android.content.res.Resources; 31import android.content.res.XmlResourceParser; 32import android.content.res.TypedArray; 33import android.content.pm.PackageManager; 34import android.content.pm.ActivityInfo; 35import android.database.sqlite.SQLiteOpenHelper; 36import android.database.sqlite.SQLiteDatabase; 37import android.database.sqlite.SQLiteStatement; 38import android.database.sqlite.SQLiteQueryBuilder; 39import android.database.Cursor; 40import android.database.SQLException; 41import android.graphics.Bitmap; 42import android.graphics.BitmapFactory; 43import android.util.Log; 44import android.util.Xml; 45import android.util.AttributeSet; 46import android.net.Uri; 47import android.text.TextUtils; 48import android.os.*; 49import android.provider.Settings; 50 51import java.io.IOException; 52import java.net.URISyntaxException; 53import java.util.ArrayList; 54import java.util.List; 55 56import org.xmlpull.v1.XmlPullParserException; 57import org.xmlpull.v1.XmlPullParser; 58import com.android.common.XmlUtils; 59import com.android.launcher2.LauncherSettings.Favorites; 60 61public class LauncherProvider extends ContentProvider { 62 private static final String TAG = "Launcher.LauncherProvider"; 63 private static final boolean LOGD = false; 64 65 private static final String DATABASE_NAME = "launcher.db"; 66 67 private static final int DATABASE_VERSION = 8; 68 69 static final String AUTHORITY = "com.android.launcher2.settings"; 70 71 static final String EXTRA_BIND_SOURCES = "com.android.launcher2.settings.bindsources"; 72 static final String EXTRA_BIND_TARGETS = "com.android.launcher2.settings.bindtargets"; 73 74 static final String TABLE_FAVORITES = "favorites"; 75 static final String PARAMETER_NOTIFY = "notify"; 76 77 /** 78 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when 79 * {@link AppWidgetHost#deleteHost()} is called during database creation. 80 * Use this to recall {@link AppWidgetHost#startListening()} if needed. 81 */ 82 static final Uri CONTENT_APPWIDGET_RESET_URI = 83 Uri.parse("content://" + AUTHORITY + "/appWidgetReset"); 84 85 private SQLiteOpenHelper mOpenHelper; 86 87 @Override 88 public boolean onCreate() { 89 mOpenHelper = new DatabaseHelper(getContext()); 90 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 111 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 112 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 118 @Override 119 public Uri insert(Uri uri, ContentValues initialValues) { 120 SqlArguments args = new SqlArguments(uri); 121 122 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 123 final long rowId = db.insert(args.table, null, initialValues); 124 if (rowId <= 0) return null; 125 126 uri = ContentUris.withAppendedId(uri, rowId); 127 sendNotify(uri); 128 129 return uri; 130 } 131 132 @Override 133 public int bulkInsert(Uri uri, ContentValues[] values) { 134 SqlArguments args = new SqlArguments(uri); 135 136 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 137 db.beginTransaction(); 138 try { 139 int numValues = values.length; 140 for (int i = 0; i < numValues; i++) { 141 if (db.insert(args.table, null, values[i]) < 0) return 0; 142 } 143 db.setTransactionSuccessful(); 144 } finally { 145 db.endTransaction(); 146 } 147 148 sendNotify(uri); 149 return values.length; 150 } 151 152 @Override 153 public int delete(Uri uri, String selection, String[] selectionArgs) { 154 SqlArguments args = new SqlArguments(uri, selection, selectionArgs); 155 156 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 157 int count = db.delete(args.table, args.where, args.args); 158 if (count > 0) sendNotify(uri); 159 160 return count; 161 } 162 163 @Override 164 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 165 SqlArguments args = new SqlArguments(uri, selection, selectionArgs); 166 167 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 168 int count = db.update(args.table, values, args.where, args.args); 169 if (count > 0) sendNotify(uri); 170 171 return count; 172 } 173 174 private void sendNotify(Uri uri) { 175 String notify = uri.getQueryParameter(PARAMETER_NOTIFY); 176 if (notify == null || "true".equals(notify)) { 177 getContext().getContentResolver().notifyChange(uri, null); 178 } 179 } 180 181 private static class DatabaseHelper extends SQLiteOpenHelper { 182 private static final String TAG_FAVORITES = "favorites"; 183 private static final String TAG_FAVORITE = "favorite"; 184 private static final String TAG_CLOCK = "clock"; 185 private static final String TAG_SEARCH = "search"; 186 private static final String TAG_APPWIDGET = "appwidget"; 187 private static final String TAG_SHORTCUT = "shortcut"; 188 189 private final Context mContext; 190 private final AppWidgetHost mAppWidgetHost; 191 192 DatabaseHelper(Context context) { 193 super(context, DATABASE_NAME, null, DATABASE_VERSION); 194 mContext = context; 195 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID); 196 } 197 198 /** 199 * Send notification that we've deleted the {@link AppWidgetHost}, 200 * probably as part of the initial database creation. The receiver may 201 * want to re-call {@link AppWidgetHost#startListening()} to ensure 202 * callbacks are correctly set. 203 */ 204 private void sendAppWidgetResetNotify() { 205 final ContentResolver resolver = mContext.getContentResolver(); 206 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null); 207 } 208 209 @Override 210 public void onCreate(SQLiteDatabase db) { 211 if (LOGD) Log.d(TAG, "creating new launcher database"); 212 213 db.execSQL("CREATE TABLE favorites (" + 214 "_id INTEGER PRIMARY KEY," + 215 "title TEXT," + 216 "intent TEXT," + 217 "container INTEGER," + 218 "screen INTEGER," + 219 "cellX INTEGER," + 220 "cellY INTEGER," + 221 "spanX INTEGER," + 222 "spanY INTEGER," + 223 "itemType INTEGER," + 224 "appWidgetId INTEGER NOT NULL DEFAULT -1," + 225 "isShortcut INTEGER," + 226 "iconType INTEGER," + 227 "iconPackage TEXT," + 228 "iconResource TEXT," + 229 "icon BLOB," + 230 "uri TEXT," + 231 "displayMode INTEGER" + 232 ");"); 233 234 // Database was just created, so wipe any previous widgets 235 if (mAppWidgetHost != null) { 236 mAppWidgetHost.deleteHost(); 237 sendAppWidgetResetNotify(); 238 } 239 240 if (!convertDatabase(db)) { 241 // Populate favorites table with initial favorites 242 loadFavorites(db); 243 } 244 } 245 246 private boolean convertDatabase(SQLiteDatabase db) { 247 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade"); 248 boolean converted = false; 249 250 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY + 251 "/old_favorites?notify=true"); 252 final ContentResolver resolver = mContext.getContentResolver(); 253 Cursor cursor = null; 254 255 try { 256 cursor = resolver.query(uri, null, null, null, null); 257 } catch (Exception e) { 258 // Ignore 259 } 260 261 // We already have a favorites database in the old provider 262 if (cursor != null && cursor.getCount() > 0) { 263 try { 264 converted = copyFromCursor(db, cursor) > 0; 265 } finally { 266 cursor.close(); 267 } 268 269 if (converted) { 270 resolver.delete(uri, null, null); 271 } 272 } 273 274 if (converted) { 275 // Convert widgets from this import into widgets 276 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade"); 277 convertWidgets(db); 278 } 279 280 return converted; 281 } 282 283 private int copyFromCursor(SQLiteDatabase db, Cursor c) { 284 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); 285 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT); 286 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE); 287 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE); 288 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON); 289 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE); 290 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE); 291 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER); 292 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE); 293 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN); 294 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); 295 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); 296 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); 297 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE); 298 299 ContentValues[] rows = new ContentValues[c.getCount()]; 300 int i = 0; 301 while (c.moveToNext()) { 302 ContentValues values = new ContentValues(c.getColumnCount()); 303 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex)); 304 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex)); 305 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex)); 306 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex)); 307 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex)); 308 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex)); 309 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex)); 310 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex)); 311 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex)); 312 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1); 313 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex)); 314 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex)); 315 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex)); 316 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex)); 317 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex)); 318 rows[i++] = values; 319 } 320 321 db.beginTransaction(); 322 int total = 0; 323 try { 324 int numValues = rows.length; 325 for (i = 0; i < numValues; i++) { 326 if (db.insert(TABLE_FAVORITES, null, rows[i]) < 0) { 327 return 0; 328 } else { 329 total++; 330 } 331 } 332 db.setTransactionSuccessful(); 333 } finally { 334 db.endTransaction(); 335 } 336 337 return total; 338 } 339 340 @Override 341 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 342 if (LOGD) Log.d(TAG, "onUpgrade triggered"); 343 344 int version = oldVersion; 345 if (version < 3) { 346 // upgrade 1,2 -> 3 added appWidgetId column 347 db.beginTransaction(); 348 try { 349 // Insert new column for holding appWidgetIds 350 db.execSQL("ALTER TABLE favorites " + 351 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;"); 352 db.setTransactionSuccessful(); 353 version = 3; 354 } catch (SQLException ex) { 355 // Old version remains, which means we wipe old data 356 Log.e(TAG, ex.getMessage(), ex); 357 } finally { 358 db.endTransaction(); 359 } 360 361 // Convert existing widgets only if table upgrade was successful 362 if (version == 3) { 363 convertWidgets(db); 364 } 365 } 366 367 if (version < 4) { 368 version = 4; 369 } 370 371 if (version < 5) { 372 // We went from 3 to 5 screens. Move everything 1 to the right 373 db.beginTransaction(); 374 try { 375 db.execSQL("UPDATE favorites SET screen=(screen + 1);"); 376 db.setTransactionSuccessful(); 377 version = 5; 378 } catch (SQLException ex) { 379 // Old version remains, which means we wipe old data 380 Log.e(TAG, ex.getMessage(), ex); 381 } finally { 382 db.endTransaction(); 383 } 384 } 385 386 if (version < 6) { 387 if (updateContactsShortcuts(db)) { 388 version = 6; 389 } 390 } 391 392 if (version < 7) { 393 // Version 7 gets rid of the special search widget. 394 convertWidgets(db); 395 version = 7; 396 } 397 398 if (version < 8) { 399 // Version 8 (froyo) has the icons all normalized. This should 400 // already be the case in practice, but we now rely on it and don't 401 // resample the images each time. 402 normalizeIcons(db); 403 version = 8; 404 } 405 406 if (version != DATABASE_VERSION) { 407 Log.w(TAG, "Destroying all old data."); 408 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); 409 onCreate(db); 410 } 411 } 412 413 private boolean updateContactsShortcuts(SQLiteDatabase db) { 414 Cursor c = null; 415 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, 416 new int[] { Favorites.ITEM_TYPE_SHORTCUT }); 417 418 db.beginTransaction(); 419 try { 420 // Select and iterate through each matching widget 421 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.INTENT }, 422 selectWhere, null, null, null, null); 423 424 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount()); 425 426 final ContentValues values = new ContentValues(); 427 final int idIndex = c.getColumnIndex(Favorites._ID); 428 final int intentIndex = c.getColumnIndex(Favorites.INTENT); 429 430 while (c != null && c.moveToNext()) { 431 long favoriteId = c.getLong(idIndex); 432 final String intentUri = c.getString(intentIndex); 433 if (intentUri != null) { 434 try { 435 Intent intent = Intent.parseUri(intentUri, 0); 436 android.util.Log.d("Home", intent.toString()); 437 final Uri uri = intent.getData(); 438 final String data = uri.toString(); 439 if (Intent.ACTION_VIEW.equals(intent.getAction()) && 440 (data.startsWith("content://contacts/people/") || 441 data.startsWith("content://com.android.contacts/contacts/lookup/"))) { 442 443 intent = new Intent("com.android.contacts.action.QUICK_CONTACT"); 444 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 445 Intent.FLAG_ACTIVITY_CLEAR_TOP | 446 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 447 448 intent.setData(uri); 449 intent.putExtra("mode", 3); 450 intent.putExtra("exclude_mimes", (String[]) null); 451 452 values.clear(); 453 values.put(LauncherSettings.Favorites.INTENT, intent.toUri(0)); 454 455 String updateWhere = Favorites._ID + "=" + favoriteId; 456 db.update(TABLE_FAVORITES, values, updateWhere, null); 457 } 458 } catch (RuntimeException ex) { 459 Log.e(TAG, "Problem upgrading shortcut", ex); 460 } catch (URISyntaxException e) { 461 Log.e(TAG, "Problem upgrading shortcut", e); 462 } 463 } 464 } 465 466 db.setTransactionSuccessful(); 467 } catch (SQLException ex) { 468 Log.w(TAG, "Problem while upgrading contacts", ex); 469 return false; 470 } finally { 471 db.endTransaction(); 472 if (c != null) { 473 c.close(); 474 } 475 } 476 477 return true; 478 } 479 480 private void normalizeIcons(SQLiteDatabase db) { 481 Log.d(TAG, "normalizing icons"); 482 483 db.beginTransaction(); 484 Cursor c = null; 485 try { 486 boolean logged = false; 487 final ContentValues values = new ContentValues(); 488 final ContentResolver cr = mContext.getContentResolver(); 489 final SQLiteStatement update = db.compileStatement("UPDATE favorites " 490 + "SET icon=? WHERE _id=?"); 491 492 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" + 493 Favorites.ICON_TYPE_BITMAP, null); 494 495 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID); 496 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON); 497 498 while (c.moveToNext()) { 499 long id = c.getLong(idIndex); 500 byte[] data = c.getBlob(iconIndex); 501 try { 502 Bitmap bitmap = Utilities.resampleIconBitmap( 503 BitmapFactory.decodeByteArray(data, 0, data.length), 504 mContext); 505 if (bitmap != null) { 506 update.bindLong(1, id); 507 data = ItemInfo.flattenBitmap(bitmap); 508 if (data != null) { 509 update.bindBlob(2, data); 510 update.execute(); 511 } 512 bitmap.recycle(); 513 bitmap = null; 514 } 515 } catch (Exception e) { 516 if (!logged) { 517 Log.e(TAG, "Failed normalizing icon " + id, e); 518 } else { 519 Log.e(TAG, "Also failed normalizing icon " + id); 520 } 521 logged = true; 522 } 523 } 524 } catch (SQLException ex) { 525 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); 526 } finally { 527 db.endTransaction(); 528 if (c != null) { 529 c.close(); 530 } 531 } 532 533 } 534 535 /** 536 * Upgrade existing clock and photo frame widgets into their new widget 537 * equivalents. 538 */ 539 private void convertWidgets(SQLiteDatabase db) { 540 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 541 final int[] bindSources = new int[] { 542 Favorites.ITEM_TYPE_WIDGET_CLOCK, 543 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME, 544 Favorites.ITEM_TYPE_WIDGET_SEARCH, 545 }; 546 547 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources); 548 549 Cursor c = null; 550 551 db.beginTransaction(); 552 try { 553 // Select and iterate through each matching widget 554 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE }, 555 selectWhere, null, null, null, null); 556 557 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount()); 558 559 final ContentValues values = new ContentValues(); 560 while (c != null && c.moveToNext()) { 561 long favoriteId = c.getLong(0); 562 int favoriteType = c.getInt(1); 563 564 // Allocate and update database with new appWidgetId 565 try { 566 int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 567 568 if (LOGD) { 569 Log.d(TAG, "allocated appWidgetId=" + appWidgetId 570 + " for favoriteId=" + favoriteId); 571 } 572 values.clear(); 573 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET); 574 values.put(Favorites.APPWIDGET_ID, appWidgetId); 575 576 // Original widgets might not have valid spans when upgrading 577 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) { 578 values.put(LauncherSettings.Favorites.SPANX, 4); 579 values.put(LauncherSettings.Favorites.SPANY, 1); 580 } else { 581 values.put(LauncherSettings.Favorites.SPANX, 2); 582 values.put(LauncherSettings.Favorites.SPANY, 2); 583 } 584 585 String updateWhere = Favorites._ID + "=" + favoriteId; 586 db.update(TABLE_FAVORITES, values, updateWhere, null); 587 588 ComponentName cn = null; 589 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) { 590 appWidgetManager.bindAppWidgetId(appWidgetId, 591 new ComponentName("com.android.alarmclock", 592 "com.android.alarmclock.AnalogAppWidgetProvider")); 593 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) { 594 appWidgetManager.bindAppWidgetId(appWidgetId, 595 new ComponentName("com.android.camera", 596 "com.android.camera.PhotoAppWidgetProvider")); 597 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) { 598 appWidgetManager.bindAppWidgetId(appWidgetId, 599 getSearchWidgetProvider()); 600 } 601 } catch (RuntimeException ex) { 602 Log.e(TAG, "Problem allocating appWidgetId", ex); 603 } 604 } 605 606 db.setTransactionSuccessful(); 607 } catch (SQLException ex) { 608 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex); 609 } finally { 610 db.endTransaction(); 611 if (c != null) { 612 c.close(); 613 } 614 } 615 } 616 617 /** 618 * Loads the default set of favorite packages from an xml file. 619 * 620 * @param db The database to write the values into 621 */ 622 private int loadFavorites(SQLiteDatabase db) { 623 Intent intent = new Intent(Intent.ACTION_MAIN, null); 624 intent.addCategory(Intent.CATEGORY_LAUNCHER); 625 ContentValues values = new ContentValues(); 626 627 PackageManager packageManager = mContext.getPackageManager(); 628 int i = 0; 629 try { 630 XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace); 631 AttributeSet attrs = Xml.asAttributeSet(parser); 632 XmlUtils.beginDocument(parser, TAG_FAVORITES); 633 634 final int depth = parser.getDepth(); 635 636 int type; 637 while (((type = parser.next()) != XmlPullParser.END_TAG || 638 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 639 640 if (type != XmlPullParser.START_TAG) { 641 continue; 642 } 643 644 boolean added = false; 645 final String name = parser.getName(); 646 647 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite); 648 649 values.clear(); 650 values.put(LauncherSettings.Favorites.CONTAINER, 651 LauncherSettings.Favorites.CONTAINER_DESKTOP); 652 values.put(LauncherSettings.Favorites.SCREEN, 653 a.getString(R.styleable.Favorite_screen)); 654 values.put(LauncherSettings.Favorites.CELLX, 655 a.getString(R.styleable.Favorite_x)); 656 values.put(LauncherSettings.Favorites.CELLY, 657 a.getString(R.styleable.Favorite_y)); 658 659 if (TAG_FAVORITE.equals(name)) { 660 added = addAppShortcut(db, values, a, packageManager, intent); 661 } else if (TAG_SEARCH.equals(name)) { 662 added = addSearchWidget(db, values); 663 } else if (TAG_CLOCK.equals(name)) { 664 added = addClockWidget(db, values); 665 } else if (TAG_APPWIDGET.equals(name)) { 666 added = addAppWidget(db, values, a); 667 } else if (TAG_SHORTCUT.equals(name)) { 668 added = addUriShortcut(db, values, a); 669 } 670 671 if (added) i++; 672 673 a.recycle(); 674 } 675 } catch (XmlPullParserException e) { 676 Log.w(TAG, "Got exception parsing favorites.", e); 677 } catch (IOException e) { 678 Log.w(TAG, "Got exception parsing favorites.", e); 679 } 680 681 return i; 682 } 683 684 private boolean addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a, 685 PackageManager packageManager, Intent intent) { 686 687 ActivityInfo info; 688 String packageName = a.getString(R.styleable.Favorite_packageName); 689 String className = a.getString(R.styleable.Favorite_className); 690 try { 691 ComponentName cn = new ComponentName(packageName, className); 692 info = packageManager.getActivityInfo(cn, 0); 693 intent.setComponent(cn); 694 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 695 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 696 values.put(Favorites.INTENT, intent.toUri(0)); 697 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString()); 698 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION); 699 values.put(Favorites.SPANX, 1); 700 values.put(Favorites.SPANY, 1); 701 db.insert(TABLE_FAVORITES, null, values); 702 } catch (PackageManager.NameNotFoundException e) { 703 Log.w(TAG, "Unable to add favorite: " + packageName + 704 "/" + className, e); 705 return false; 706 } 707 return true; 708 } 709 710 private ComponentName getSearchWidgetProvider() { 711 SearchManager searchManager = 712 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); 713 ComponentName searchComponent = searchManager.getGlobalSearchActivity(); 714 if (searchComponent == null) return null; 715 return getProviderInPackage(searchComponent.getPackageName()); 716 } 717 718 /** 719 * Gets an appwidget provider from the given package. If the package contains more than 720 * one appwidget provider, an arbitrary one is returned. 721 */ 722 private ComponentName getProviderInPackage(String packageName) { 723 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 724 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders(); 725 if (providers == null) return null; 726 final int providerCount = providers.size(); 727 for (int i = 0; i < providerCount; i++) { 728 ComponentName provider = providers.get(i).provider; 729 if (provider != null && provider.getPackageName().equals(packageName)) { 730 return provider; 731 } 732 } 733 return null; 734 } 735 736 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) { 737 ComponentName cn = getSearchWidgetProvider(); 738 return addAppWidget(db, values, cn, 4, 1); 739 } 740 741 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) { 742 ComponentName cn = new ComponentName("com.android.alarmclock", 743 "com.android.alarmclock.AnalogAppWidgetProvider"); 744 return addAppWidget(db, values, cn, 2, 2); 745 } 746 747 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, TypedArray a) { 748 String packageName = a.getString(R.styleable.Favorite_packageName); 749 String className = a.getString(R.styleable.Favorite_className); 750 751 if (packageName == null || className == null) { 752 return false; 753 } 754 755 ComponentName cn = new ComponentName(packageName, className); 756 int spanX = a.getInt(R.styleable.Favorite_spanX, 0); 757 int spanY = a.getInt(R.styleable.Favorite_spanY, 0); 758 return addAppWidget(db, values, cn, spanX, spanY); 759 } 760 761 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn, 762 int spanX, int spanY) { 763 boolean allocatedAppWidgets = false; 764 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 765 766 try { 767 int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 768 769 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET); 770 values.put(Favorites.SPANX, spanX); 771 values.put(Favorites.SPANY, spanY); 772 values.put(Favorites.APPWIDGET_ID, appWidgetId); 773 db.insert(TABLE_FAVORITES, null, values); 774 775 allocatedAppWidgets = true; 776 777 appWidgetManager.bindAppWidgetId(appWidgetId, cn); 778 } catch (RuntimeException ex) { 779 Log.e(TAG, "Problem allocating appWidgetId", ex); 780 } 781 782 return allocatedAppWidgets; 783 } 784 785 private boolean addUriShortcut(SQLiteDatabase db, ContentValues values, 786 TypedArray a) { 787 Resources r = mContext.getResources(); 788 789 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0); 790 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0); 791 792 Intent intent; 793 String uri = null; 794 try { 795 uri = a.getString(R.styleable.Favorite_uri); 796 intent = Intent.parseUri(uri, 0); 797 } catch (URISyntaxException e) { 798 Log.w(TAG, "Shortcut has malformed uri: " + uri); 799 return false; // Oh well 800 } 801 802 if (iconResId == 0 || titleResId == 0) { 803 Log.w(TAG, "Shortcut is missing title or icon resource ID"); 804 return false; 805 } 806 807 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 808 values.put(Favorites.INTENT, intent.toUri(0)); 809 values.put(Favorites.TITLE, r.getString(titleResId)); 810 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT); 811 values.put(Favorites.SPANX, 1); 812 values.put(Favorites.SPANY, 1); 813 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE); 814 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName()); 815 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId)); 816 817 db.insert(TABLE_FAVORITES, null, values); 818 819 return true; 820 } 821 } 822 823 /** 824 * Build a query string that will match any row where the column matches 825 * anything in the values list. 826 */ 827 static String buildOrWhereString(String column, int[] values) { 828 StringBuilder selectWhere = new StringBuilder(); 829 for (int i = values.length - 1; i >= 0; i--) { 830 selectWhere.append(column).append("=").append(values[i]); 831 if (i > 0) { 832 selectWhere.append(" OR "); 833 } 834 } 835 return selectWhere.toString(); 836 } 837 838 static class SqlArguments { 839 public final String table; 840 public final String where; 841 public final String[] args; 842 843 SqlArguments(Uri url, String where, String[] args) { 844 if (url.getPathSegments().size() == 1) { 845 this.table = url.getPathSegments().get(0); 846 this.where = where; 847 this.args = args; 848 } else if (url.getPathSegments().size() != 2) { 849 throw new IllegalArgumentException("Invalid URI: " + url); 850 } else if (!TextUtils.isEmpty(where)) { 851 throw new UnsupportedOperationException("WHERE clause not supported: " + url); 852 } else { 853 this.table = url.getPathSegments().get(0); 854 this.where = "_id=" + ContentUris.parseId(url); 855 this.args = null; 856 } 857 } 858 859 SqlArguments(Uri url) { 860 if (url.getPathSegments().size() == 1) { 861 table = url.getPathSegments().get(0); 862 where = null; 863 args = null; 864 } else { 865 throw new IllegalArgumentException("Invalid URI: " + url); 866 } 867 } 868 } 869} 870