blob: ae227a998fb9e1c853674f6a4088b1c41f3476c3 [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000019import android.app.SearchManager;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070020import android.appwidget.AppWidgetHost;
Mike Cleronb87bd162009-10-30 16:36:56 -070021import android.appwidget.AppWidgetManager;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000022import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080023import android.content.ComponentName;
Adam Cohen228da5a2011-07-27 22:23:47 -070024import android.content.ContentProvider;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.ContentResolver;
Adam Cohen228da5a2011-07-27 22:23:47 -070026import android.content.ContentUris;
27import android.content.ContentValues;
28import android.content.Context;
29import android.content.Intent;
Michael Jurkab85f8a42012-04-25 15:48:32 -070030import android.content.SharedPreferences;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080031import android.content.pm.ActivityInfo;
Adam Cohen228da5a2011-07-27 22:23:47 -070032import android.content.pm.PackageManager;
33import android.content.res.Resources;
34import android.content.res.TypedArray;
35import android.content.res.XmlResourceParser;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.database.Cursor;
37import android.database.SQLException;
Adam Cohen228da5a2011-07-27 22:23:47 -070038import android.database.sqlite.SQLiteDatabase;
39import android.database.sqlite.SQLiteOpenHelper;
40import android.database.sqlite.SQLiteQueryBuilder;
41import android.database.sqlite.SQLiteStatement;
Joe Onorato0589f0f2010-02-08 13:44:00 -080042import android.graphics.Bitmap;
43import android.graphics.BitmapFactory;
Adam Cohen228da5a2011-07-27 22:23:47 -070044import android.net.Uri;
Winson Chungb3302ae2012-05-01 10:19:14 -070045import android.os.Bundle;
Adam Cohen228da5a2011-07-27 22:23:47 -070046import android.provider.Settings;
47import android.text.TextUtils;
48import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049import android.util.Log;
50import android.util.Xml;
Adam Cohen228da5a2011-07-27 22:23:47 -070051
Daniel Sandler325dc232013-06-05 22:57:57 -040052import com.android.launcher3.LauncherSettings.Favorites;
Michael Jurka8b805b12012-04-18 14:23:14 -070053
54import org.xmlpull.v1.XmlPullParser;
55import org.xmlpull.v1.XmlPullParserException;
56
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070058import java.net.URISyntaxException;
Adam Cohen228da5a2011-07-27 22:23:47 -070059import java.util.ArrayList;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000060import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080061
The Android Open Source Project31dd5032009-03-03 19:32:27 -080062public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080063 private static final String TAG = "Launcher.LauncherProvider";
64 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065
66 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070067
Adam Cohendcd297f2013-06-18 13:13:40 -070068 private static final int DATABASE_VERSION = 13;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080069
Adam Cohene25af792013-06-06 23:08:25 -070070 static final String OLD_AUTHORITY = "com.android.launcher2.settings";
Daniel Sandler325dc232013-06-05 22:57:57 -040071 static final String AUTHORITY = "com.android.launcher3.settings";
Winson Chung3d503fb2011-07-13 17:25:49 -070072
The Android Open Source Project31dd5032009-03-03 19:32:27 -080073 static final String TABLE_FAVORITES = "favorites";
Adam Cohendcd297f2013-06-18 13:13:40 -070074 static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075 static final String PARAMETER_NOTIFY = "notify";
Michael Jurkab85f8a42012-04-25 15:48:32 -070076 static final String DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED =
77 "DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED";
Michael Jurka45355c42012-10-08 13:21:35 +020078 static final String DEFAULT_WORKSPACE_RESOURCE_ID =
79 "DEFAULT_WORKSPACE_RESOURCE_ID";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080080
Winson Chungb3302ae2012-05-01 10:19:14 -070081 private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
Adam Cohene25af792013-06-06 23:08:25 -070082 "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
Winson Chungb3302ae2012-05-01 10:19:14 -070083
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070084 /**
Romain Guy73b979d2009-06-09 12:57:21 -070085 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070086 * {@link AppWidgetHost#deleteHost()} is called during database creation.
87 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
88 */
89 static final Uri CONTENT_APPWIDGET_RESET_URI =
90 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
Daniel Lehmannc3a80402012-04-23 21:35:11 -070091
Michael Jurkaa8c760d2011-04-28 14:59:33 -070092 private DatabaseHelper mOpenHelper;
Adam Cohendcd297f2013-06-18 13:13:40 -070093 private static boolean sLoadOldDb;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080094
95 @Override
96 public boolean onCreate() {
Daniel Sandlere4f98912013-06-25 15:13:26 -040097 final Context context = getContext();
98 mOpenHelper = new DatabaseHelper(context);
99 LauncherAppState.setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800100 return true;
101 }
102
103 @Override
104 public String getType(Uri uri) {
105 SqlArguments args = new SqlArguments(uri, null, null);
106 if (TextUtils.isEmpty(args.where)) {
107 return "vnd.android.cursor.dir/" + args.table;
108 } else {
109 return "vnd.android.cursor.item/" + args.table;
110 }
111 }
112
113 @Override
114 public Cursor query(Uri uri, String[] projection, String selection,
115 String[] selectionArgs, String sortOrder) {
116
117 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
118 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
119 qb.setTables(args.table);
120
Romain Guy73b979d2009-06-09 12:57:21 -0700121 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800122 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
123 result.setNotificationUri(getContext().getContentResolver(), uri);
124
125 return result;
126 }
127
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700128 private static long dbInsertAndCheck(DatabaseHelper helper,
129 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
130 if (!values.containsKey(LauncherSettings.Favorites._ID)) {
131 throw new RuntimeException("Error: attempting to add item without specifying an id");
132 }
133 return db.insert(table, nullColumnHack, values);
134 }
135
Adam Cohen228da5a2011-07-27 22:23:47 -0700136 private static void deleteId(SQLiteDatabase db, long id) {
137 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
138 SqlArguments args = new SqlArguments(uri, null, null);
139 db.delete(args.table, args.where, args.args);
140 }
141
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800142 @Override
143 public Uri insert(Uri uri, ContentValues initialValues) {
144 SqlArguments args = new SqlArguments(uri);
145
146 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700147 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800148 if (rowId <= 0) return null;
149
150 uri = ContentUris.withAppendedId(uri, rowId);
151 sendNotify(uri);
152
153 return uri;
154 }
155
156 @Override
157 public int bulkInsert(Uri uri, ContentValues[] values) {
158 SqlArguments args = new SqlArguments(uri);
159
160 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
161 db.beginTransaction();
162 try {
163 int numValues = values.length;
164 for (int i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700165 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
166 return 0;
167 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800168 }
169 db.setTransactionSuccessful();
170 } finally {
171 db.endTransaction();
172 }
173
174 sendNotify(uri);
175 return values.length;
176 }
177
178 @Override
179 public int delete(Uri uri, String selection, String[] selectionArgs) {
180 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
181
182 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
183 int count = db.delete(args.table, args.where, args.args);
184 if (count > 0) sendNotify(uri);
185
186 return count;
187 }
188
189 @Override
190 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
191 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
192
193 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
194 int count = db.update(args.table, values, args.where, args.args);
195 if (count > 0) sendNotify(uri);
196
197 return count;
198 }
199
200 private void sendNotify(Uri uri) {
201 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
202 if (notify == null || "true".equals(notify)) {
203 getContext().getContentResolver().notifyChange(uri, null);
204 }
205 }
206
Adam Cohendcd297f2013-06-18 13:13:40 -0700207 public long generateNewItemId() {
208 return mOpenHelper.generateNewItemId();
209 }
210
211 public long generateNewScreenId() {
212 return mOpenHelper.generateNewScreenId();
213 }
214
215 // This is only required one time while loading the workspace during the
216 // upgrade path, and should never be called from anywhere else.
217 public void updateMaxScreenId(long maxScreenId) {
218 mOpenHelper.updateMaxScreenId(maxScreenId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700219 }
220
Brian Muramatsu5524b492012-10-02 16:55:54 -0700221 /**
Adam Cohene25af792013-06-06 23:08:25 -0700222 * @param Should we load the old db for upgrade? first run only.
223 */
224 synchronized public boolean shouldLoadOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400225 String spKey = LauncherAppState.getSharedPreferencesKey();
Adam Cohene25af792013-06-06 23:08:25 -0700226 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
227
Adam Cohendcd297f2013-06-18 13:13:40 -0700228 boolean loadOldDb = false || sLoadOldDb;
229
230 sLoadOldDb = false;
Adam Cohene25af792013-06-06 23:08:25 -0700231 if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
232
233 SharedPreferences.Editor editor = sp.edit();
234 editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
235 editor.commit();
236 loadOldDb = true;
237 }
238 return loadOldDb;
239 }
240
241 /**
Brian Muramatsu5524b492012-10-02 16:55:54 -0700242 * @param workspaceResId that can be 0 to use default or non-zero for specific resource
243 */
Michael Jurka45355c42012-10-08 13:21:35 +0200244 synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400245 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700246 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
Adam Cohene25af792013-06-06 23:08:25 -0700247
Michael Jurkab85f8a42012-04-25 15:48:32 -0700248 if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
Michael Jurka45355c42012-10-08 13:21:35 +0200249 int workspaceResId = origWorkspaceResId;
250
Brian Muramatsu5524b492012-10-02 16:55:54 -0700251 // Use default workspace resource if none provided
252 if (workspaceResId == 0) {
Michael Jurka45355c42012-10-08 13:21:35 +0200253 workspaceResId = sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, R.xml.default_workspace);
Brian Muramatsu5524b492012-10-02 16:55:54 -0700254 }
255
Michael Jurkab85f8a42012-04-25 15:48:32 -0700256 // Populate favorites table with initial favorites
257 SharedPreferences.Editor editor = sp.edit();
258 editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
Michael Jurka45355c42012-10-08 13:21:35 +0200259 if (origWorkspaceResId != 0) {
260 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
261 }
Adam Cohene25af792013-06-06 23:08:25 -0700262
Brian Muramatsu5524b492012-10-02 16:55:54 -0700263 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
Michael Jurkab85f8a42012-04-25 15:48:32 -0700264 editor.commit();
265 }
266 }
267
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800268 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800269 private static final String TAG_FAVORITES = "favorites";
270 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700271 private static final String TAG_CLOCK = "clock";
272 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700273 private static final String TAG_APPWIDGET = "appwidget";
274 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700275 private static final String TAG_FOLDER = "folder";
Winson Chungb3302ae2012-05-01 10:19:14 -0700276 private static final String TAG_EXTRA = "extra";
Winson Chung3d503fb2011-07-13 17:25:49 -0700277
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800278 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700279 private final AppWidgetHost mAppWidgetHost;
Adam Cohendcd297f2013-06-18 13:13:40 -0700280 private long mMaxItemId = -1;
281 private long mMaxScreenId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800282
283 DatabaseHelper(Context context) {
284 super(context, DATABASE_NAME, null, DATABASE_VERSION);
285 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700286 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700287
288 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
289 // the DB here
Adam Cohendcd297f2013-06-18 13:13:40 -0700290 if (mMaxItemId == -1) {
291 mMaxItemId = initializeMaxItemId(getWritableDatabase());
292 }
293 if (mMaxScreenId == -1) {
294 mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
Winson Chung3d503fb2011-07-13 17:25:49 -0700295 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800296 }
297
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700298 /**
299 * Send notification that we've deleted the {@link AppWidgetHost},
300 * probably as part of the initial database creation. The receiver may
301 * want to re-call {@link AppWidgetHost#startListening()} to ensure
302 * callbacks are correctly set.
303 */
304 private void sendAppWidgetResetNotify() {
305 final ContentResolver resolver = mContext.getContentResolver();
306 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
307 }
308
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800309 @Override
310 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800311 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700312
Adam Cohendcd297f2013-06-18 13:13:40 -0700313 mMaxItemId = 1;
314 mMaxScreenId = 0;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700315
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800316 db.execSQL("CREATE TABLE favorites (" +
317 "_id INTEGER PRIMARY KEY," +
318 "title TEXT," +
319 "intent TEXT," +
320 "container INTEGER," +
321 "screen INTEGER," +
322 "cellX INTEGER," +
323 "cellY INTEGER," +
324 "spanX INTEGER," +
325 "spanY INTEGER," +
326 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700327 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800328 "isShortcut INTEGER," +
329 "iconType INTEGER," +
330 "iconPackage TEXT," +
331 "iconResource TEXT," +
332 "icon BLOB," +
333 "uri TEXT," +
334 "displayMode INTEGER" +
335 ");");
Adam Cohendcd297f2013-06-18 13:13:40 -0700336 addWorkspacesTable(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800337
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700338 // Database was just created, so wipe any previous widgets
339 if (mAppWidgetHost != null) {
340 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700341 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800342 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700343
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800344 if (!convertDatabase(db)) {
Michael Jurkab85f8a42012-04-25 15:48:32 -0700345 // Set a shared pref so that we know we need to load the default workspace later
346 setFlagToLoadDefaultWorkspaceLater();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800347 }
348 }
349
Adam Cohendcd297f2013-06-18 13:13:40 -0700350 private void addWorkspacesTable(SQLiteDatabase db) {
351 db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
352 LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
353 LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER" +
354 ");");
355 }
356
Michael Jurkab85f8a42012-04-25 15:48:32 -0700357 private void setFlagToLoadDefaultWorkspaceLater() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400358 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700359 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
360 SharedPreferences.Editor editor = sp.edit();
361 editor.putBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, true);
362 editor.commit();
363 }
364
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800365 private boolean convertDatabase(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800366 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800367 boolean converted = false;
368
369 final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
370 "/old_favorites?notify=true");
371 final ContentResolver resolver = mContext.getContentResolver();
372 Cursor cursor = null;
373
374 try {
375 cursor = resolver.query(uri, null, null, null, null);
376 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700377 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800378 }
379
380 // We already have a favorites database in the old provider
381 if (cursor != null && cursor.getCount() > 0) {
382 try {
383 converted = copyFromCursor(db, cursor) > 0;
384 } finally {
385 cursor.close();
386 }
387
388 if (converted) {
389 resolver.delete(uri, null, null);
390 }
391 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700392
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800393 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700394 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800395 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800396 convertWidgets(db);
397 }
398
399 return converted;
400 }
401
402 private int copyFromCursor(SQLiteDatabase db, Cursor c) {
Romain Guy73b979d2009-06-09 12:57:21 -0700403 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800404 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
405 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
406 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
407 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
408 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
409 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
410 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
411 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
412 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
413 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
414 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
415 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
416 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
417
418 ContentValues[] rows = new ContentValues[c.getCount()];
419 int i = 0;
420 while (c.moveToNext()) {
421 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700422 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800423 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
424 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
425 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
426 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
427 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
428 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
429 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
430 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700431 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800432 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
433 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
434 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
435 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
436 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
437 rows[i++] = values;
438 }
439
440 db.beginTransaction();
441 int total = 0;
442 try {
443 int numValues = rows.length;
444 for (i = 0; i < numValues; i++) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700445 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800446 return 0;
447 } else {
448 total++;
449 }
450 }
451 db.setTransactionSuccessful();
452 } finally {
453 db.endTransaction();
454 }
455
456 return total;
457 }
458
459 @Override
460 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800461 if (LOGD) Log.d(TAG, "onUpgrade triggered");
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700462
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800463 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700464 if (version < 3) {
465 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800466 db.beginTransaction();
467 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700468 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800469 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700470 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800471 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700472 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800473 } catch (SQLException ex) {
474 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800475 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800476 } finally {
477 db.endTransaction();
478 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700479
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700481 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800482 convertWidgets(db);
483 }
484 }
Romain Guy73b979d2009-06-09 12:57:21 -0700485
486 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800487 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700488 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700489
Romain Guy509cd6a2010-03-23 15:10:56 -0700490 // Where's version 5?
491 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
Daniel Sandler325dc232013-06-05 22:57:57 -0400492 // - Passion shipped on 2.1 with version 6 of launcher3
Romain Guy509cd6a2010-03-23 15:10:56 -0700493 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
494 // but version 5 on there was the updateContactsShortcuts change
495 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
496 // The updateContactsShortcuts change is idempotent, so running it twice
497 // is okay so we'll do that when upgrading the devices that shipped with it.
498 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800499 // We went from 3 to 5 screens. Move everything 1 to the right
500 db.beginTransaction();
501 try {
502 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
503 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800504 } catch (SQLException ex) {
505 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800506 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800507 } finally {
508 db.endTransaction();
509 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700510
Romain Guy509cd6a2010-03-23 15:10:56 -0700511 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800512 if (updateContactsShortcuts(db)) {
513 version = 6;
514 }
515 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000516
517 if (version < 7) {
518 // Version 7 gets rid of the special search widget.
519 convertWidgets(db);
520 version = 7;
521 }
522
Joe Onorato0589f0f2010-02-08 13:44:00 -0800523 if (version < 8) {
524 // Version 8 (froyo) has the icons all normalized. This should
525 // already be the case in practice, but we now rely on it and don't
526 // resample the images each time.
527 normalizeIcons(db);
528 version = 8;
529 }
530
Winson Chung3d503fb2011-07-13 17:25:49 -0700531 if (version < 9) {
532 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
533 // before it gets a change to get set, so we need to read it here when we use it)
Adam Cohendcd297f2013-06-18 13:13:40 -0700534 if (mMaxItemId == -1) {
535 mMaxItemId = initializeMaxItemId(db);
Winson Chung3d503fb2011-07-13 17:25:49 -0700536 }
537
538 // Add default hotseat icons
Winson Chung6d092682011-11-16 18:43:26 -0800539 loadFavorites(db, R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700540 version = 9;
541 }
542
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700543 // We bumped the version three time during JB, once to update the launch flags, once to
544 // update the override for the default launch animation and once to set the mimetype
545 // to improve startup performance
546 if (version < 12) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700547 // Contact shortcuts need a different set of flags to be launched now
548 // The updateContactsShortcuts change is idempotent, so we can keep using it like
549 // back in the Donut days
550 updateContactsShortcuts(db);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700551 version = 12;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700552 }
553
Adam Cohendcd297f2013-06-18 13:13:40 -0700554 if (version < 13) {
555 // With the new shrink-wrapped and re-orderable workspaces, it makes sense
556 // to persist workspace screens and their relative order.
557 mMaxScreenId = 0;
558
559 // This will never happen in the wild, but when we switch to using workspace
560 // screen ids, redo the import from old launcher.
561 sLoadOldDb = true;
562
563 addWorkspacesTable(db);
564 version = 13;
565 }
566
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800567 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800568 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800569 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
Adam Cohendcd297f2013-06-18 13:13:40 -0700570 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
571
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800572 onCreate(db);
573 }
574 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800575
576 private boolean updateContactsShortcuts(SQLiteDatabase db) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800577 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
578 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
579
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700580 Cursor c = null;
581 final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800582 db.beginTransaction();
583 try {
584 // Select and iterate through each matching widget
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700585 c = db.query(TABLE_FAVORITES,
586 new String[] { Favorites._ID, Favorites.INTENT },
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800587 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700588 if (c == null) return false;
589
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800590 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700591
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800592 final int idIndex = c.getColumnIndex(Favorites._ID);
593 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700594
595 while (c.moveToNext()) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800596 long favoriteId = c.getLong(idIndex);
597 final String intentUri = c.getString(intentIndex);
598 if (intentUri != null) {
599 try {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700600 final Intent intent = Intent.parseUri(intentUri, 0);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800601 android.util.Log.d("Home", intent.toString());
602 final Uri uri = intent.getData();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700603 if (uri != null) {
604 final String data = uri.toString();
605 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
606 actionQuickContact.equals(intent.getAction())) &&
607 (data.startsWith("content://contacts/people/") ||
608 data.startsWith("content://com.android.contacts/" +
609 "contacts/lookup/"))) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800610
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700611 final Intent newIntent = new Intent(actionQuickContact);
612 // When starting from the launcher, start in a new, cleared task
613 // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
614 // clear the whole thing preemptively here since
615 // QuickContactActivity will finish itself when launching other
616 // detail activities.
617 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
618 Intent.FLAG_ACTIVITY_CLEAR_TASK);
Winson Chung2672ff92012-05-04 16:22:30 -0700619 newIntent.putExtra(
620 Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700621 newIntent.setData(uri);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700622 // Determine the type and also put that in the shortcut
623 // (that can speed up launch a bit)
624 newIntent.setDataAndType(uri, newIntent.resolveType(mContext));
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800625
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700626 final ContentValues values = new ContentValues();
627 values.put(LauncherSettings.Favorites.INTENT,
628 newIntent.toUri(0));
629
630 String updateWhere = Favorites._ID + "=" + favoriteId;
631 db.update(TABLE_FAVORITES, values, updateWhere, null);
632 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800633 }
634 } catch (RuntimeException ex) {
635 Log.e(TAG, "Problem upgrading shortcut", ex);
636 } catch (URISyntaxException e) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700637 Log.e(TAG, "Problem upgrading shortcut", e);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800638 }
639 }
640 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700641
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800642 db.setTransactionSuccessful();
643 } catch (SQLException ex) {
644 Log.w(TAG, "Problem while upgrading contacts", ex);
645 return false;
646 } finally {
647 db.endTransaction();
648 if (c != null) {
649 c.close();
650 }
651 }
652
653 return true;
654 }
655
Joe Onorato0589f0f2010-02-08 13:44:00 -0800656 private void normalizeIcons(SQLiteDatabase db) {
657 Log.d(TAG, "normalizing icons");
658
Joe Onorato346e1292010-02-18 10:34:24 -0500659 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800660 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400661 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800662 try {
663 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400664 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600665 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800666
667 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
668 Favorites.ICON_TYPE_BITMAP, null);
669
670 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
671 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
672
673 while (c.moveToNext()) {
674 long id = c.getLong(idIndex);
675 byte[] data = c.getBlob(iconIndex);
676 try {
677 Bitmap bitmap = Utilities.resampleIconBitmap(
678 BitmapFactory.decodeByteArray(data, 0, data.length),
679 mContext);
680 if (bitmap != null) {
681 update.bindLong(1, id);
682 data = ItemInfo.flattenBitmap(bitmap);
683 if (data != null) {
684 update.bindBlob(2, data);
685 update.execute();
686 }
687 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800688 }
689 } catch (Exception e) {
690 if (!logged) {
691 Log.e(TAG, "Failed normalizing icon " + id, e);
692 } else {
693 Log.e(TAG, "Also failed normalizing icon " + id);
694 }
695 logged = true;
696 }
697 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000698 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800699 } catch (SQLException ex) {
700 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
701 } finally {
702 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400703 if (update != null) {
704 update.close();
705 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800706 if (c != null) {
707 c.close();
708 }
709 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700710 }
711
712 // Generates a new ID to use for an object in your database. This method should be only
713 // called from the main UI thread. As an exception, we do call it when we call the
714 // constructor from the worker thread; however, this doesn't extend until after the
715 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
716 // after that point
Adam Cohendcd297f2013-06-18 13:13:40 -0700717 public long generateNewItemId() {
718 if (mMaxItemId < 0) {
719 throw new RuntimeException("Error: max item id was not initialized");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700720 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700721 mMaxItemId += 1;
722 return mMaxItemId;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700723 }
724
Adam Cohendcd297f2013-06-18 13:13:40 -0700725 private long initializeMaxItemId(SQLiteDatabase db) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700726 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
727
728 // get the result
729 final int maxIdIndex = 0;
730 long id = -1;
731 if (c != null && c.moveToNext()) {
732 id = c.getLong(maxIdIndex);
733 }
Michael Jurka5130e402011-10-13 04:55:35 -0700734 if (c != null) {
735 c.close();
736 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700737
738 if (id == -1) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700739 throw new RuntimeException("Error: could not query max item id");
740 }
741
742 return id;
743 }
744
745 // Generates a new ID to use for an workspace screen in your database. This method
746 // should be only called from the main UI thread. As an exception, we do call it when we
747 // call the constructor from the worker thread; however, this doesn't extend until after the
748 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
749 // after that point
750 public long generateNewScreenId() {
751 if (mMaxScreenId < 0) {
752 throw new RuntimeException("Error: max screen id was not initialized");
753 }
754 mMaxScreenId += 1;
755 return mMaxScreenId;
756 }
757
758 public void updateMaxScreenId(long maxScreenId) {
759 mMaxScreenId = maxScreenId;
760 }
761
762 private long initializeMaxScreenId(SQLiteDatabase db) {
763 Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
764
765 // get the result
766 final int maxIdIndex = 0;
767 long id = -1;
768 if (c != null && c.moveToNext()) {
769 id = c.getLong(maxIdIndex);
770 }
771 if (c != null) {
772 c.close();
773 }
774
775 if (id == -1) {
776 throw new RuntimeException("Error: could not query max screen id");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700777 }
778
779 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800780 }
781
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800782 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700783 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000784 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800785 */
786 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000787 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800788 final int[] bindSources = new int[] {
789 Favorites.ITEM_TYPE_WIDGET_CLOCK,
790 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000791 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800792 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000793
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800794 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700795
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800796 Cursor c = null;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700797
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800798 db.beginTransaction();
799 try {
800 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000801 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800802 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700803
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800804 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700805
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800806 final ContentValues values = new ContentValues();
807 while (c != null && c.moveToNext()) {
808 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000809 int favoriteType = c.getInt(1);
810
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700811 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800812 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700813 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700814
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800815 if (LOGD) {
816 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
817 + " for favoriteId=" + favoriteId);
818 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800819 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000820 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
821 values.put(Favorites.APPWIDGET_ID, appWidgetId);
822
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800823 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +0000824 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
825 values.put(LauncherSettings.Favorites.SPANX, 4);
826 values.put(LauncherSettings.Favorites.SPANY, 1);
827 } else {
828 values.put(LauncherSettings.Favorites.SPANX, 2);
829 values.put(LauncherSettings.Favorites.SPANY, 2);
830 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800831
832 String updateWhere = Favorites._ID + "=" + favoriteId;
833 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +0000834
Bjorn Bringert34251342009-12-15 13:33:11 +0000835 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700836 // TODO: check return value
837 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +0000838 new ComponentName("com.android.alarmclock",
839 "com.android.alarmclock.AnalogAppWidgetProvider"));
840 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700841 // TODO: check return value
842 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +0000843 new ComponentName("com.android.camera",
844 "com.android.camera.PhotoAppWidgetProvider"));
845 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700846 // TODO: check return value
847 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +0000848 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +0000849 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800850 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800851 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800852 }
853 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700854
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800855 db.setTransactionSuccessful();
856 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800857 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800858 } finally {
859 db.endTransaction();
860 if (c != null) {
861 c.close();
862 }
863 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800864 }
865
Michael Jurka8b805b12012-04-18 14:23:14 -0700866 private static final void beginDocument(XmlPullParser parser, String firstElementName)
867 throws XmlPullParserException, IOException {
868 int type;
Michael Jurka9bc8eba2012-05-21 20:36:44 -0700869 while ((type = parser.next()) != XmlPullParser.START_TAG
870 && type != XmlPullParser.END_DOCUMENT) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700871 ;
872 }
873
Michael Jurka9bc8eba2012-05-21 20:36:44 -0700874 if (type != XmlPullParser.START_TAG) {
Michael Jurka8b805b12012-04-18 14:23:14 -0700875 throw new XmlPullParserException("No start tag found");
876 }
877
878 if (!parser.getName().equals(firstElementName)) {
879 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
880 ", expected " + firstElementName);
881 }
882 }
883
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800884 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800885 * Loads the default set of favorite packages from an xml file.
886 *
887 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -0700888 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800889 */
Winson Chung6d092682011-11-16 18:43:26 -0800890 private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800891 Intent intent = new Intent(Intent.ACTION_MAIN, null);
892 intent.addCategory(Intent.CATEGORY_LAUNCHER);
893 ContentValues values = new ContentValues();
894
895 PackageManager packageManager = mContext.getPackageManager();
Andrew Flynn0dca1ec2012-02-29 13:33:22 -0800896 int allAppsButtonRank =
897 mContext.getResources().getInteger(R.integer.hotseat_all_apps_index);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800898 int i = 0;
899 try {
Winson Chung6d092682011-11-16 18:43:26 -0800900 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700901 AttributeSet attrs = Xml.asAttributeSet(parser);
Michael Jurka8b805b12012-04-18 14:23:14 -0700902 beginDocument(parser, TAG_FAVORITES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800903
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700904 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800905
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700906 int type;
907 while (((type = parser.next()) != XmlPullParser.END_TAG ||
908 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
909
910 if (type != XmlPullParser.START_TAG) {
911 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800912 }
913
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700914 boolean added = false;
915 final String name = parser.getName();
916
917 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
918
Winson Chung3d503fb2011-07-13 17:25:49 -0700919 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
920 if (a.hasValue(R.styleable.Favorite_container)) {
921 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
922 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700923
Winson Chung6d092682011-11-16 18:43:26 -0800924 String screen = a.getString(R.styleable.Favorite_screen);
925 String x = a.getString(R.styleable.Favorite_x);
926 String y = a.getString(R.styleable.Favorite_y);
927
928 // If we are adding to the hotseat, the screen is used as the position in the
929 // hotseat. This screen can't be at position 0 because AllApps is in the
930 // zeroth position.
Andrew Flynn0dca1ec2012-02-29 13:33:22 -0800931 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
932 && Integer.valueOf(screen) == allAppsButtonRank) {
Winson Chung6d092682011-11-16 18:43:26 -0800933 throw new RuntimeException("Invalid screen position for hotseat item");
934 }
935
936 values.clear();
937 values.put(LauncherSettings.Favorites.CONTAINER, container);
938 values.put(LauncherSettings.Favorites.SCREEN, screen);
939 values.put(LauncherSettings.Favorites.CELLX, x);
940 values.put(LauncherSettings.Favorites.CELLY, y);
941
942 if (TAG_FAVORITE.equals(name)) {
943 long id = addAppShortcut(db, values, a, packageManager, intent);
944 added = id >= 0;
945 } else if (TAG_SEARCH.equals(name)) {
946 added = addSearchWidget(db, values);
947 } else if (TAG_CLOCK.equals(name)) {
948 added = addClockWidget(db, values);
949 } else if (TAG_APPWIDGET.equals(name)) {
Winson Chungb3302ae2012-05-01 10:19:14 -0700950 added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
Winson Chung6d092682011-11-16 18:43:26 -0800951 } else if (TAG_SHORTCUT.equals(name)) {
952 long id = addUriShortcut(db, values, a);
953 added = id >= 0;
954 } else if (TAG_FOLDER.equals(name)) {
955 String title;
956 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
957 if (titleResId != -1) {
958 title = mContext.getResources().getString(titleResId);
959 } else {
960 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -0700961 }
Winson Chung6d092682011-11-16 18:43:26 -0800962 values.put(LauncherSettings.Favorites.TITLE, title);
963 long folderId = addFolder(db, values);
964 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -0700965
Winson Chung6d092682011-11-16 18:43:26 -0800966 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -0700967
Winson Chung6d092682011-11-16 18:43:26 -0800968 int folderDepth = parser.getDepth();
969 while ((type = parser.next()) != XmlPullParser.END_TAG ||
970 parser.getDepth() > folderDepth) {
971 if (type != XmlPullParser.START_TAG) {
972 continue;
973 }
974 final String folder_item_name = parser.getName();
975
976 TypedArray ar = mContext.obtainStyledAttributes(attrs,
977 R.styleable.Favorite);
978 values.clear();
979 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
980
981 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
982 long id =
983 addAppShortcut(db, values, ar, packageManager, intent);
984 if (id >= 0) {
985 folderItems.add(id);
986 }
987 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
988 long id = addUriShortcut(db, values, ar);
989 if (id >= 0) {
990 folderItems.add(id);
991 }
Adam Cohen228da5a2011-07-27 22:23:47 -0700992 } else {
Winson Chung6d092682011-11-16 18:43:26 -0800993 throw new RuntimeException("Folders can " +
994 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -0700995 }
Winson Chung6d092682011-11-16 18:43:26 -0800996 ar.recycle();
997 }
998 // We can only have folders with >= 2 items, so we need to remove the
999 // folder and clean up if less than 2 items were included, or some
1000 // failed to add, and less than 2 were actually added
1001 if (folderItems.size() < 2 && folderId >= 0) {
1002 // We just delete the folder and any items that made it
1003 deleteId(db, folderId);
1004 if (folderItems.size() > 0) {
1005 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -07001006 }
Winson Chung6d092682011-11-16 18:43:26 -08001007 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -07001008 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001009 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001010 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001011 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001012 }
1013 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001014 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001015 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001016 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -07001017 } catch (RuntimeException e) {
1018 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001019 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001020
1021 return i;
1022 }
1023
Adam Cohen228da5a2011-07-27 22:23:47 -07001024 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001025 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -07001026 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001027 ActivityInfo info;
1028 String packageName = a.getString(R.styleable.Favorite_packageName);
1029 String className = a.getString(R.styleable.Favorite_className);
1030 try {
Romain Guy693599f2010-03-23 10:58:18 -07001031 ComponentName cn;
1032 try {
1033 cn = new ComponentName(packageName, className);
1034 info = packageManager.getActivityInfo(cn, 0);
1035 } catch (PackageManager.NameNotFoundException nnfe) {
1036 String[] packages = packageManager.currentToCanonicalPackageNames(
1037 new String[] { packageName });
1038 cn = new ComponentName(packages[0], className);
1039 info = packageManager.getActivityInfo(cn, 0);
1040 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001041 id = generateNewItemId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001042 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -07001043 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1044 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -07001045 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001046 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
1047 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
1048 values.put(Favorites.SPANX, 1);
1049 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001050 values.put(Favorites._ID, generateNewItemId());
Adam Cohen228da5a2011-07-27 22:23:47 -07001051 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1052 return -1;
1053 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001054 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001055 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001056 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001057 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001058 return id;
1059 }
1060
1061 private long addFolder(SQLiteDatabase db, ContentValues values) {
1062 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
1063 values.put(Favorites.SPANX, 1);
1064 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001065 long id = generateNewItemId();
Adam Cohen228da5a2011-07-27 22:23:47 -07001066 values.put(Favorites._ID, id);
1067 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
1068 return -1;
1069 } else {
1070 return id;
1071 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001072 }
1073
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001074 private ComponentName getSearchWidgetProvider() {
1075 SearchManager searchManager =
1076 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
1077 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
1078 if (searchComponent == null) return null;
1079 return getProviderInPackage(searchComponent.getPackageName());
1080 }
1081
1082 /**
1083 * Gets an appwidget provider from the given package. If the package contains more than
1084 * one appwidget provider, an arbitrary one is returned.
1085 */
1086 private ComponentName getProviderInPackage(String packageName) {
1087 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1088 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
1089 if (providers == null) return null;
1090 final int providerCount = providers.size();
1091 for (int i = 0; i < providerCount; i++) {
1092 ComponentName provider = providers.get(i).provider;
1093 if (provider != null && provider.getPackageName().equals(packageName)) {
1094 return provider;
1095 }
1096 }
1097 return null;
1098 }
1099
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001100 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001101 ComponentName cn = getSearchWidgetProvider();
Winson Chungb3302ae2012-05-01 10:19:14 -07001102 return addAppWidget(db, values, cn, 4, 1, null);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001103 }
1104
1105 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +00001106 ComponentName cn = new ComponentName("com.android.alarmclock",
1107 "com.android.alarmclock.AnalogAppWidgetProvider");
Winson Chungb3302ae2012-05-01 10:19:14 -07001108 return addAppWidget(db, values, cn, 2, 2, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001109 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001110
Winson Chungb3302ae2012-05-01 10:19:14 -07001111 private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
1112 SQLiteDatabase db, ContentValues values, TypedArray a,
1113 PackageManager packageManager) throws XmlPullParserException, IOException {
Romain Guy693599f2010-03-23 10:58:18 -07001114
Mike Cleronb87bd162009-10-30 16:36:56 -07001115 String packageName = a.getString(R.styleable.Favorite_packageName);
1116 String className = a.getString(R.styleable.Favorite_className);
1117
1118 if (packageName == null || className == null) {
1119 return false;
1120 }
Romain Guy693599f2010-03-23 10:58:18 -07001121
1122 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -07001123 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -07001124 try {
1125 packageManager.getReceiverInfo(cn, 0);
1126 } catch (Exception e) {
1127 String[] packages = packageManager.currentToCanonicalPackageNames(
1128 new String[] { packageName });
1129 cn = new ComponentName(packages[0], className);
1130 try {
1131 packageManager.getReceiverInfo(cn, 0);
1132 } catch (Exception e1) {
1133 hasPackage = false;
1134 }
1135 }
1136
1137 if (hasPackage) {
1138 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
1139 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
Winson Chungb3302ae2012-05-01 10:19:14 -07001140
1141 // Read the extras
1142 Bundle extras = new Bundle();
1143 int widgetDepth = parser.getDepth();
1144 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1145 parser.getDepth() > widgetDepth) {
1146 if (type != XmlPullParser.START_TAG) {
1147 continue;
1148 }
1149
1150 TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
1151 if (TAG_EXTRA.equals(parser.getName())) {
1152 String key = ar.getString(R.styleable.Extra_key);
1153 String value = ar.getString(R.styleable.Extra_value);
1154 if (key != null && value != null) {
1155 extras.putString(key, value);
1156 } else {
1157 throw new RuntimeException("Widget extras must have a key and value");
1158 }
1159 } else {
1160 throw new RuntimeException("Widgets can contain only extras");
1161 }
1162 ar.recycle();
1163 }
1164
1165 return addAppWidget(db, values, cn, spanX, spanY, extras);
Romain Guy693599f2010-03-23 10:58:18 -07001166 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001167
Romain Guy693599f2010-03-23 10:58:18 -07001168 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +00001169 }
Bjorn Bringert7984c942009-12-09 15:38:25 +00001170 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
Winson Chungb3302ae2012-05-01 10:19:14 -07001171 int spanX, int spanY, Bundle extras) {
Mike Cleronb87bd162009-10-30 16:36:56 -07001172 boolean allocatedAppWidgets = false;
1173 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1174
1175 try {
1176 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001177
Mike Cleronb87bd162009-10-30 16:36:56 -07001178 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +00001179 values.put(Favorites.SPANX, spanX);
1180 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -07001181 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Adam Cohendcd297f2013-06-18 13:13:40 -07001182 values.put(Favorites._ID, generateNewItemId());
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001183 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -07001184
1185 allocatedAppWidgets = true;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001186
Michael Jurka8b805b12012-04-18 14:23:14 -07001187 // TODO: need to check return value
1188 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
Winson Chungb3302ae2012-05-01 10:19:14 -07001189
1190 // Send a broadcast to configure the widget
1191 if (extras != null && !extras.isEmpty()) {
1192 Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
1193 intent.setComponent(cn);
1194 intent.putExtras(extras);
1195 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1196 mContext.sendBroadcast(intent);
1197 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001198 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001199 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -07001200 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001201
Mike Cleronb87bd162009-10-30 16:36:56 -07001202 return allocatedAppWidgets;
1203 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001204
1205 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -07001206 TypedArray a) {
1207 Resources r = mContext.getResources();
1208
1209 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
1210 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
1211
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001212 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -07001213 String uri = null;
1214 try {
1215 uri = a.getString(R.styleable.Favorite_uri);
1216 intent = Intent.parseUri(uri, 0);
1217 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001218 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -07001219 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -07001220 }
1221
1222 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001223 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001224 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001225 }
1226
Adam Cohendcd297f2013-06-18 13:13:40 -07001227 long id = generateNewItemId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001228 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1229 values.put(Favorites.INTENT, intent.toUri(0));
1230 values.put(Favorites.TITLE, r.getString(titleResId));
1231 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1232 values.put(Favorites.SPANX, 1);
1233 values.put(Favorites.SPANY, 1);
1234 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1235 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1236 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001237 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001238
Adam Cohen228da5a2011-07-27 22:23:47 -07001239 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1240 return -1;
1241 }
1242 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001243 }
1244 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001245
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 /**
1247 * Build a query string that will match any row where the column matches
1248 * anything in the values list.
1249 */
1250 static String buildOrWhereString(String column, int[] values) {
1251 StringBuilder selectWhere = new StringBuilder();
1252 for (int i = values.length - 1; i >= 0; i--) {
1253 selectWhere.append(column).append("=").append(values[i]);
1254 if (i > 0) {
1255 selectWhere.append(" OR ");
1256 }
1257 }
1258 return selectWhere.toString();
1259 }
1260
1261 static class SqlArguments {
1262 public final String table;
1263 public final String where;
1264 public final String[] args;
1265
1266 SqlArguments(Uri url, String where, String[] args) {
1267 if (url.getPathSegments().size() == 1) {
1268 this.table = url.getPathSegments().get(0);
1269 this.where = where;
1270 this.args = args;
1271 } else if (url.getPathSegments().size() != 2) {
1272 throw new IllegalArgumentException("Invalid URI: " + url);
1273 } else if (!TextUtils.isEmpty(where)) {
1274 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1275 } else {
1276 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001277 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001278 this.args = null;
1279 }
1280 }
1281
1282 SqlArguments(Uri url) {
1283 if (url.getPathSegments().size() == 1) {
1284 table = url.getPathSegments().get(0);
1285 where = null;
1286 args = null;
1287 } else {
1288 throw new IllegalArgumentException("Invalid URI: " + url);
1289 }
1290 }
1291 }
1292}