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