blob: 7adbadea10b41049fa5ed83065026e99e5eb3ec3 [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;
Chris Wrene523e702013-10-09 10:36:55 -040053import com.android.launcher3.config.ProviderConfig;
Michael Jurka8b805b12012-04-18 14:23:14 -070054
55import org.xmlpull.v1.XmlPullParser;
56import org.xmlpull.v1.XmlPullParserException;
57
Dan Sandlerd5024042014-01-09 15:01:33 -050058import java.io.File;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070060import java.net.URISyntaxException;
Adam Cohen228da5a2011-07-27 22:23:47 -070061import java.util.ArrayList;
Dan Sandlerd5024042014-01-09 15:01:33 -050062import java.util.HashSet;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000063import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080066 private static final String TAG = "Launcher.LauncherProvider";
67 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080068
69 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070070
Chris Wren1ada10d2013-09-13 18:01:38 -040071 private static final int DATABASE_VERSION = 15;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072
Adam Cohene25af792013-06-06 23:08:25 -070073 static final String OLD_AUTHORITY = "com.android.launcher2.settings";
Chris Wrene523e702013-10-09 10:36:55 -040074 static final String AUTHORITY = ProviderConfig.AUTHORITY;
Winson Chung3d503fb2011-07-13 17:25:49 -070075
Dan Sandlerf0b8dac2013-11-19 12:21:25 -050076 // Should we attempt to load anything from the com.android.launcher2 provider?
Dan Sandlerd5024042014-01-09 15:01:33 -050077 static final boolean IMPORT_LAUNCHER2_DATABASE = false;
Dan Sandlerf0b8dac2013-11-19 12:21:25 -050078
The Android Open Source Project31dd5032009-03-03 19:32:27 -080079 static final String TABLE_FAVORITES = "favorites";
Adam Cohendcd297f2013-06-18 13:13:40 -070080 static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081 static final String PARAMETER_NOTIFY = "notify";
Winson Chungc763c4e2013-07-19 13:49:06 -070082 static final String UPGRADED_FROM_OLD_DATABASE =
83 "UPGRADED_FROM_OLD_DATABASE";
84 static final String EMPTY_DATABASE_CREATED =
85 "EMPTY_DATABASE_CREATED";
Michael Jurka45355c42012-10-08 13:21:35 +020086 static final String DEFAULT_WORKSPACE_RESOURCE_ID =
87 "DEFAULT_WORKSPACE_RESOURCE_ID";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088
Winson Chungb3302ae2012-05-01 10:19:14 -070089 private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
Adam Cohene25af792013-06-06 23:08:25 -070090 "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
Winson Chungb3302ae2012-05-01 10:19:14 -070091
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070092 /**
Romain Guy73b979d2009-06-09 12:57:21 -070093 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070094 * {@link AppWidgetHost#deleteHost()} is called during database creation.
95 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
96 */
97 static final Uri CONTENT_APPWIDGET_RESET_URI =
98 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
Daniel Lehmannc3a80402012-04-23 21:35:11 -070099
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700100 private DatabaseHelper mOpenHelper;
Winson Chungc763c4e2013-07-19 13:49:06 -0700101 private static boolean sJustLoadedFromOldDb;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800102
103 @Override
104 public boolean onCreate() {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400105 final Context context = getContext();
106 mOpenHelper = new DatabaseHelper(context);
107 LauncherAppState.setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 return true;
109 }
110
111 @Override
112 public String getType(Uri uri) {
113 SqlArguments args = new SqlArguments(uri, null, null);
114 if (TextUtils.isEmpty(args.where)) {
115 return "vnd.android.cursor.dir/" + args.table;
116 } else {
117 return "vnd.android.cursor.item/" + args.table;
118 }
119 }
120
121 @Override
122 public Cursor query(Uri uri, String[] projection, String selection,
123 String[] selectionArgs, String sortOrder) {
124
125 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
126 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
127 qb.setTables(args.table);
128
Romain Guy73b979d2009-06-09 12:57:21 -0700129 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800130 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
131 result.setNotificationUri(getContext().getContentResolver(), uri);
132
133 return result;
134 }
135
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700136 private static long dbInsertAndCheck(DatabaseHelper helper,
137 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
Dan Sandlerd5024042014-01-09 15:01:33 -0500138 if (values == null) {
139 throw new RuntimeException("Error: attempting to insert null values");
140 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700141 if (!values.containsKey(LauncherSettings.Favorites._ID)) {
142 throw new RuntimeException("Error: attempting to add item without specifying an id");
143 }
144 return db.insert(table, nullColumnHack, values);
145 }
146
Adam Cohen228da5a2011-07-27 22:23:47 -0700147 private static void deleteId(SQLiteDatabase db, long id) {
148 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
149 SqlArguments args = new SqlArguments(uri, null, null);
150 db.delete(args.table, args.where, args.args);
151 }
152
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800153 @Override
154 public Uri insert(Uri uri, ContentValues initialValues) {
155 SqlArguments args = new SqlArguments(uri);
156
157 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Chris Wren1ada10d2013-09-13 18:01:38 -0400158 addModifiedTime(initialValues);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700159 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800160 if (rowId <= 0) return null;
161
162 uri = ContentUris.withAppendedId(uri, rowId);
163 sendNotify(uri);
164
165 return uri;
166 }
167
168 @Override
169 public int bulkInsert(Uri uri, ContentValues[] values) {
170 SqlArguments args = new SqlArguments(uri);
171
172 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
173 db.beginTransaction();
174 try {
175 int numValues = values.length;
176 for (int i = 0; i < numValues; i++) {
Chris Wren1ada10d2013-09-13 18:01:38 -0400177 addModifiedTime(values[i]);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700178 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
179 return 0;
180 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800181 }
182 db.setTransactionSuccessful();
183 } finally {
184 db.endTransaction();
185 }
186
187 sendNotify(uri);
188 return values.length;
189 }
190
191 @Override
192 public int delete(Uri uri, String selection, String[] selectionArgs) {
193 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
194
195 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
196 int count = db.delete(args.table, args.where, args.args);
197 if (count > 0) sendNotify(uri);
198
199 return count;
200 }
201
202 @Override
203 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
204 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
205
Chris Wren1ada10d2013-09-13 18:01:38 -0400206 addModifiedTime(values);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800207 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
208 int count = db.update(args.table, values, args.where, args.args);
209 if (count > 0) sendNotify(uri);
210
211 return count;
212 }
213
214 private void sendNotify(Uri uri) {
215 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
216 if (notify == null || "true".equals(notify)) {
217 getContext().getContentResolver().notifyChange(uri, null);
218 }
Chris Wren1ada10d2013-09-13 18:01:38 -0400219
220 // always notify the backup agent
Chris Wren92aa4232013-10-04 11:29:36 -0400221 LauncherBackupAgentHelper.dataChanged(getContext());
Chris Wren1ada10d2013-09-13 18:01:38 -0400222 }
223
224 private void addModifiedTime(ContentValues values) {
225 values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800226 }
227
Adam Cohendcd297f2013-06-18 13:13:40 -0700228 public long generateNewItemId() {
229 return mOpenHelper.generateNewItemId();
230 }
231
Winson Chungc763c4e2013-07-19 13:49:06 -0700232 public void updateMaxItemId(long id) {
233 mOpenHelper.updateMaxItemId(id);
234 }
235
Adam Cohendcd297f2013-06-18 13:13:40 -0700236 public long generateNewScreenId() {
237 return mOpenHelper.generateNewScreenId();
238 }
239
240 // This is only required one time while loading the workspace during the
241 // upgrade path, and should never be called from anywhere else.
242 public void updateMaxScreenId(long maxScreenId) {
243 mOpenHelper.updateMaxScreenId(maxScreenId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700244 }
245
Brian Muramatsu5524b492012-10-02 16:55:54 -0700246 /**
Adam Cohene25af792013-06-06 23:08:25 -0700247 * @param Should we load the old db for upgrade? first run only.
248 */
Winson Chungc763c4e2013-07-19 13:49:06 -0700249 synchronized public boolean justLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400250 String spKey = LauncherAppState.getSharedPreferencesKey();
Adam Cohene25af792013-06-06 23:08:25 -0700251 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
252
Winson Chungc763c4e2013-07-19 13:49:06 -0700253 boolean loadedOldDb = false || sJustLoadedFromOldDb;
Adam Cohendcd297f2013-06-18 13:13:40 -0700254
Winson Chungc763c4e2013-07-19 13:49:06 -0700255 sJustLoadedFromOldDb = false;
256 if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) {
Adam Cohene25af792013-06-06 23:08:25 -0700257
258 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700259 editor.remove(UPGRADED_FROM_OLD_DATABASE);
Adam Cohene25af792013-06-06 23:08:25 -0700260 editor.commit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700261 loadedOldDb = true;
Adam Cohene25af792013-06-06 23:08:25 -0700262 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700263 return loadedOldDb;
Adam Cohene25af792013-06-06 23:08:25 -0700264 }
265
266 /**
Brian Muramatsu5524b492012-10-02 16:55:54 -0700267 * @param workspaceResId that can be 0 to use default or non-zero for specific resource
268 */
Michael Jurka45355c42012-10-08 13:21:35 +0200269 synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400270 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700271 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
Adam Cohene25af792013-06-06 23:08:25 -0700272
Winson Chungc763c4e2013-07-19 13:49:06 -0700273 if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Michael Jurka45355c42012-10-08 13:21:35 +0200274 int workspaceResId = origWorkspaceResId;
275
Brian Muramatsu5524b492012-10-02 16:55:54 -0700276 // Use default workspace resource if none provided
277 if (workspaceResId == 0) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800278 workspaceResId =
279 sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
Brian Muramatsu5524b492012-10-02 16:55:54 -0700280 }
281
Michael Jurkab85f8a42012-04-25 15:48:32 -0700282 // Populate favorites table with initial favorites
283 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700284 editor.remove(EMPTY_DATABASE_CREATED);
Michael Jurka45355c42012-10-08 13:21:35 +0200285 if (origWorkspaceResId != 0) {
286 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
287 }
Adam Cohene25af792013-06-06 23:08:25 -0700288
Brian Muramatsu5524b492012-10-02 16:55:54 -0700289 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
Winson Chungc763c4e2013-07-19 13:49:06 -0700290 mOpenHelper.setFlagJustLoadedOldDb();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700291 editor.commit();
292 }
293 }
294
Dan Sandlerd5024042014-01-09 15:01:33 -0500295 public void migrateLauncher2Shortcuts() {
296 mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
297 LauncherSettings.Favorites.OLD_CONTENT_URI);
298 }
299
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800300 private static int getDefaultWorkspaceResourceId() {
Nilesh Agrawal16f3ea82014-01-09 17:14:01 -0800301 if (LauncherAppState.isDisableAllApps()) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800302 return R.xml.default_workspace_no_all_apps;
303 } else {
304 return R.xml.default_workspace;
305 }
306 }
307
Winson Chungc763c4e2013-07-19 13:49:06 -0700308 private static interface ContentValuesCallback {
309 public void onRow(ContentValues values);
310 }
311
Adam Cohen6dbe0492013-12-02 17:00:14 -0800312 private static boolean shouldImportLauncher2Database(Context context) {
313 boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet);
314
315 // We don't import the old databse for tablets, as the grid size has changed.
316 return !isTablet && IMPORT_LAUNCHER2_DATABASE;
317 }
318
Dan Sandlerd5024042014-01-09 15:01:33 -0500319 public void deleteDatabase() {
320 // Are you sure? (y/n)
321 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
322 final String dbFile = db.getPath();
323 mOpenHelper.close();
324 SQLiteDatabase.deleteDatabase(new File(dbFile));
325 mOpenHelper = new DatabaseHelper(getContext());
326 }
327
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800328 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800329 private static final String TAG_FAVORITES = "favorites";
330 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700331 private static final String TAG_CLOCK = "clock";
332 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700333 private static final String TAG_APPWIDGET = "appwidget";
334 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700335 private static final String TAG_FOLDER = "folder";
Winson Chungb3302ae2012-05-01 10:19:14 -0700336 private static final String TAG_EXTRA = "extra";
Daniel Sandler57dac262013-10-03 13:28:36 -0400337 private static final String TAG_INCLUDE = "include";
Winson Chung3d503fb2011-07-13 17:25:49 -0700338
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800339 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700340 private final AppWidgetHost mAppWidgetHost;
Adam Cohendcd297f2013-06-18 13:13:40 -0700341 private long mMaxItemId = -1;
342 private long mMaxScreenId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800343
344 DatabaseHelper(Context context) {
345 super(context, DATABASE_NAME, null, DATABASE_VERSION);
346 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700347 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700348
349 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
350 // the DB here
Adam Cohendcd297f2013-06-18 13:13:40 -0700351 if (mMaxItemId == -1) {
352 mMaxItemId = initializeMaxItemId(getWritableDatabase());
353 }
354 if (mMaxScreenId == -1) {
355 mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
Winson Chung3d503fb2011-07-13 17:25:49 -0700356 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800357 }
358
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700359 /**
360 * Send notification that we've deleted the {@link AppWidgetHost},
361 * probably as part of the initial database creation. The receiver may
362 * want to re-call {@link AppWidgetHost#startListening()} to ensure
363 * callbacks are correctly set.
364 */
365 private void sendAppWidgetResetNotify() {
366 final ContentResolver resolver = mContext.getContentResolver();
367 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
368 }
369
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800370 @Override
371 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800372 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700373
Adam Cohendcd297f2013-06-18 13:13:40 -0700374 mMaxItemId = 1;
375 mMaxScreenId = 0;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700376
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800377 db.execSQL("CREATE TABLE favorites (" +
378 "_id INTEGER PRIMARY KEY," +
379 "title TEXT," +
380 "intent TEXT," +
381 "container INTEGER," +
382 "screen INTEGER," +
383 "cellX INTEGER," +
384 "cellY INTEGER," +
385 "spanX INTEGER," +
386 "spanY INTEGER," +
387 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700388 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800389 "isShortcut INTEGER," +
390 "iconType INTEGER," +
391 "iconPackage TEXT," +
392 "iconResource TEXT," +
393 "icon BLOB," +
394 "uri TEXT," +
Chris Wrend5e66bf2013-09-16 14:02:29 -0400395 "displayMode INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400396 "appWidgetProvider TEXT," +
397 "modified INTEGER NOT NULL DEFAULT 0" +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800398 ");");
Adam Cohendcd297f2013-06-18 13:13:40 -0700399 addWorkspacesTable(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800400
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700401 // Database was just created, so wipe any previous widgets
402 if (mAppWidgetHost != null) {
403 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700404 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800405 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700406
Adam Cohen6dbe0492013-12-02 17:00:14 -0800407 if (shouldImportLauncher2Database(mContext)) {
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500408 // Try converting the old database
409 ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
410 public void onRow(ContentValues values) {
411 int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
412 if (container == Favorites.CONTAINER_DESKTOP) {
413 int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
414 screen = (int) upgradeLauncherDb_permuteScreens(screen);
415 values.put(LauncherSettings.Favorites.SCREEN, screen);
416 }
417 }
418 };
419 Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
420 "/old_favorites?notify=true");
421 if (!convertDatabase(db, uri, permuteScreensCb, true)) {
422 // Try and upgrade from the Launcher2 db
423 uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
424 if (!convertDatabase(db, uri, permuteScreensCb, false)) {
425 // If we fail, then set a flag to load the default workspace
426 setFlagEmptyDbCreated();
427 return;
Winson Chungc763c4e2013-07-19 13:49:06 -0700428 }
429 }
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500430 // Right now, in non-default workspace cases, we want to run the final
431 // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
432 // set that flag too.
433 setFlagJustLoadedOldDb();
434 } else {
435 // Fresh and clean launcher DB.
436 mMaxItemId = initializeMaxItemId(db);
437 setFlagEmptyDbCreated();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800438 }
439 }
440
Adam Cohendcd297f2013-06-18 13:13:40 -0700441 private void addWorkspacesTable(SQLiteDatabase db) {
442 db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
443 LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400444 LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
445 LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
Adam Cohendcd297f2013-06-18 13:13:40 -0700446 ");");
447 }
448
Winson Chungc763c4e2013-07-19 13:49:06 -0700449 private void setFlagJustLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400450 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700451 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
452 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700453 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
454 editor.putBoolean(EMPTY_DATABASE_CREATED, false);
Michael Jurkab85f8a42012-04-25 15:48:32 -0700455 editor.commit();
456 }
457
Winson Chungc763c4e2013-07-19 13:49:06 -0700458 private void setFlagEmptyDbCreated() {
459 String spKey = LauncherAppState.getSharedPreferencesKey();
460 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
461 SharedPreferences.Editor editor = sp.edit();
462 editor.putBoolean(EMPTY_DATABASE_CREATED, true);
463 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
464 editor.commit();
465 }
466
467 // We rearrange the screens from the old launcher
468 // 12345 -> 34512
469 private long upgradeLauncherDb_permuteScreens(long screen) {
470 if (screen >= 2) {
471 return screen - 2;
472 } else {
473 return screen + 3;
474 }
475 }
476
477 private boolean convertDatabase(SQLiteDatabase db, Uri uri,
478 ContentValuesCallback cb, boolean deleteRows) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800479 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800480 boolean converted = false;
481
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800482 final ContentResolver resolver = mContext.getContentResolver();
483 Cursor cursor = null;
484
485 try {
486 cursor = resolver.query(uri, null, null, null, null);
487 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700488 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800489 }
490
491 // We already have a favorites database in the old provider
Winson Chungc763c4e2013-07-19 13:49:06 -0700492 if (cursor != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800493 try {
Winson Chungc763c4e2013-07-19 13:49:06 -0700494 if (cursor.getCount() > 0) {
495 converted = copyFromCursor(db, cursor, cb) > 0;
496 if (converted && deleteRows) {
497 resolver.delete(uri, null, null);
498 }
499 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800500 } finally {
501 cursor.close();
502 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800503 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700504
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800505 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700506 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800507 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800508 convertWidgets(db);
Winson Chungc763c4e2013-07-19 13:49:06 -0700509
510 // Update max item id
511 mMaxItemId = initializeMaxItemId(db);
512 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800513 }
514
515 return converted;
516 }
517
Winson Chungc763c4e2013-07-19 13:49:06 -0700518 private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) {
Romain Guy73b979d2009-06-09 12:57:21 -0700519 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800520 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
521 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
522 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
523 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
524 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
525 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
526 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
527 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
528 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
529 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
530 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
531 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
532 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
533
534 ContentValues[] rows = new ContentValues[c.getCount()];
535 int i = 0;
536 while (c.moveToNext()) {
537 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700538 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800539 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
540 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
541 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
542 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
543 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
544 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
545 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
546 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700547 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800548 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
549 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
550 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
551 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
552 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
Winson Chungc763c4e2013-07-19 13:49:06 -0700553 if (cb != null) {
554 cb.onRow(values);
555 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800556 rows[i++] = values;
557 }
558
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800559 int total = 0;
Winson Chungc763c4e2013-07-19 13:49:06 -0700560 if (i > 0) {
561 db.beginTransaction();
562 try {
563 int numValues = rows.length;
564 for (i = 0; i < numValues; i++) {
565 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
566 return 0;
567 } else {
568 total++;
569 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800570 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700571 db.setTransactionSuccessful();
572 } finally {
573 db.endTransaction();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800574 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800575 }
576
577 return total;
578 }
579
580 @Override
581 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Winson Chungc763c4e2013-07-19 13:49:06 -0700582 if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700583
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800584 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700585 if (version < 3) {
586 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800587 db.beginTransaction();
588 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700589 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800590 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700591 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800592 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700593 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800594 } catch (SQLException ex) {
595 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800596 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800597 } finally {
598 db.endTransaction();
599 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700600
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800601 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700602 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800603 convertWidgets(db);
604 }
605 }
Romain Guy73b979d2009-06-09 12:57:21 -0700606
607 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800608 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700609 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700610
Romain Guy509cd6a2010-03-23 15:10:56 -0700611 // Where's version 5?
612 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
Daniel Sandler325dc232013-06-05 22:57:57 -0400613 // - Passion shipped on 2.1 with version 6 of launcher3
Romain Guy509cd6a2010-03-23 15:10:56 -0700614 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
615 // but version 5 on there was the updateContactsShortcuts change
616 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
617 // The updateContactsShortcuts change is idempotent, so running it twice
618 // is okay so we'll do that when upgrading the devices that shipped with it.
619 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800620 // We went from 3 to 5 screens. Move everything 1 to the right
621 db.beginTransaction();
622 try {
623 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
624 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800625 } catch (SQLException ex) {
626 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800627 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800628 } finally {
629 db.endTransaction();
630 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700631
Romain Guy509cd6a2010-03-23 15:10:56 -0700632 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800633 if (updateContactsShortcuts(db)) {
634 version = 6;
635 }
636 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000637
638 if (version < 7) {
639 // Version 7 gets rid of the special search widget.
640 convertWidgets(db);
641 version = 7;
642 }
643
Joe Onorato0589f0f2010-02-08 13:44:00 -0800644 if (version < 8) {
645 // Version 8 (froyo) has the icons all normalized. This should
646 // already be the case in practice, but we now rely on it and don't
647 // resample the images each time.
648 normalizeIcons(db);
649 version = 8;
650 }
651
Winson Chung3d503fb2011-07-13 17:25:49 -0700652 if (version < 9) {
653 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
654 // 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 -0700655 if (mMaxItemId == -1) {
656 mMaxItemId = initializeMaxItemId(db);
Winson Chung3d503fb2011-07-13 17:25:49 -0700657 }
658
659 // Add default hotseat icons
Winson Chung6d092682011-11-16 18:43:26 -0800660 loadFavorites(db, R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700661 version = 9;
662 }
663
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700664 // We bumped the version three time during JB, once to update the launch flags, once to
665 // update the override for the default launch animation and once to set the mimetype
666 // to improve startup performance
667 if (version < 12) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700668 // Contact shortcuts need a different set of flags to be launched now
669 // The updateContactsShortcuts change is idempotent, so we can keep using it like
670 // back in the Donut days
671 updateContactsShortcuts(db);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700672 version = 12;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700673 }
674
Adam Cohendcd297f2013-06-18 13:13:40 -0700675 if (version < 13) {
676 // With the new shrink-wrapped and re-orderable workspaces, it makes sense
677 // to persist workspace screens and their relative order.
678 mMaxScreenId = 0;
679
680 // This will never happen in the wild, but when we switch to using workspace
681 // screen ids, redo the import from old launcher.
Winson Chungc763c4e2013-07-19 13:49:06 -0700682 sJustLoadedFromOldDb = true;
Adam Cohendcd297f2013-06-18 13:13:40 -0700683
684 addWorkspacesTable(db);
685 version = 13;
686 }
687
Chris Wrend5e66bf2013-09-16 14:02:29 -0400688 if (version < 14) {
689 db.beginTransaction();
690 try {
691 // Insert new column for holding widget provider name
692 db.execSQL("ALTER TABLE favorites " +
693 "ADD COLUMN appWidgetProvider TEXT;");
694 db.setTransactionSuccessful();
695 version = 14;
696 } catch (SQLException ex) {
697 // Old version remains, which means we wipe old data
698 Log.e(TAG, ex.getMessage(), ex);
699 } finally {
700 db.endTransaction();
701 }
702 }
703
Chris Wren1ada10d2013-09-13 18:01:38 -0400704
705 if (version < 15) {
706 db.beginTransaction();
707 try {
708 // Insert new column for holding update timestamp
709 db.execSQL("ALTER TABLE favorites " +
710 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
711 db.execSQL("ALTER TABLE workspaceScreens " +
712 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
713 db.setTransactionSuccessful();
714 version = 15;
715 } catch (SQLException ex) {
716 // Old version remains, which means we wipe old data
717 Log.e(TAG, ex.getMessage(), ex);
718 } finally {
719 db.endTransaction();
720 }
721 }
722
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800723 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800724 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800725 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
Adam Cohendcd297f2013-06-18 13:13:40 -0700726 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
727
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800728 onCreate(db);
729 }
730 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800731
732 private boolean updateContactsShortcuts(SQLiteDatabase db) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800733 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
734 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
735
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700736 Cursor c = null;
737 final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800738 db.beginTransaction();
739 try {
740 // Select and iterate through each matching widget
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700741 c = db.query(TABLE_FAVORITES,
742 new String[] { Favorites._ID, Favorites.INTENT },
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800743 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700744 if (c == null) return false;
745
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800746 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700747
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800748 final int idIndex = c.getColumnIndex(Favorites._ID);
749 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700750
751 while (c.moveToNext()) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800752 long favoriteId = c.getLong(idIndex);
753 final String intentUri = c.getString(intentIndex);
754 if (intentUri != null) {
755 try {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700756 final Intent intent = Intent.parseUri(intentUri, 0);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800757 android.util.Log.d("Home", intent.toString());
758 final Uri uri = intent.getData();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700759 if (uri != null) {
760 final String data = uri.toString();
761 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
762 actionQuickContact.equals(intent.getAction())) &&
763 (data.startsWith("content://contacts/people/") ||
764 data.startsWith("content://com.android.contacts/" +
765 "contacts/lookup/"))) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800766
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700767 final Intent newIntent = new Intent(actionQuickContact);
768 // When starting from the launcher, start in a new, cleared task
769 // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
770 // clear the whole thing preemptively here since
771 // QuickContactActivity will finish itself when launching other
772 // detail activities.
773 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
774 Intent.FLAG_ACTIVITY_CLEAR_TASK);
Winson Chung2672ff92012-05-04 16:22:30 -0700775 newIntent.putExtra(
776 Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700777 newIntent.setData(uri);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700778 // Determine the type and also put that in the shortcut
779 // (that can speed up launch a bit)
780 newIntent.setDataAndType(uri, newIntent.resolveType(mContext));
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800781
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700782 final ContentValues values = new ContentValues();
783 values.put(LauncherSettings.Favorites.INTENT,
784 newIntent.toUri(0));
785
786 String updateWhere = Favorites._ID + "=" + favoriteId;
787 db.update(TABLE_FAVORITES, values, updateWhere, null);
788 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800789 }
790 } catch (RuntimeException ex) {
791 Log.e(TAG, "Problem upgrading shortcut", ex);
792 } catch (URISyntaxException e) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700793 Log.e(TAG, "Problem upgrading shortcut", e);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800794 }
795 }
796 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700797
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800798 db.setTransactionSuccessful();
799 } catch (SQLException ex) {
800 Log.w(TAG, "Problem while upgrading contacts", ex);
801 return false;
802 } finally {
803 db.endTransaction();
804 if (c != null) {
805 c.close();
806 }
807 }
808
809 return true;
810 }
811
Joe Onorato0589f0f2010-02-08 13:44:00 -0800812 private void normalizeIcons(SQLiteDatabase db) {
813 Log.d(TAG, "normalizing icons");
814
Joe Onorato346e1292010-02-18 10:34:24 -0500815 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800816 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400817 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800818 try {
819 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400820 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600821 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800822
823 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
824 Favorites.ICON_TYPE_BITMAP, null);
825
826 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
827 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
828
829 while (c.moveToNext()) {
830 long id = c.getLong(idIndex);
831 byte[] data = c.getBlob(iconIndex);
832 try {
833 Bitmap bitmap = Utilities.resampleIconBitmap(
834 BitmapFactory.decodeByteArray(data, 0, data.length),
835 mContext);
836 if (bitmap != null) {
837 update.bindLong(1, id);
838 data = ItemInfo.flattenBitmap(bitmap);
839 if (data != null) {
840 update.bindBlob(2, data);
841 update.execute();
842 }
843 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800844 }
845 } catch (Exception e) {
846 if (!logged) {
847 Log.e(TAG, "Failed normalizing icon " + id, e);
848 } else {
849 Log.e(TAG, "Also failed normalizing icon " + id);
850 }
851 logged = true;
852 }
853 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000854 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800855 } catch (SQLException ex) {
856 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
857 } finally {
858 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400859 if (update != null) {
860 update.close();
861 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800862 if (c != null) {
863 c.close();
864 }
865 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700866 }
867
868 // Generates a new ID to use for an object in your database. This method should be only
869 // called from the main UI thread. As an exception, we do call it when we call the
870 // constructor from the worker thread; however, this doesn't extend until after the
871 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
872 // after that point
Adam Cohendcd297f2013-06-18 13:13:40 -0700873 public long generateNewItemId() {
874 if (mMaxItemId < 0) {
875 throw new RuntimeException("Error: max item id was not initialized");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700876 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700877 mMaxItemId += 1;
878 return mMaxItemId;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700879 }
880
Winson Chungc763c4e2013-07-19 13:49:06 -0700881 public void updateMaxItemId(long id) {
882 mMaxItemId = id + 1;
883 }
884
Adam Cohendcd297f2013-06-18 13:13:40 -0700885 private long initializeMaxItemId(SQLiteDatabase db) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700886 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
887
888 // get the result
889 final int maxIdIndex = 0;
890 long id = -1;
891 if (c != null && c.moveToNext()) {
892 id = c.getLong(maxIdIndex);
893 }
Michael Jurka5130e402011-10-13 04:55:35 -0700894 if (c != null) {
895 c.close();
896 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700897
898 if (id == -1) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700899 throw new RuntimeException("Error: could not query max item id");
900 }
901
902 return id;
903 }
904
905 // Generates a new ID to use for an workspace screen in your database. This method
906 // should be only called from the main UI thread. As an exception, we do call it when we
907 // call the constructor from the worker thread; however, this doesn't extend until after the
908 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
909 // after that point
910 public long generateNewScreenId() {
911 if (mMaxScreenId < 0) {
912 throw new RuntimeException("Error: max screen id was not initialized");
913 }
914 mMaxScreenId += 1;
Winson Chunga90303b2013-11-15 13:05:06 -0800915 // Log to disk
916 Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700917 return mMaxScreenId;
918 }
919
920 public void updateMaxScreenId(long maxScreenId) {
Winson Chunga90303b2013-11-15 13:05:06 -0800921 // Log to disk
922 Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700923 mMaxScreenId = maxScreenId;
924 }
925
926 private long initializeMaxScreenId(SQLiteDatabase db) {
927 Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
928
929 // get the result
930 final int maxIdIndex = 0;
931 long id = -1;
932 if (c != null && c.moveToNext()) {
933 id = c.getLong(maxIdIndex);
934 }
935 if (c != null) {
936 c.close();
937 }
938
939 if (id == -1) {
940 throw new RuntimeException("Error: could not query max screen id");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700941 }
942
Winson Chunga90303b2013-11-15 13:05:06 -0800943 // Log to disk
944 Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700945 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800946 }
947
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800948 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700949 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000950 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800951 */
952 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000953 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800954 final int[] bindSources = new int[] {
955 Favorites.ITEM_TYPE_WIDGET_CLOCK,
956 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000957 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800958 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000959
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800960 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700961
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800962 Cursor c = null;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700963
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800964 db.beginTransaction();
965 try {
966 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000967 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800968 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700969
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800970 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700971
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800972 final ContentValues values = new ContentValues();
973 while (c != null && c.moveToNext()) {
974 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000975 int favoriteType = c.getInt(1);
976
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700977 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800978 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700979 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700980
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800981 if (LOGD) {
982 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
983 + " for favoriteId=" + favoriteId);
984 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800985 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +0000986 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
987 values.put(Favorites.APPWIDGET_ID, appWidgetId);
988
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800989 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +0000990 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
991 values.put(LauncherSettings.Favorites.SPANX, 4);
992 values.put(LauncherSettings.Favorites.SPANY, 1);
993 } else {
994 values.put(LauncherSettings.Favorites.SPANX, 2);
995 values.put(LauncherSettings.Favorites.SPANY, 2);
996 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800997
998 String updateWhere = Favorites._ID + "=" + favoriteId;
999 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +00001000
Bjorn Bringert34251342009-12-15 13:33:11 +00001001 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001002 // TODO: check return value
1003 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001004 new ComponentName("com.android.alarmclock",
1005 "com.android.alarmclock.AnalogAppWidgetProvider"));
1006 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001007 // TODO: check return value
1008 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001009 new ComponentName("com.android.camera",
1010 "com.android.camera.PhotoAppWidgetProvider"));
1011 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001012 // TODO: check return value
1013 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001014 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +00001015 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001016 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001017 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001018 }
1019 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001020
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001021 db.setTransactionSuccessful();
1022 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001023 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001024 } finally {
1025 db.endTransaction();
1026 if (c != null) {
1027 c.close();
1028 }
1029 }
Winson Chungc763c4e2013-07-19 13:49:06 -07001030
1031 // Update max item id
1032 mMaxItemId = initializeMaxItemId(db);
1033 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001034 }
1035
Michael Jurka8b805b12012-04-18 14:23:14 -07001036 private static final void beginDocument(XmlPullParser parser, String firstElementName)
1037 throws XmlPullParserException, IOException {
1038 int type;
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001039 while ((type = parser.next()) != XmlPullParser.START_TAG
1040 && type != XmlPullParser.END_DOCUMENT) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001041 ;
1042 }
1043
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001044 if (type != XmlPullParser.START_TAG) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001045 throw new XmlPullParserException("No start tag found");
1046 }
1047
1048 if (!parser.getName().equals(firstElementName)) {
1049 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1050 ", expected " + firstElementName);
1051 }
1052 }
1053
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001054 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001055 * Loads the default set of favorite packages from an xml file.
1056 *
1057 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -07001058 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001059 */
Winson Chung6d092682011-11-16 18:43:26 -08001060 private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001061 Intent intent = new Intent(Intent.ACTION_MAIN, null);
1062 intent.addCategory(Intent.CATEGORY_LAUNCHER);
1063 ContentValues values = new ContentValues();
1064
Daniel Sandler57dac262013-10-03 13:28:36 -04001065 if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
1066
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001067 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001068 int i = 0;
1069 try {
Winson Chung6d092682011-11-16 18:43:26 -08001070 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001071 AttributeSet attrs = Xml.asAttributeSet(parser);
Michael Jurka8b805b12012-04-18 14:23:14 -07001072 beginDocument(parser, TAG_FAVORITES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001073
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001074 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001075
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001076 int type;
1077 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1078 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
1079
1080 if (type != XmlPullParser.START_TAG) {
1081 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001082 }
1083
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001084 boolean added = false;
1085 final String name = parser.getName();
1086
Daniel Sandler57dac262013-10-03 13:28:36 -04001087 if (TAG_INCLUDE.equals(name)) {
1088 final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Include);
1089
1090 final int resId = a.getResourceId(R.styleable.Include_workspace, 0);
1091
1092 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
1093 "", resId));
1094
1095 if (resId != 0 && resId != workspaceResourceId) {
1096 // recursively load some more favorites, why not?
1097 i += loadFavorites(db, resId);
1098 added = false;
Daniel Sandler57dac262013-10-03 13:28:36 -04001099 } else {
1100 Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
1101 }
1102
1103 a.recycle();
1104
1105 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
1106 continue;
1107 }
1108
1109 // Assuming it's a <favorite> at this point
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001110 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
1111
Winson Chung3d503fb2011-07-13 17:25:49 -07001112 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
1113 if (a.hasValue(R.styleable.Favorite_container)) {
1114 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
1115 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001116
Winson Chung6d092682011-11-16 18:43:26 -08001117 String screen = a.getString(R.styleable.Favorite_screen);
1118 String x = a.getString(R.styleable.Favorite_x);
1119 String y = a.getString(R.styleable.Favorite_y);
1120
Winson Chung6d092682011-11-16 18:43:26 -08001121 values.clear();
1122 values.put(LauncherSettings.Favorites.CONTAINER, container);
1123 values.put(LauncherSettings.Favorites.SCREEN, screen);
1124 values.put(LauncherSettings.Favorites.CELLX, x);
1125 values.put(LauncherSettings.Favorites.CELLY, y);
1126
Daniel Sandler57dac262013-10-03 13:28:36 -04001127 if (LOGD) {
1128 final String title = a.getString(R.styleable.Favorite_title);
1129 final String pkg = a.getString(R.styleable.Favorite_packageName);
1130 final String something = title != null ? title : pkg;
1131 Log.v(TAG, String.format(
1132 ("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
1133 "", name,
1134 (something == null ? "" : (" \"" + something + "\"")),
1135 container, screen, x, y));
1136 }
1137
Winson Chung6d092682011-11-16 18:43:26 -08001138 if (TAG_FAVORITE.equals(name)) {
1139 long id = addAppShortcut(db, values, a, packageManager, intent);
1140 added = id >= 0;
1141 } else if (TAG_SEARCH.equals(name)) {
1142 added = addSearchWidget(db, values);
1143 } else if (TAG_CLOCK.equals(name)) {
1144 added = addClockWidget(db, values);
1145 } else if (TAG_APPWIDGET.equals(name)) {
Winson Chungb3302ae2012-05-01 10:19:14 -07001146 added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
Winson Chung6d092682011-11-16 18:43:26 -08001147 } else if (TAG_SHORTCUT.equals(name)) {
1148 long id = addUriShortcut(db, values, a);
1149 added = id >= 0;
1150 } else if (TAG_FOLDER.equals(name)) {
1151 String title;
1152 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
1153 if (titleResId != -1) {
1154 title = mContext.getResources().getString(titleResId);
1155 } else {
1156 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -07001157 }
Winson Chung6d092682011-11-16 18:43:26 -08001158 values.put(LauncherSettings.Favorites.TITLE, title);
1159 long folderId = addFolder(db, values);
1160 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -07001161
Winson Chung6d092682011-11-16 18:43:26 -08001162 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -07001163
Winson Chung6d092682011-11-16 18:43:26 -08001164 int folderDepth = parser.getDepth();
1165 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1166 parser.getDepth() > folderDepth) {
1167 if (type != XmlPullParser.START_TAG) {
1168 continue;
1169 }
1170 final String folder_item_name = parser.getName();
1171
1172 TypedArray ar = mContext.obtainStyledAttributes(attrs,
1173 R.styleable.Favorite);
1174 values.clear();
1175 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
1176
Daniel Sandler57dac262013-10-03 13:28:36 -04001177 if (LOGD) {
1178 final String pkg = ar.getString(R.styleable.Favorite_packageName);
1179 final String uri = ar.getString(R.styleable.Favorite_uri);
1180 Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
1181 folder_item_name, uri != null ? uri : pkg));
1182 }
1183
Winson Chung6d092682011-11-16 18:43:26 -08001184 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
1185 long id =
1186 addAppShortcut(db, values, ar, packageManager, intent);
1187 if (id >= 0) {
1188 folderItems.add(id);
1189 }
1190 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
1191 long id = addUriShortcut(db, values, ar);
1192 if (id >= 0) {
1193 folderItems.add(id);
1194 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001195 } else {
Winson Chung6d092682011-11-16 18:43:26 -08001196 throw new RuntimeException("Folders can " +
1197 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -07001198 }
Winson Chung6d092682011-11-16 18:43:26 -08001199 ar.recycle();
1200 }
1201 // We can only have folders with >= 2 items, so we need to remove the
1202 // folder and clean up if less than 2 items were included, or some
1203 // failed to add, and less than 2 were actually added
1204 if (folderItems.size() < 2 && folderId >= 0) {
1205 // We just delete the folder and any items that made it
1206 deleteId(db, folderId);
1207 if (folderItems.size() > 0) {
1208 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -07001209 }
Winson Chung6d092682011-11-16 18:43:26 -08001210 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -07001211 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001212 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001213 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001214 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001215 }
1216 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001217 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001218 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001219 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -07001220 } catch (RuntimeException e) {
1221 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001222 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001223
Winson Chungc763c4e2013-07-19 13:49:06 -07001224 // Update the max item id after we have loaded the database
1225 if (mMaxItemId == -1) {
1226 mMaxItemId = initializeMaxItemId(db);
1227 }
1228
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001229 return i;
1230 }
1231
Adam Cohen228da5a2011-07-27 22:23:47 -07001232 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001233 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -07001234 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001235 ActivityInfo info;
1236 String packageName = a.getString(R.styleable.Favorite_packageName);
1237 String className = a.getString(R.styleable.Favorite_className);
1238 try {
Romain Guy693599f2010-03-23 10:58:18 -07001239 ComponentName cn;
1240 try {
1241 cn = new ComponentName(packageName, className);
1242 info = packageManager.getActivityInfo(cn, 0);
1243 } catch (PackageManager.NameNotFoundException nnfe) {
1244 String[] packages = packageManager.currentToCanonicalPackageNames(
1245 new String[] { packageName });
1246 cn = new ComponentName(packages[0], className);
1247 info = packageManager.getActivityInfo(cn, 0);
1248 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001249 id = generateNewItemId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001250 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -07001251 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1252 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -07001253 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001254 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
1255 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
1256 values.put(Favorites.SPANX, 1);
1257 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001258 values.put(Favorites._ID, generateNewItemId());
Adam Cohen228da5a2011-07-27 22:23:47 -07001259 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1260 return -1;
1261 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001262 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001263 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001264 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001265 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001266 return id;
1267 }
1268
1269 private long addFolder(SQLiteDatabase db, ContentValues values) {
1270 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
1271 values.put(Favorites.SPANX, 1);
1272 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001273 long id = generateNewItemId();
Adam Cohen228da5a2011-07-27 22:23:47 -07001274 values.put(Favorites._ID, id);
1275 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
1276 return -1;
1277 } else {
1278 return id;
1279 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001280 }
1281
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001282 private ComponentName getSearchWidgetProvider() {
1283 SearchManager searchManager =
1284 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
1285 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
1286 if (searchComponent == null) return null;
1287 return getProviderInPackage(searchComponent.getPackageName());
1288 }
1289
1290 /**
1291 * Gets an appwidget provider from the given package. If the package contains more than
1292 * one appwidget provider, an arbitrary one is returned.
1293 */
1294 private ComponentName getProviderInPackage(String packageName) {
1295 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1296 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
1297 if (providers == null) return null;
1298 final int providerCount = providers.size();
1299 for (int i = 0; i < providerCount; i++) {
1300 ComponentName provider = providers.get(i).provider;
1301 if (provider != null && provider.getPackageName().equals(packageName)) {
1302 return provider;
1303 }
1304 }
1305 return null;
1306 }
1307
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001308 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001309 ComponentName cn = getSearchWidgetProvider();
Winson Chungb3302ae2012-05-01 10:19:14 -07001310 return addAppWidget(db, values, cn, 4, 1, null);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001311 }
1312
1313 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +00001314 ComponentName cn = new ComponentName("com.android.alarmclock",
1315 "com.android.alarmclock.AnalogAppWidgetProvider");
Winson Chungb3302ae2012-05-01 10:19:14 -07001316 return addAppWidget(db, values, cn, 2, 2, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001317 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001318
Winson Chungb3302ae2012-05-01 10:19:14 -07001319 private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
1320 SQLiteDatabase db, ContentValues values, TypedArray a,
1321 PackageManager packageManager) throws XmlPullParserException, IOException {
Romain Guy693599f2010-03-23 10:58:18 -07001322
Mike Cleronb87bd162009-10-30 16:36:56 -07001323 String packageName = a.getString(R.styleable.Favorite_packageName);
1324 String className = a.getString(R.styleable.Favorite_className);
1325
1326 if (packageName == null || className == null) {
1327 return false;
1328 }
Romain Guy693599f2010-03-23 10:58:18 -07001329
1330 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -07001331 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -07001332 try {
1333 packageManager.getReceiverInfo(cn, 0);
1334 } catch (Exception e) {
1335 String[] packages = packageManager.currentToCanonicalPackageNames(
1336 new String[] { packageName });
1337 cn = new ComponentName(packages[0], className);
1338 try {
1339 packageManager.getReceiverInfo(cn, 0);
1340 } catch (Exception e1) {
1341 hasPackage = false;
1342 }
1343 }
1344
1345 if (hasPackage) {
1346 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
1347 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
Winson Chungb3302ae2012-05-01 10:19:14 -07001348
1349 // Read the extras
1350 Bundle extras = new Bundle();
1351 int widgetDepth = parser.getDepth();
1352 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1353 parser.getDepth() > widgetDepth) {
1354 if (type != XmlPullParser.START_TAG) {
1355 continue;
1356 }
1357
1358 TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
1359 if (TAG_EXTRA.equals(parser.getName())) {
1360 String key = ar.getString(R.styleable.Extra_key);
1361 String value = ar.getString(R.styleable.Extra_value);
1362 if (key != null && value != null) {
1363 extras.putString(key, value);
1364 } else {
1365 throw new RuntimeException("Widget extras must have a key and value");
1366 }
1367 } else {
1368 throw new RuntimeException("Widgets can contain only extras");
1369 }
1370 ar.recycle();
1371 }
1372
1373 return addAppWidget(db, values, cn, spanX, spanY, extras);
Romain Guy693599f2010-03-23 10:58:18 -07001374 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001375
Romain Guy693599f2010-03-23 10:58:18 -07001376 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +00001377 }
Bjorn Bringert7984c942009-12-09 15:38:25 +00001378 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
Winson Chungb3302ae2012-05-01 10:19:14 -07001379 int spanX, int spanY, Bundle extras) {
Mike Cleronb87bd162009-10-30 16:36:56 -07001380 boolean allocatedAppWidgets = false;
1381 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1382
1383 try {
1384 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001385
Mike Cleronb87bd162009-10-30 16:36:56 -07001386 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +00001387 values.put(Favorites.SPANX, spanX);
1388 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -07001389 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Chris Wrend5e66bf2013-09-16 14:02:29 -04001390 values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
Adam Cohendcd297f2013-06-18 13:13:40 -07001391 values.put(Favorites._ID, generateNewItemId());
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001392 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -07001393
1394 allocatedAppWidgets = true;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001395
Michael Jurka8b805b12012-04-18 14:23:14 -07001396 // TODO: need to check return value
1397 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
Winson Chungb3302ae2012-05-01 10:19:14 -07001398
1399 // Send a broadcast to configure the widget
1400 if (extras != null && !extras.isEmpty()) {
1401 Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
1402 intent.setComponent(cn);
1403 intent.putExtras(extras);
1404 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1405 mContext.sendBroadcast(intent);
1406 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001407 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001408 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -07001409 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001410
Mike Cleronb87bd162009-10-30 16:36:56 -07001411 return allocatedAppWidgets;
1412 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001413
1414 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -07001415 TypedArray a) {
1416 Resources r = mContext.getResources();
1417
1418 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
1419 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
1420
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001421 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -07001422 String uri = null;
1423 try {
1424 uri = a.getString(R.styleable.Favorite_uri);
1425 intent = Intent.parseUri(uri, 0);
1426 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001427 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -07001428 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -07001429 }
1430
1431 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001432 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001433 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001434 }
1435
Adam Cohendcd297f2013-06-18 13:13:40 -07001436 long id = generateNewItemId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001437 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1438 values.put(Favorites.INTENT, intent.toUri(0));
1439 values.put(Favorites.TITLE, r.getString(titleResId));
1440 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1441 values.put(Favorites.SPANX, 1);
1442 values.put(Favorites.SPANY, 1);
1443 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1444 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1445 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001446 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001447
Adam Cohen228da5a2011-07-27 22:23:47 -07001448 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1449 return -1;
1450 }
1451 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001452 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001453
1454 public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
1455 final ContentResolver resolver = mContext.getContentResolver();
1456 Cursor c = null;
1457 int count = 0;
1458 int curScreen = 0;
1459
1460 try {
1461 c = resolver.query(uri, null, null, null, "title ASC");
1462 } catch (Exception e) {
1463 // Ignore
1464 }
1465
1466
1467 // We already have a favorites database in the old provider
1468 if (c != null) {
1469 try {
1470 if (c.getCount() > 0) {
1471 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1472 final int intentIndex
1473 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1474 final int titleIndex
1475 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1476 final int iconTypeIndex
1477 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
1478 final int iconIndex
1479 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1480 final int iconPackageIndex
1481 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
1482 final int iconResourceIndex
1483 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
1484 final int containerIndex
1485 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1486 final int itemTypeIndex
1487 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1488 final int screenIndex
1489 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1490 final int cellXIndex
1491 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1492 final int cellYIndex
1493 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1494 final int uriIndex
1495 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1496 final int displayModeIndex
1497 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
1498
1499 int i = 0;
1500 int curX = 0;
1501 int curY = 0;
1502
1503 final LauncherAppState app = LauncherAppState.getInstance();
1504 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1505 final int width = (int) grid.numColumns;
1506 final int height = (int) grid.numRows;
1507 final int hotseatWidth = (int) grid.numHotseatIcons;
1508
1509 final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
1510
1511 final ContentValues[] rows = new ContentValues[c.getCount()];
1512
1513 while (c.moveToNext()) {
1514 final int itemType = c.getInt(itemTypeIndex);
1515 if (itemType != Favorites.ITEM_TYPE_APPLICATION
1516 && itemType != Favorites.ITEM_TYPE_SHORTCUT
1517 && itemType != Favorites.ITEM_TYPE_FOLDER) {
1518 continue;
1519 }
1520
1521 final int cellX = c.getInt(cellXIndex);
1522 final int cellY = c.getInt(cellYIndex);
1523 final int screen = c.getInt(screenIndex);
1524 int container = c.getInt(containerIndex);
1525 final String intentStr = c.getString(intentIndex);
1526 Launcher.addDumpLog(TAG, "migrating \""
1527 + c.getString(titleIndex) + "\": " + intentStr, true);
1528
1529 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
1530 if (TextUtils.isEmpty(intentStr)) {
1531 // no intent? no icon
1532 Launcher.addDumpLog(TAG, "skipping empty intent", true);
1533 continue;
1534 } else {
1535 try {
1536 // Canonicalize
1537 final Intent intent = Intent.parseUri(intentStr, 0);
1538 // the Play Store sets the package parameter, but Launcher
1539 // does not, so we clear that out to keep them the same
1540 intent.setPackage(null);
1541 final String key = intent.toUri(0);
1542 if (seenIntents.contains(key)) {
1543 Launcher.addDumpLog(TAG, "skipping duplicate", true);
1544 continue;
1545 } else {
1546 seenIntents.add(key);
1547 }
1548 } catch (URISyntaxException e) {
1549 // bogus intent?
1550 Launcher.addDumpLog(TAG,
1551 "skipping invalid intent uri", true);
1552 continue;
1553 }
1554 }
1555 }
1556
1557 ContentValues values = new ContentValues(c.getColumnCount());
1558 values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
1559 values.put(LauncherSettings.Favorites.INTENT, intentStr);
1560 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
1561 values.put(LauncherSettings.Favorites.ICON_TYPE,
1562 c.getInt(iconTypeIndex));
1563 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
1564 values.put(LauncherSettings.Favorites.ICON_PACKAGE,
1565 c.getString(iconPackageIndex));
1566 values.put(LauncherSettings.Favorites.ICON_RESOURCE,
1567 c.getString(iconResourceIndex));
1568 values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
1569 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
1570 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
1571 values.put(LauncherSettings.Favorites.DISPLAY_MODE,
1572 c.getInt(displayModeIndex));
1573
1574 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
1575 && screen >= hotseatWidth) {
1576 // no room for you in the hotseat? it's off to the desktop with you
1577 container = Favorites.CONTAINER_DESKTOP;
1578 }
1579
1580 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1581 // In a folder or in the hotseat, preserve position
1582 values.put(LauncherSettings.Favorites.SCREEN, screen);
1583 values.put(LauncherSettings.Favorites.CELLX, cellX);
1584 values.put(LauncherSettings.Favorites.CELLY, cellY);
1585 } else {
1586 values.put(LauncherSettings.Favorites.SCREEN, curScreen);
1587 values.put(LauncherSettings.Favorites.CELLX, curX);
1588 values.put(LauncherSettings.Favorites.CELLY, curY);
1589 curX = (curX + 1) % width;
1590 if (curX == 0) {
1591 curY = (curY + 1);
1592 }
1593 // Leave the last row of icons blank on screen 0
1594 if (curScreen == 0 && curY == height - 1 || curY == height) {
1595 curScreen = (int) generateNewScreenId();
1596 curY = 0;
1597 }
1598 }
1599
1600 values.put(LauncherSettings.Favorites.CONTAINER, container);
1601
1602 rows[i++] = values;
1603 }
1604
1605 if (i > 0) {
1606 db.beginTransaction();
1607 try {
1608 final int N = rows.length;
1609 for (i = 0; i < N; i++) {
1610 if (rows[i] == null) continue;
1611 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i])
1612 < 0) {
1613 return;
1614 } else {
1615 count++;
1616 }
1617 }
1618 db.setTransactionSuccessful();
1619 } finally {
1620 db.endTransaction();
1621 }
1622 }
1623
1624 db.beginTransaction();
1625 try {
1626 for (i=0; i<=curScreen; i++) {
1627 final ContentValues values = new ContentValues();
1628 values.put(LauncherSettings.WorkspaceScreens._ID, i);
1629 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1630 if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
1631 < 0) {
1632 return;
1633 }
1634 }
1635 db.setTransactionSuccessful();
1636 } finally {
1637 db.endTransaction();
1638 }
1639 }
1640 } finally {
1641 c.close();
1642 }
1643 }
1644
1645 Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
1646 + (curScreen+1) + " screens", true);
1647
1648 // ensure that new screens are created to hold these icons
1649 setFlagJustLoadedOldDb();
1650
1651 // Update max IDs; very important since we just grabbed IDs from another database
1652 mMaxItemId = initializeMaxItemId(db);
1653 mMaxScreenId = initializeMaxScreenId(db);
1654 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
1655 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001656 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001657
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001658 /**
1659 * Build a query string that will match any row where the column matches
1660 * anything in the values list.
1661 */
1662 static String buildOrWhereString(String column, int[] values) {
1663 StringBuilder selectWhere = new StringBuilder();
1664 for (int i = values.length - 1; i >= 0; i--) {
1665 selectWhere.append(column).append("=").append(values[i]);
1666 if (i > 0) {
1667 selectWhere.append(" OR ");
1668 }
1669 }
1670 return selectWhere.toString();
1671 }
1672
1673 static class SqlArguments {
1674 public final String table;
1675 public final String where;
1676 public final String[] args;
1677
1678 SqlArguments(Uri url, String where, String[] args) {
1679 if (url.getPathSegments().size() == 1) {
1680 this.table = url.getPathSegments().get(0);
1681 this.where = where;
1682 this.args = args;
1683 } else if (url.getPathSegments().size() != 2) {
1684 throw new IllegalArgumentException("Invalid URI: " + url);
1685 } else if (!TextUtils.isEmpty(where)) {
1686 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1687 } else {
1688 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001689 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001690 this.args = null;
1691 }
1692 }
1693
1694 SqlArguments(Uri url) {
1695 if (url.getPathSegments().size() == 1) {
1696 table = url.getPathSegments().get(0);
1697 where = null;
1698 args = null;
1699 } else {
1700 throw new IllegalArgumentException("Invalid URI: " + url);
1701 }
1702 }
1703 }
1704}