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