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 17 package com.android.launcher; 18 19 import android.appwidget.AppWidgetHost; 20 import android.content.ContentProvider; 21 import android.content.Context; 22 import android.content.ContentValues; 23 import android.content.Intent; 24 import android.content.ComponentName; 25 import android.content.ContentUris; 26 import android.content.ContentResolver; 27 import android.content.res.XmlResourceParser; 28 import android.content.res.TypedArray; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ActivityInfo; 31 import android.database.sqlite.SQLiteOpenHelper; 32 import android.database.sqlite.SQLiteDatabase; 33 import android.database.sqlite.SQLiteQueryBuilder; 34 import android.database.Cursor; 35 import android.database.SQLException; 36 import android.util.Log; 37 import android.util.Xml; 38 import android.util.AttributeSet; 39 import android.net.Uri; 40 import android.text.TextUtils; 41 import android.os.*; 42 import android.provider.Settings; 43 44 import java.io.IOException; 45 import java.util.ArrayList; 46 47 import org.xmlpull.v1.XmlPullParserException; 48 import org.xmlpull.v1.XmlPullParser; 49 import com.android.internal.util.XmlUtils; 50 import com.android.launcher.LauncherSettings.Favorites; 51 52 public 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 58 private static final int DATABASE_VERSION = 4; 59 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"; 66 static final String TABLE_GESTURES = "gestures"; 67 static final String PARAMETER_NOTIFY = "notify"; 68 69 /** 70 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when 71 * {@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 77 private SQLiteOpenHelper mOpenHelper; 78 79 @Override onCreate()80 public boolean onCreate() { 81 mOpenHelper = new DatabaseHelper(getContext()); 82 return true; 83 } 84 85 @Override getType(Uri uri)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 query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)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 103 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 104 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 insert(Uri uri, ContentValues initialValues)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 bulkInsert(Uri uri, ContentValues[] values)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 delete(Uri uri, String selection, String[] selectionArgs)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 update(Uri uri, ContentValues values, String selection, String[] selectionArgs)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 sendNotify(Uri uri)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 { 174 private static final String TAG_FAVORITES = "favorites"; 175 private static final String TAG_FAVORITE = "favorite"; 176 private static final String TAG_CLOCK = "clock"; 177 private static final String TAG_SEARCH = "search"; 178 179 private final Context mContext; 180 private final AppWidgetHost mAppWidgetHost; 181 DatabaseHelper(Context context)182 DatabaseHelper(Context context) { 183 super(context, DATABASE_NAME, null, DATABASE_VERSION); 184 mContext = context; 185 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID); 186 } 187 188 /** 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 */ sendAppWidgetResetNotify()194 private void sendAppWidgetResetNotify() { 195 final ContentResolver resolver = mContext.getContentResolver(); 196 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null); 197 } 198 199 @Override onCreate(SQLiteDatabase db)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," + 214 "appWidgetId INTEGER NOT NULL DEFAULT -1," + 215 "isShortcut INTEGER," + 216 "iconType INTEGER," + 217 "iconPackage TEXT," + 218 "iconResource TEXT," + 219 "icon BLOB," + 220 "uri TEXT," + 221 "displayMode INTEGER" + 222 ");"); 223 224 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 235 // Database was just created, so wipe any previous widgets 236 if (mAppWidgetHost != null) { 237 mAppWidgetHost.deleteHost(); 238 sendAppWidgetResetNotify(); 239 } 240 241 if (!convertDatabase(db)) { 242 // Populate favorites table with initial favorites 243 loadFavorites(db); 244 } 245 } 246 convertDatabase(SQLiteDatabase db)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) { 276 // Convert widgets from this import into widgets 277 if (LOGD) Log.d(LOG_TAG, "converted and now triggering widget upgrade"); 278 convertWidgets(db); 279 } 280 281 return converted; 282 } 283 copyFromCursor(SQLiteDatabase db, Cursor c)284 private int copyFromCursor(SQLiteDatabase db, Cursor c) { 285 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID); 286 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()); 304 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex)); 305 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)); 313 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1); 314 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 onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)342 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 343 if (LOGD) Log.d(LOG_TAG, "onUpgrade triggered"); 344 345 int version = oldVersion; 346 if (version < 3) { 347 // upgrade 1,2 -> 3 added appWidgetId column 348 db.beginTransaction(); 349 try { 350 // Insert new column for holding appWidgetIds 351 db.execSQL("ALTER TABLE favorites " + 352 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;"); 353 db.setTransactionSuccessful(); 354 version = 3; 355 } 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 363 if (version == 3) { 364 convertWidgets(db); 365 } 366 } 367 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 } 390 391 if (version != DATABASE_VERSION) { 392 Log.w(LOG_TAG, "Destroying all old data."); 393 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); 394 db.execSQL("DROP TABLE IF EXISTS " + TABLE_GESTURES); 395 onCreate(db); 396 } 397 } 398 399 /** 400 * 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. 403 */ convertWidgets(SQLiteDatabase db)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", 412 "com.android.alarmclock.AnalogAppWidgetProvider")); 413 bindTargets.add(new ComponentName("com.android.camera", 414 "com.android.camera.PhotoAppWidgetProvider")); 415 416 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources); 417 418 Cursor c = null; 419 boolean allocatedAppWidgets = false; 420 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 433 // Allocate and update database with new appWidgetId 434 try { 435 int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 436 437 if (LOGD) Log.d(LOG_TAG, "allocated appWidgetId="+appWidgetId+" for favoriteId="+favoriteId); 438 439 values.clear(); 440 values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId); 441 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 449 allocatedAppWidgets = true; 450 } catch (RuntimeException ex) { 451 Log.e(LOG_TAG, "Problem allocating appWidgetId", ex); 452 } 453 } 454 455 db.setTransactionSuccessful(); 456 } catch (SQLException ex) { 457 Log.w(LOG_TAG, "Problem while allocating appWidgetIds for existing widgets", ex); 458 } finally { 459 db.endTransaction(); 460 if (c != null) { 461 c.close(); 462 } 463 } 464 465 // If any appWidgetIds allocated, then launch over to binder 466 if (allocatedAppWidgets) { 467 launchAppWidgetBinder(bindSources, bindTargets); 468 } 469 } 470 471 /** 472 * Launch the widget binder that walks through the Launcher database, 473 * binding any matching widgets to the corresponding targets. We can't 474 * bind ourselves because our parent process can't obtain the 475 * BIND_APPWIDGET permission. 476 */ launchAppWidgetBinder(int[] bindSources, ArrayList<ComponentName> bindTargets)477 private void launchAppWidgetBinder(int[] bindSources, ArrayList<ComponentName> bindTargets) { 478 final Intent intent = new Intent(); 479 intent.setComponent(new ComponentName("com.android.settings", 480 "com.android.settings.LauncherAppWidgetBinder")); 481 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 495 */ loadFavorites(SQLiteDatabase db)496 private int loadFavorites(SQLiteDatabase db) { 497 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(); 502 int i = 0; 503 try { 504 XmlResourceParser parser = mContext.getResources().getXml(R.xml.default_workspace); 505 AttributeSet attrs = Xml.asAttributeSet(parser); 506 XmlUtils.beginDocument(parser, TAG_FAVORITES); 507 508 final int depth = parser.getDepth(); 509 510 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; 516 } 517 518 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); 539 } 540 541 if (added) i++; 542 543 a.recycle(); 544 } 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 } 550 551 return i; 552 } 553 addShortcut(SQLiteDatabase db, ContentValues values, TypedArray a, PackageManager packageManager, Intent intent)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); 564 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 565 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 566 values.put(Favorites.INTENT, intent.toUri(0)); 567 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 addSearchWidget(SQLiteDatabase db, ContentValues values)580 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) { 581 // Add a search box 582 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_WIDGET_SEARCH); 583 values.put(Favorites.SPANX, 4); 584 values.put(Favorites.SPANY, 1); 585 db.insert(TABLE_FAVORITES, null, values); 586 587 return true; 588 } 589 addClockWidget(SQLiteDatabase db, ContentValues values)590 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) { 591 final int[] bindSources = new int[] { 592 Favorites.ITEM_TYPE_WIDGET_CLOCK, 593 }; 594 595 final ArrayList<ComponentName> bindTargets = new ArrayList<ComponentName>(); 596 bindTargets.add(new ComponentName("com.android.alarmclock", 597 "com.android.alarmclock.AnalogAppWidgetProvider")); 598 599 boolean allocatedAppWidgets = false; 600 601 // Try binding to an analog clock widget 602 try { 603 int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 604 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); 609 db.insert(TABLE_FAVORITES, null, values); 610 611 allocatedAppWidgets = true; 612 } catch (RuntimeException ex) { 613 Log.e(LOG_TAG, "Problem allocating appWidgetId", ex); 614 } 615 616 // If any appWidgetIds allocated, then launch over to binder 617 if (allocatedAppWidgets) { 618 launchAppWidgetBinder(bindSources, bindTargets); 619 } 620 621 return allocatedAppWidgets; 622 } 623 } 624 625 /** 626 * Build a query string that will match any row where the column matches 627 * anything in the values list. 628 */ buildOrWhereString(String column, int[] values)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 SqlArguments(Uri url, String where, String[] args)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 SqlArguments(Uri url)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 } 672