blob: 52bcf98e7c893bbba116c9513232b595af676efd [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
Winson Chung0b560dd2014-01-21 13:00:26 -0800111 public boolean wasNewDbCreated() {
112 return mOpenHelper.wasNewDbCreated();
113 }
114
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800115 @Override
116 public String getType(Uri uri) {
117 SqlArguments args = new SqlArguments(uri, null, null);
118 if (TextUtils.isEmpty(args.where)) {
119 return "vnd.android.cursor.dir/" + args.table;
120 } else {
121 return "vnd.android.cursor.item/" + args.table;
122 }
123 }
124
125 @Override
126 public Cursor query(Uri uri, String[] projection, String selection,
127 String[] selectionArgs, String sortOrder) {
128
129 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
130 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
131 qb.setTables(args.table);
132
Romain Guy73b979d2009-06-09 12:57:21 -0700133 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800134 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
135 result.setNotificationUri(getContext().getContentResolver(), uri);
136
137 return result;
138 }
139
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700140 private static long dbInsertAndCheck(DatabaseHelper helper,
141 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
Dan Sandlerd5024042014-01-09 15:01:33 -0500142 if (values == null) {
143 throw new RuntimeException("Error: attempting to insert null values");
144 }
Chris Wren5dee7af2013-12-20 17:22:11 -0500145 if (!values.containsKey(LauncherSettings.BaseLauncherColumns._ID)) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700146 throw new RuntimeException("Error: attempting to add item without specifying an id");
147 }
Chris Wren5dee7af2013-12-20 17:22:11 -0500148 helper.checkId(table, values);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700149 return db.insert(table, nullColumnHack, values);
150 }
151
Adam Cohen228da5a2011-07-27 22:23:47 -0700152 private static void deleteId(SQLiteDatabase db, long id) {
153 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
154 SqlArguments args = new SqlArguments(uri, null, null);
155 db.delete(args.table, args.where, args.args);
156 }
157
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158 @Override
159 public Uri insert(Uri uri, ContentValues initialValues) {
160 SqlArguments args = new SqlArguments(uri);
161
162 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Chris Wren1ada10d2013-09-13 18:01:38 -0400163 addModifiedTime(initialValues);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700164 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800165 if (rowId <= 0) return null;
166
167 uri = ContentUris.withAppendedId(uri, rowId);
168 sendNotify(uri);
169
170 return uri;
171 }
172
173 @Override
174 public int bulkInsert(Uri uri, ContentValues[] values) {
175 SqlArguments args = new SqlArguments(uri);
176
177 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
178 db.beginTransaction();
179 try {
180 int numValues = values.length;
181 for (int i = 0; i < numValues; i++) {
Chris Wren1ada10d2013-09-13 18:01:38 -0400182 addModifiedTime(values[i]);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700183 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
184 return 0;
185 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800186 }
187 db.setTransactionSuccessful();
188 } finally {
189 db.endTransaction();
190 }
191
192 sendNotify(uri);
193 return values.length;
194 }
195
196 @Override
197 public int delete(Uri uri, String selection, String[] selectionArgs) {
198 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
199
200 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
201 int count = db.delete(args.table, args.where, args.args);
202 if (count > 0) sendNotify(uri);
203
204 return count;
205 }
206
207 @Override
208 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
209 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
210
Chris Wren1ada10d2013-09-13 18:01:38 -0400211 addModifiedTime(values);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
213 int count = db.update(args.table, values, args.where, args.args);
214 if (count > 0) sendNotify(uri);
215
216 return count;
217 }
218
219 private void sendNotify(Uri uri) {
220 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
221 if (notify == null || "true".equals(notify)) {
222 getContext().getContentResolver().notifyChange(uri, null);
223 }
Chris Wren1ada10d2013-09-13 18:01:38 -0400224
225 // always notify the backup agent
Chris Wren92aa4232013-10-04 11:29:36 -0400226 LauncherBackupAgentHelper.dataChanged(getContext());
Chris Wren1ada10d2013-09-13 18:01:38 -0400227 }
228
229 private void addModifiedTime(ContentValues values) {
230 values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800231 }
232
Adam Cohendcd297f2013-06-18 13:13:40 -0700233 public long generateNewItemId() {
234 return mOpenHelper.generateNewItemId();
235 }
236
Winson Chungc763c4e2013-07-19 13:49:06 -0700237 public void updateMaxItemId(long id) {
238 mOpenHelper.updateMaxItemId(id);
239 }
240
Adam Cohendcd297f2013-06-18 13:13:40 -0700241 public long generateNewScreenId() {
242 return mOpenHelper.generateNewScreenId();
243 }
244
245 // This is only required one time while loading the workspace during the
246 // upgrade path, and should never be called from anywhere else.
247 public void updateMaxScreenId(long maxScreenId) {
248 mOpenHelper.updateMaxScreenId(maxScreenId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700249 }
250
Brian Muramatsu5524b492012-10-02 16:55:54 -0700251 /**
Adam Cohene25af792013-06-06 23:08:25 -0700252 * @param Should we load the old db for upgrade? first run only.
253 */
Winson Chungc763c4e2013-07-19 13:49:06 -0700254 synchronized public boolean justLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400255 String spKey = LauncherAppState.getSharedPreferencesKey();
Adam Cohene25af792013-06-06 23:08:25 -0700256 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
257
Winson Chungc763c4e2013-07-19 13:49:06 -0700258 boolean loadedOldDb = false || sJustLoadedFromOldDb;
Adam Cohendcd297f2013-06-18 13:13:40 -0700259
Winson Chungc763c4e2013-07-19 13:49:06 -0700260 sJustLoadedFromOldDb = false;
261 if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) {
Adam Cohene25af792013-06-06 23:08:25 -0700262
263 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700264 editor.remove(UPGRADED_FROM_OLD_DATABASE);
Adam Cohene25af792013-06-06 23:08:25 -0700265 editor.commit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700266 loadedOldDb = true;
Adam Cohene25af792013-06-06 23:08:25 -0700267 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700268 return loadedOldDb;
Adam Cohene25af792013-06-06 23:08:25 -0700269 }
270
271 /**
Brian Muramatsu5524b492012-10-02 16:55:54 -0700272 * @param workspaceResId that can be 0 to use default or non-zero for specific resource
273 */
Michael Jurka45355c42012-10-08 13:21:35 +0200274 synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400275 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700276 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
Adam Cohene25af792013-06-06 23:08:25 -0700277
Winson Chungc763c4e2013-07-19 13:49:06 -0700278 if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Chris Wren5dee7af2013-12-20 17:22:11 -0500279 Log.d(TAG, "loading default workspace");
Michael Jurka45355c42012-10-08 13:21:35 +0200280 int workspaceResId = origWorkspaceResId;
281
Brian Muramatsu5524b492012-10-02 16:55:54 -0700282 // Use default workspace resource if none provided
283 if (workspaceResId == 0) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800284 workspaceResId =
285 sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
Brian Muramatsu5524b492012-10-02 16:55:54 -0700286 }
287
Michael Jurkab85f8a42012-04-25 15:48:32 -0700288 // Populate favorites table with initial favorites
289 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700290 editor.remove(EMPTY_DATABASE_CREATED);
Michael Jurka45355c42012-10-08 13:21:35 +0200291 if (origWorkspaceResId != 0) {
292 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
293 }
Adam Cohene25af792013-06-06 23:08:25 -0700294
Brian Muramatsu5524b492012-10-02 16:55:54 -0700295 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
Winson Chungc763c4e2013-07-19 13:49:06 -0700296 mOpenHelper.setFlagJustLoadedOldDb();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700297 editor.commit();
298 }
299 }
300
Dan Sandlerd5024042014-01-09 15:01:33 -0500301 public void migrateLauncher2Shortcuts() {
302 mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
303 LauncherSettings.Favorites.OLD_CONTENT_URI);
304 }
305
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800306 private static int getDefaultWorkspaceResourceId() {
Nilesh Agrawal16f3ea82014-01-09 17:14:01 -0800307 if (LauncherAppState.isDisableAllApps()) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800308 return R.xml.default_workspace_no_all_apps;
309 } else {
310 return R.xml.default_workspace;
311 }
312 }
313
Winson Chungc763c4e2013-07-19 13:49:06 -0700314 private static interface ContentValuesCallback {
315 public void onRow(ContentValues values);
316 }
317
Adam Cohen6dbe0492013-12-02 17:00:14 -0800318 private static boolean shouldImportLauncher2Database(Context context) {
319 boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet);
320
321 // We don't import the old databse for tablets, as the grid size has changed.
322 return !isTablet && IMPORT_LAUNCHER2_DATABASE;
323 }
324
Dan Sandlerd5024042014-01-09 15:01:33 -0500325 public void deleteDatabase() {
326 // Are you sure? (y/n)
327 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Dan Sandler2b471742014-01-21 14:14:41 -0500328 final File dbFile = new File(db.getPath());
Dan Sandlerd5024042014-01-09 15:01:33 -0500329 mOpenHelper.close();
Dan Sandler2b471742014-01-21 14:14:41 -0500330 if (dbFile.exists()) {
331 SQLiteDatabase.deleteDatabase(dbFile);
332 }
Dan Sandlerd5024042014-01-09 15:01:33 -0500333 mOpenHelper = new DatabaseHelper(getContext());
334 }
335
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800336 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800337 private static final String TAG_FAVORITES = "favorites";
338 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700339 private static final String TAG_CLOCK = "clock";
340 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700341 private static final String TAG_APPWIDGET = "appwidget";
342 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700343 private static final String TAG_FOLDER = "folder";
Winson Chungb3302ae2012-05-01 10:19:14 -0700344 private static final String TAG_EXTRA = "extra";
Daniel Sandler57dac262013-10-03 13:28:36 -0400345 private static final String TAG_INCLUDE = "include";
Winson Chung3d503fb2011-07-13 17:25:49 -0700346
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800347 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700348 private final AppWidgetHost mAppWidgetHost;
Adam Cohendcd297f2013-06-18 13:13:40 -0700349 private long mMaxItemId = -1;
350 private long mMaxScreenId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800351
Winson Chung0b560dd2014-01-21 13:00:26 -0800352 private boolean mNewDbCreated = false;
353
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800354 DatabaseHelper(Context context) {
355 super(context, DATABASE_NAME, null, DATABASE_VERSION);
356 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700357 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700358
359 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
360 // the DB here
Adam Cohendcd297f2013-06-18 13:13:40 -0700361 if (mMaxItemId == -1) {
362 mMaxItemId = initializeMaxItemId(getWritableDatabase());
363 }
364 if (mMaxScreenId == -1) {
365 mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
Winson Chung3d503fb2011-07-13 17:25:49 -0700366 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800367 }
368
Winson Chung0b560dd2014-01-21 13:00:26 -0800369 public boolean wasNewDbCreated() {
370 return mNewDbCreated;
371 }
372
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700373 /**
374 * Send notification that we've deleted the {@link AppWidgetHost},
375 * probably as part of the initial database creation. The receiver may
376 * want to re-call {@link AppWidgetHost#startListening()} to ensure
377 * callbacks are correctly set.
378 */
379 private void sendAppWidgetResetNotify() {
380 final ContentResolver resolver = mContext.getContentResolver();
381 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
382 }
383
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800384 @Override
385 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800386 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700387
Adam Cohendcd297f2013-06-18 13:13:40 -0700388 mMaxItemId = 1;
389 mMaxScreenId = 0;
Winson Chung0b560dd2014-01-21 13:00:26 -0800390 mNewDbCreated = true;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700391
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800392 db.execSQL("CREATE TABLE favorites (" +
393 "_id INTEGER PRIMARY KEY," +
394 "title TEXT," +
395 "intent TEXT," +
396 "container INTEGER," +
397 "screen INTEGER," +
398 "cellX INTEGER," +
399 "cellY INTEGER," +
400 "spanX INTEGER," +
401 "spanY INTEGER," +
402 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700403 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800404 "isShortcut INTEGER," +
405 "iconType INTEGER," +
406 "iconPackage TEXT," +
407 "iconResource TEXT," +
408 "icon BLOB," +
409 "uri TEXT," +
Chris Wrend5e66bf2013-09-16 14:02:29 -0400410 "displayMode INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400411 "appWidgetProvider TEXT," +
412 "modified INTEGER NOT NULL DEFAULT 0" +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800413 ");");
Adam Cohendcd297f2013-06-18 13:13:40 -0700414 addWorkspacesTable(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800415
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700416 // Database was just created, so wipe any previous widgets
417 if (mAppWidgetHost != null) {
418 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700419 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800420 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700421
Adam Cohen6dbe0492013-12-02 17:00:14 -0800422 if (shouldImportLauncher2Database(mContext)) {
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500423 // Try converting the old database
424 ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
425 public void onRow(ContentValues values) {
426 int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
427 if (container == Favorites.CONTAINER_DESKTOP) {
428 int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
429 screen = (int) upgradeLauncherDb_permuteScreens(screen);
430 values.put(LauncherSettings.Favorites.SCREEN, screen);
431 }
432 }
433 };
434 Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
435 "/old_favorites?notify=true");
436 if (!convertDatabase(db, uri, permuteScreensCb, true)) {
437 // Try and upgrade from the Launcher2 db
438 uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
439 if (!convertDatabase(db, uri, permuteScreensCb, false)) {
440 // If we fail, then set a flag to load the default workspace
441 setFlagEmptyDbCreated();
442 return;
Winson Chungc763c4e2013-07-19 13:49:06 -0700443 }
444 }
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500445 // Right now, in non-default workspace cases, we want to run the final
446 // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
447 // set that flag too.
448 setFlagJustLoadedOldDb();
449 } else {
450 // Fresh and clean launcher DB.
451 mMaxItemId = initializeMaxItemId(db);
452 setFlagEmptyDbCreated();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800453 }
454 }
455
Adam Cohendcd297f2013-06-18 13:13:40 -0700456 private void addWorkspacesTable(SQLiteDatabase db) {
457 db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
458 LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400459 LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
460 LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
Adam Cohendcd297f2013-06-18 13:13:40 -0700461 ");");
462 }
463
Winson Chungc763c4e2013-07-19 13:49:06 -0700464 private void setFlagJustLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400465 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700466 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
467 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700468 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
469 editor.putBoolean(EMPTY_DATABASE_CREATED, false);
Michael Jurkab85f8a42012-04-25 15:48:32 -0700470 editor.commit();
471 }
472
Winson Chungc763c4e2013-07-19 13:49:06 -0700473 private void setFlagEmptyDbCreated() {
474 String spKey = LauncherAppState.getSharedPreferencesKey();
475 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
476 SharedPreferences.Editor editor = sp.edit();
477 editor.putBoolean(EMPTY_DATABASE_CREATED, true);
478 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
479 editor.commit();
480 }
481
482 // We rearrange the screens from the old launcher
483 // 12345 -> 34512
484 private long upgradeLauncherDb_permuteScreens(long screen) {
485 if (screen >= 2) {
486 return screen - 2;
487 } else {
488 return screen + 3;
489 }
490 }
491
492 private boolean convertDatabase(SQLiteDatabase db, Uri uri,
493 ContentValuesCallback cb, boolean deleteRows) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800494 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800495 boolean converted = false;
496
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800497 final ContentResolver resolver = mContext.getContentResolver();
498 Cursor cursor = null;
499
500 try {
501 cursor = resolver.query(uri, null, null, null, null);
502 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700503 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800504 }
505
506 // We already have a favorites database in the old provider
Winson Chungc763c4e2013-07-19 13:49:06 -0700507 if (cursor != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800508 try {
Winson Chungc763c4e2013-07-19 13:49:06 -0700509 if (cursor.getCount() > 0) {
510 converted = copyFromCursor(db, cursor, cb) > 0;
511 if (converted && deleteRows) {
512 resolver.delete(uri, null, null);
513 }
514 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800515 } finally {
516 cursor.close();
517 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800518 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700519
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800520 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700521 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800522 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800523 convertWidgets(db);
Winson Chungc763c4e2013-07-19 13:49:06 -0700524
525 // Update max item id
526 mMaxItemId = initializeMaxItemId(db);
527 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800528 }
529
530 return converted;
531 }
532
Winson Chungc763c4e2013-07-19 13:49:06 -0700533 private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) {
Romain Guy73b979d2009-06-09 12:57:21 -0700534 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800535 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
536 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
537 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
538 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
539 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
540 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
541 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
542 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
543 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
544 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
545 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
546 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
547 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
548
549 ContentValues[] rows = new ContentValues[c.getCount()];
550 int i = 0;
551 while (c.moveToNext()) {
552 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700553 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800554 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
555 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
556 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
557 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
558 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
559 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
560 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
561 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700562 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800563 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
564 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
565 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
566 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
567 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
Winson Chungc763c4e2013-07-19 13:49:06 -0700568 if (cb != null) {
569 cb.onRow(values);
570 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800571 rows[i++] = values;
572 }
573
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800574 int total = 0;
Winson Chungc763c4e2013-07-19 13:49:06 -0700575 if (i > 0) {
576 db.beginTransaction();
577 try {
578 int numValues = rows.length;
579 for (i = 0; i < numValues; i++) {
580 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
581 return 0;
582 } else {
583 total++;
584 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800585 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700586 db.setTransactionSuccessful();
587 } finally {
588 db.endTransaction();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800589 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800590 }
591
592 return total;
593 }
594
595 @Override
596 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Winson Chungc763c4e2013-07-19 13:49:06 -0700597 if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700598
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800599 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700600 if (version < 3) {
601 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800602 db.beginTransaction();
603 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700604 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800605 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700606 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800607 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700608 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800609 } catch (SQLException ex) {
610 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800611 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800612 } finally {
613 db.endTransaction();
614 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700615
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800616 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700617 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800618 convertWidgets(db);
619 }
620 }
Romain Guy73b979d2009-06-09 12:57:21 -0700621
622 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800623 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700624 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700625
Romain Guy509cd6a2010-03-23 15:10:56 -0700626 // Where's version 5?
627 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
Daniel Sandler325dc232013-06-05 22:57:57 -0400628 // - Passion shipped on 2.1 with version 6 of launcher3
Romain Guy509cd6a2010-03-23 15:10:56 -0700629 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
630 // but version 5 on there was the updateContactsShortcuts change
631 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
632 // The updateContactsShortcuts change is idempotent, so running it twice
633 // is okay so we'll do that when upgrading the devices that shipped with it.
634 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800635 // We went from 3 to 5 screens. Move everything 1 to the right
636 db.beginTransaction();
637 try {
638 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
639 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800640 } catch (SQLException ex) {
641 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800642 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800643 } finally {
644 db.endTransaction();
645 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700646
Romain Guy509cd6a2010-03-23 15:10:56 -0700647 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800648 if (updateContactsShortcuts(db)) {
649 version = 6;
650 }
651 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000652
653 if (version < 7) {
654 // Version 7 gets rid of the special search widget.
655 convertWidgets(db);
656 version = 7;
657 }
658
Joe Onorato0589f0f2010-02-08 13:44:00 -0800659 if (version < 8) {
660 // Version 8 (froyo) has the icons all normalized. This should
661 // already be the case in practice, but we now rely on it and don't
662 // resample the images each time.
663 normalizeIcons(db);
664 version = 8;
665 }
666
Winson Chung3d503fb2011-07-13 17:25:49 -0700667 if (version < 9) {
668 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
669 // 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 -0700670 if (mMaxItemId == -1) {
671 mMaxItemId = initializeMaxItemId(db);
Winson Chung3d503fb2011-07-13 17:25:49 -0700672 }
673
674 // Add default hotseat icons
Winson Chung6d092682011-11-16 18:43:26 -0800675 loadFavorites(db, R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700676 version = 9;
677 }
678
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700679 // We bumped the version three time during JB, once to update the launch flags, once to
680 // update the override for the default launch animation and once to set the mimetype
681 // to improve startup performance
682 if (version < 12) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700683 // Contact shortcuts need a different set of flags to be launched now
684 // The updateContactsShortcuts change is idempotent, so we can keep using it like
685 // back in the Donut days
686 updateContactsShortcuts(db);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700687 version = 12;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700688 }
689
Adam Cohendcd297f2013-06-18 13:13:40 -0700690 if (version < 13) {
691 // With the new shrink-wrapped and re-orderable workspaces, it makes sense
692 // to persist workspace screens and their relative order.
693 mMaxScreenId = 0;
694
695 // This will never happen in the wild, but when we switch to using workspace
696 // screen ids, redo the import from old launcher.
Winson Chungc763c4e2013-07-19 13:49:06 -0700697 sJustLoadedFromOldDb = true;
Adam Cohendcd297f2013-06-18 13:13:40 -0700698
699 addWorkspacesTable(db);
700 version = 13;
701 }
702
Chris Wrend5e66bf2013-09-16 14:02:29 -0400703 if (version < 14) {
704 db.beginTransaction();
705 try {
706 // Insert new column for holding widget provider name
707 db.execSQL("ALTER TABLE favorites " +
708 "ADD COLUMN appWidgetProvider TEXT;");
709 db.setTransactionSuccessful();
710 version = 14;
711 } catch (SQLException ex) {
712 // Old version remains, which means we wipe old data
713 Log.e(TAG, ex.getMessage(), ex);
714 } finally {
715 db.endTransaction();
716 }
717 }
718
Chris Wren1ada10d2013-09-13 18:01:38 -0400719
720 if (version < 15) {
721 db.beginTransaction();
722 try {
723 // Insert new column for holding update timestamp
724 db.execSQL("ALTER TABLE favorites " +
725 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
726 db.execSQL("ALTER TABLE workspaceScreens " +
727 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
728 db.setTransactionSuccessful();
729 version = 15;
730 } catch (SQLException ex) {
731 // Old version remains, which means we wipe old data
732 Log.e(TAG, ex.getMessage(), ex);
733 } finally {
734 db.endTransaction();
735 }
736 }
737
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800738 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800739 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800740 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
Adam Cohendcd297f2013-06-18 13:13:40 -0700741 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
742
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800743 onCreate(db);
744 }
745 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800746
747 private boolean updateContactsShortcuts(SQLiteDatabase db) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800748 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
749 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
750
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700751 Cursor c = null;
752 final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800753 db.beginTransaction();
754 try {
755 // Select and iterate through each matching widget
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700756 c = db.query(TABLE_FAVORITES,
757 new String[] { Favorites._ID, Favorites.INTENT },
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800758 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700759 if (c == null) return false;
760
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800761 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700762
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800763 final int idIndex = c.getColumnIndex(Favorites._ID);
764 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700765
766 while (c.moveToNext()) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800767 long favoriteId = c.getLong(idIndex);
768 final String intentUri = c.getString(intentIndex);
769 if (intentUri != null) {
770 try {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700771 final Intent intent = Intent.parseUri(intentUri, 0);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800772 android.util.Log.d("Home", intent.toString());
773 final Uri uri = intent.getData();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700774 if (uri != null) {
775 final String data = uri.toString();
776 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
777 actionQuickContact.equals(intent.getAction())) &&
778 (data.startsWith("content://contacts/people/") ||
779 data.startsWith("content://com.android.contacts/" +
780 "contacts/lookup/"))) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800781
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700782 final Intent newIntent = new Intent(actionQuickContact);
783 // When starting from the launcher, start in a new, cleared task
784 // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
785 // clear the whole thing preemptively here since
786 // QuickContactActivity will finish itself when launching other
787 // detail activities.
788 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
789 Intent.FLAG_ACTIVITY_CLEAR_TASK);
Winson Chung2672ff92012-05-04 16:22:30 -0700790 newIntent.putExtra(
791 Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700792 newIntent.setData(uri);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700793 // Determine the type and also put that in the shortcut
794 // (that can speed up launch a bit)
795 newIntent.setDataAndType(uri, newIntent.resolveType(mContext));
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800796
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700797 final ContentValues values = new ContentValues();
798 values.put(LauncherSettings.Favorites.INTENT,
799 newIntent.toUri(0));
800
801 String updateWhere = Favorites._ID + "=" + favoriteId;
802 db.update(TABLE_FAVORITES, values, updateWhere, null);
803 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800804 }
805 } catch (RuntimeException ex) {
806 Log.e(TAG, "Problem upgrading shortcut", ex);
807 } catch (URISyntaxException e) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700808 Log.e(TAG, "Problem upgrading shortcut", e);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800809 }
810 }
811 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700812
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800813 db.setTransactionSuccessful();
814 } catch (SQLException ex) {
815 Log.w(TAG, "Problem while upgrading contacts", ex);
816 return false;
817 } finally {
818 db.endTransaction();
819 if (c != null) {
820 c.close();
821 }
822 }
823
824 return true;
825 }
826
Joe Onorato0589f0f2010-02-08 13:44:00 -0800827 private void normalizeIcons(SQLiteDatabase db) {
828 Log.d(TAG, "normalizing icons");
829
Joe Onorato346e1292010-02-18 10:34:24 -0500830 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800831 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400832 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800833 try {
834 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400835 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600836 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800837
838 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
839 Favorites.ICON_TYPE_BITMAP, null);
840
841 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
842 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
843
844 while (c.moveToNext()) {
845 long id = c.getLong(idIndex);
846 byte[] data = c.getBlob(iconIndex);
847 try {
848 Bitmap bitmap = Utilities.resampleIconBitmap(
849 BitmapFactory.decodeByteArray(data, 0, data.length),
850 mContext);
851 if (bitmap != null) {
852 update.bindLong(1, id);
853 data = ItemInfo.flattenBitmap(bitmap);
854 if (data != null) {
855 update.bindBlob(2, data);
856 update.execute();
857 }
858 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800859 }
860 } catch (Exception e) {
861 if (!logged) {
862 Log.e(TAG, "Failed normalizing icon " + id, e);
863 } else {
864 Log.e(TAG, "Also failed normalizing icon " + id);
865 }
866 logged = true;
867 }
868 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000869 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800870 } catch (SQLException ex) {
871 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
872 } finally {
873 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400874 if (update != null) {
875 update.close();
876 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800877 if (c != null) {
878 c.close();
879 }
880 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700881 }
882
883 // Generates a new ID to use for an object in your database. This method should be only
884 // called from the main UI thread. As an exception, we do call it when we call the
885 // constructor from the worker thread; however, this doesn't extend until after the
886 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
887 // after that point
Adam Cohendcd297f2013-06-18 13:13:40 -0700888 public long generateNewItemId() {
889 if (mMaxItemId < 0) {
890 throw new RuntimeException("Error: max item id was not initialized");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700891 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700892 mMaxItemId += 1;
893 return mMaxItemId;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700894 }
895
Winson Chungc763c4e2013-07-19 13:49:06 -0700896 public void updateMaxItemId(long id) {
897 mMaxItemId = id + 1;
898 }
899
Chris Wren5dee7af2013-12-20 17:22:11 -0500900 public void checkId(String table, ContentValues values) {
901 long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
902 if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
903 mMaxScreenId = Math.max(id, mMaxScreenId);
904 } else {
905 mMaxItemId = Math.max(id, mMaxItemId);
906 }
907 }
908
Adam Cohendcd297f2013-06-18 13:13:40 -0700909 private long initializeMaxItemId(SQLiteDatabase db) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700910 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
911
912 // get the result
913 final int maxIdIndex = 0;
914 long id = -1;
915 if (c != null && c.moveToNext()) {
916 id = c.getLong(maxIdIndex);
917 }
Michael Jurka5130e402011-10-13 04:55:35 -0700918 if (c != null) {
919 c.close();
920 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700921
922 if (id == -1) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700923 throw new RuntimeException("Error: could not query max item id");
924 }
925
926 return id;
927 }
928
929 // Generates a new ID to use for an workspace screen in your database. This method
930 // should be only called from the main UI thread. As an exception, we do call it when we
931 // call the constructor from the worker thread; however, this doesn't extend until after the
932 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
933 // after that point
934 public long generateNewScreenId() {
935 if (mMaxScreenId < 0) {
936 throw new RuntimeException("Error: max screen id was not initialized");
937 }
938 mMaxScreenId += 1;
Winson Chunga90303b2013-11-15 13:05:06 -0800939 // Log to disk
940 Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700941 return mMaxScreenId;
942 }
943
944 public void updateMaxScreenId(long maxScreenId) {
Winson Chunga90303b2013-11-15 13:05:06 -0800945 // Log to disk
946 Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700947 mMaxScreenId = maxScreenId;
948 }
949
950 private long initializeMaxScreenId(SQLiteDatabase db) {
951 Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
952
953 // get the result
954 final int maxIdIndex = 0;
955 long id = -1;
956 if (c != null && c.moveToNext()) {
957 id = c.getLong(maxIdIndex);
958 }
959 if (c != null) {
960 c.close();
961 }
962
963 if (id == -1) {
964 throw new RuntimeException("Error: could not query max screen id");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700965 }
966
Winson Chunga90303b2013-11-15 13:05:06 -0800967 // Log to disk
968 Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700969 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800970 }
971
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800972 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700973 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000974 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800975 */
976 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000977 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800978 final int[] bindSources = new int[] {
979 Favorites.ITEM_TYPE_WIDGET_CLOCK,
980 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000981 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800982 };
Bjorn Bringert7984c942009-12-09 15:38:25 +0000983
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800984 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700985
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800986 Cursor c = null;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700987
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800988 db.beginTransaction();
989 try {
990 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +0000991 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800992 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700993
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800994 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700995
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800996 final ContentValues values = new ContentValues();
997 while (c != null && c.moveToNext()) {
998 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +0000999 int favoriteType = c.getInt(1);
1000
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001001 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001002 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001003 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001004
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001005 if (LOGD) {
1006 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
1007 + " for favoriteId=" + favoriteId);
1008 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001009 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +00001010 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
1011 values.put(Favorites.APPWIDGET_ID, appWidgetId);
1012
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001013 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +00001014 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
1015 values.put(LauncherSettings.Favorites.SPANX, 4);
1016 values.put(LauncherSettings.Favorites.SPANY, 1);
1017 } else {
1018 values.put(LauncherSettings.Favorites.SPANX, 2);
1019 values.put(LauncherSettings.Favorites.SPANY, 2);
1020 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001021
1022 String updateWhere = Favorites._ID + "=" + favoriteId;
1023 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +00001024
Bjorn Bringert34251342009-12-15 13:33:11 +00001025 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001026 // TODO: check return value
1027 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001028 new ComponentName("com.android.alarmclock",
1029 "com.android.alarmclock.AnalogAppWidgetProvider"));
1030 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001031 // TODO: check return value
1032 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001033 new ComponentName("com.android.camera",
1034 "com.android.camera.PhotoAppWidgetProvider"));
1035 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001036 // TODO: check return value
1037 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001038 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +00001039 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001040 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001041 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001042 }
1043 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001044
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001045 db.setTransactionSuccessful();
1046 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001047 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001048 } finally {
1049 db.endTransaction();
1050 if (c != null) {
1051 c.close();
1052 }
1053 }
Winson Chungc763c4e2013-07-19 13:49:06 -07001054
1055 // Update max item id
1056 mMaxItemId = initializeMaxItemId(db);
1057 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001058 }
1059
Michael Jurka8b805b12012-04-18 14:23:14 -07001060 private static final void beginDocument(XmlPullParser parser, String firstElementName)
1061 throws XmlPullParserException, IOException {
1062 int type;
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001063 while ((type = parser.next()) != XmlPullParser.START_TAG
1064 && type != XmlPullParser.END_DOCUMENT) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001065 ;
1066 }
1067
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001068 if (type != XmlPullParser.START_TAG) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001069 throw new XmlPullParserException("No start tag found");
1070 }
1071
1072 if (!parser.getName().equals(firstElementName)) {
1073 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1074 ", expected " + firstElementName);
1075 }
1076 }
1077
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001078 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001079 * Loads the default set of favorite packages from an xml file.
1080 *
1081 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -07001082 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001083 */
Winson Chung6d092682011-11-16 18:43:26 -08001084 private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001085 Intent intent = new Intent(Intent.ACTION_MAIN, null);
1086 intent.addCategory(Intent.CATEGORY_LAUNCHER);
1087 ContentValues values = new ContentValues();
1088
Daniel Sandler57dac262013-10-03 13:28:36 -04001089 if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
1090
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001091 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001092 int i = 0;
1093 try {
Winson Chung6d092682011-11-16 18:43:26 -08001094 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001095 AttributeSet attrs = Xml.asAttributeSet(parser);
Michael Jurka8b805b12012-04-18 14:23:14 -07001096 beginDocument(parser, TAG_FAVORITES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001097
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001098 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001099
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001100 int type;
1101 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1102 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
1103
1104 if (type != XmlPullParser.START_TAG) {
1105 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001106 }
1107
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001108 boolean added = false;
1109 final String name = parser.getName();
1110
Daniel Sandler57dac262013-10-03 13:28:36 -04001111 if (TAG_INCLUDE.equals(name)) {
1112 final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Include);
1113
1114 final int resId = a.getResourceId(R.styleable.Include_workspace, 0);
1115
1116 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
1117 "", resId));
1118
1119 if (resId != 0 && resId != workspaceResourceId) {
1120 // recursively load some more favorites, why not?
1121 i += loadFavorites(db, resId);
1122 added = false;
Daniel Sandler57dac262013-10-03 13:28:36 -04001123 } else {
1124 Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
1125 }
1126
1127 a.recycle();
1128
1129 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
1130 continue;
1131 }
1132
1133 // Assuming it's a <favorite> at this point
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001134 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
1135
Winson Chung3d503fb2011-07-13 17:25:49 -07001136 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
1137 if (a.hasValue(R.styleable.Favorite_container)) {
1138 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
1139 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001140
Winson Chung6d092682011-11-16 18:43:26 -08001141 String screen = a.getString(R.styleable.Favorite_screen);
1142 String x = a.getString(R.styleable.Favorite_x);
1143 String y = a.getString(R.styleable.Favorite_y);
1144
Winson Chung6d092682011-11-16 18:43:26 -08001145 values.clear();
1146 values.put(LauncherSettings.Favorites.CONTAINER, container);
1147 values.put(LauncherSettings.Favorites.SCREEN, screen);
1148 values.put(LauncherSettings.Favorites.CELLX, x);
1149 values.put(LauncherSettings.Favorites.CELLY, y);
1150
Daniel Sandler57dac262013-10-03 13:28:36 -04001151 if (LOGD) {
1152 final String title = a.getString(R.styleable.Favorite_title);
1153 final String pkg = a.getString(R.styleable.Favorite_packageName);
1154 final String something = title != null ? title : pkg;
1155 Log.v(TAG, String.format(
1156 ("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
1157 "", name,
1158 (something == null ? "" : (" \"" + something + "\"")),
1159 container, screen, x, y));
1160 }
1161
Winson Chung6d092682011-11-16 18:43:26 -08001162 if (TAG_FAVORITE.equals(name)) {
1163 long id = addAppShortcut(db, values, a, packageManager, intent);
1164 added = id >= 0;
1165 } else if (TAG_SEARCH.equals(name)) {
1166 added = addSearchWidget(db, values);
1167 } else if (TAG_CLOCK.equals(name)) {
1168 added = addClockWidget(db, values);
1169 } else if (TAG_APPWIDGET.equals(name)) {
Winson Chungb3302ae2012-05-01 10:19:14 -07001170 added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
Winson Chung6d092682011-11-16 18:43:26 -08001171 } else if (TAG_SHORTCUT.equals(name)) {
1172 long id = addUriShortcut(db, values, a);
1173 added = id >= 0;
1174 } else if (TAG_FOLDER.equals(name)) {
1175 String title;
1176 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
1177 if (titleResId != -1) {
1178 title = mContext.getResources().getString(titleResId);
1179 } else {
1180 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -07001181 }
Winson Chung6d092682011-11-16 18:43:26 -08001182 values.put(LauncherSettings.Favorites.TITLE, title);
1183 long folderId = addFolder(db, values);
1184 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -07001185
Winson Chung6d092682011-11-16 18:43:26 -08001186 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -07001187
Winson Chung6d092682011-11-16 18:43:26 -08001188 int folderDepth = parser.getDepth();
1189 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1190 parser.getDepth() > folderDepth) {
1191 if (type != XmlPullParser.START_TAG) {
1192 continue;
1193 }
1194 final String folder_item_name = parser.getName();
1195
1196 TypedArray ar = mContext.obtainStyledAttributes(attrs,
1197 R.styleable.Favorite);
1198 values.clear();
1199 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
1200
Daniel Sandler57dac262013-10-03 13:28:36 -04001201 if (LOGD) {
1202 final String pkg = ar.getString(R.styleable.Favorite_packageName);
1203 final String uri = ar.getString(R.styleable.Favorite_uri);
1204 Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
1205 folder_item_name, uri != null ? uri : pkg));
1206 }
1207
Winson Chung6d092682011-11-16 18:43:26 -08001208 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
1209 long id =
1210 addAppShortcut(db, values, ar, packageManager, intent);
1211 if (id >= 0) {
1212 folderItems.add(id);
1213 }
1214 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
1215 long id = addUriShortcut(db, values, ar);
1216 if (id >= 0) {
1217 folderItems.add(id);
1218 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001219 } else {
Winson Chung6d092682011-11-16 18:43:26 -08001220 throw new RuntimeException("Folders can " +
1221 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -07001222 }
Winson Chung6d092682011-11-16 18:43:26 -08001223 ar.recycle();
1224 }
1225 // We can only have folders with >= 2 items, so we need to remove the
1226 // folder and clean up if less than 2 items were included, or some
1227 // failed to add, and less than 2 were actually added
1228 if (folderItems.size() < 2 && folderId >= 0) {
1229 // We just delete the folder and any items that made it
1230 deleteId(db, folderId);
1231 if (folderItems.size() > 0) {
1232 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -07001233 }
Winson Chung6d092682011-11-16 18:43:26 -08001234 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -07001235 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001236 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001237 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001238 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001239 }
1240 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001241 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001242 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001243 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -07001244 } catch (RuntimeException e) {
1245 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001247
Winson Chungc763c4e2013-07-19 13:49:06 -07001248 // Update the max item id after we have loaded the database
1249 if (mMaxItemId == -1) {
1250 mMaxItemId = initializeMaxItemId(db);
1251 }
1252
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001253 return i;
1254 }
1255
Adam Cohen228da5a2011-07-27 22:23:47 -07001256 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001257 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -07001258 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001259 ActivityInfo info;
1260 String packageName = a.getString(R.styleable.Favorite_packageName);
1261 String className = a.getString(R.styleable.Favorite_className);
1262 try {
Romain Guy693599f2010-03-23 10:58:18 -07001263 ComponentName cn;
1264 try {
1265 cn = new ComponentName(packageName, className);
1266 info = packageManager.getActivityInfo(cn, 0);
1267 } catch (PackageManager.NameNotFoundException nnfe) {
1268 String[] packages = packageManager.currentToCanonicalPackageNames(
1269 new String[] { packageName });
1270 cn = new ComponentName(packages[0], className);
1271 info = packageManager.getActivityInfo(cn, 0);
1272 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001273 id = generateNewItemId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001274 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -07001275 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1276 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -07001277 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001278 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
1279 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
1280 values.put(Favorites.SPANX, 1);
1281 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001282 values.put(Favorites._ID, generateNewItemId());
Adam Cohen228da5a2011-07-27 22:23:47 -07001283 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1284 return -1;
1285 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001286 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001287 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001288 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001289 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001290 return id;
1291 }
1292
1293 private long addFolder(SQLiteDatabase db, ContentValues values) {
1294 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
1295 values.put(Favorites.SPANX, 1);
1296 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001297 long id = generateNewItemId();
Adam Cohen228da5a2011-07-27 22:23:47 -07001298 values.put(Favorites._ID, id);
1299 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
1300 return -1;
1301 } else {
1302 return id;
1303 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001304 }
1305
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001306 private ComponentName getSearchWidgetProvider() {
1307 SearchManager searchManager =
1308 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
1309 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
1310 if (searchComponent == null) return null;
1311 return getProviderInPackage(searchComponent.getPackageName());
1312 }
1313
1314 /**
1315 * Gets an appwidget provider from the given package. If the package contains more than
1316 * one appwidget provider, an arbitrary one is returned.
1317 */
1318 private ComponentName getProviderInPackage(String packageName) {
1319 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1320 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
1321 if (providers == null) return null;
1322 final int providerCount = providers.size();
1323 for (int i = 0; i < providerCount; i++) {
1324 ComponentName provider = providers.get(i).provider;
1325 if (provider != null && provider.getPackageName().equals(packageName)) {
1326 return provider;
1327 }
1328 }
1329 return null;
1330 }
1331
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001332 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001333 ComponentName cn = getSearchWidgetProvider();
Winson Chungb3302ae2012-05-01 10:19:14 -07001334 return addAppWidget(db, values, cn, 4, 1, null);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001335 }
1336
1337 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +00001338 ComponentName cn = new ComponentName("com.android.alarmclock",
1339 "com.android.alarmclock.AnalogAppWidgetProvider");
Winson Chungb3302ae2012-05-01 10:19:14 -07001340 return addAppWidget(db, values, cn, 2, 2, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001341 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001342
Winson Chungb3302ae2012-05-01 10:19:14 -07001343 private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
1344 SQLiteDatabase db, ContentValues values, TypedArray a,
1345 PackageManager packageManager) throws XmlPullParserException, IOException {
Romain Guy693599f2010-03-23 10:58:18 -07001346
Mike Cleronb87bd162009-10-30 16:36:56 -07001347 String packageName = a.getString(R.styleable.Favorite_packageName);
1348 String className = a.getString(R.styleable.Favorite_className);
1349
1350 if (packageName == null || className == null) {
1351 return false;
1352 }
Romain Guy693599f2010-03-23 10:58:18 -07001353
1354 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -07001355 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -07001356 try {
1357 packageManager.getReceiverInfo(cn, 0);
1358 } catch (Exception e) {
1359 String[] packages = packageManager.currentToCanonicalPackageNames(
1360 new String[] { packageName });
1361 cn = new ComponentName(packages[0], className);
1362 try {
1363 packageManager.getReceiverInfo(cn, 0);
1364 } catch (Exception e1) {
1365 hasPackage = false;
1366 }
1367 }
1368
1369 if (hasPackage) {
1370 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
1371 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
Winson Chungb3302ae2012-05-01 10:19:14 -07001372
1373 // Read the extras
1374 Bundle extras = new Bundle();
1375 int widgetDepth = parser.getDepth();
1376 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1377 parser.getDepth() > widgetDepth) {
1378 if (type != XmlPullParser.START_TAG) {
1379 continue;
1380 }
1381
1382 TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
1383 if (TAG_EXTRA.equals(parser.getName())) {
1384 String key = ar.getString(R.styleable.Extra_key);
1385 String value = ar.getString(R.styleable.Extra_value);
1386 if (key != null && value != null) {
1387 extras.putString(key, value);
1388 } else {
1389 throw new RuntimeException("Widget extras must have a key and value");
1390 }
1391 } else {
1392 throw new RuntimeException("Widgets can contain only extras");
1393 }
1394 ar.recycle();
1395 }
1396
1397 return addAppWidget(db, values, cn, spanX, spanY, extras);
Romain Guy693599f2010-03-23 10:58:18 -07001398 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001399
Romain Guy693599f2010-03-23 10:58:18 -07001400 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +00001401 }
Bjorn Bringert7984c942009-12-09 15:38:25 +00001402 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
Winson Chungb3302ae2012-05-01 10:19:14 -07001403 int spanX, int spanY, Bundle extras) {
Mike Cleronb87bd162009-10-30 16:36:56 -07001404 boolean allocatedAppWidgets = false;
1405 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1406
1407 try {
1408 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001409
Mike Cleronb87bd162009-10-30 16:36:56 -07001410 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +00001411 values.put(Favorites.SPANX, spanX);
1412 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -07001413 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Chris Wrend5e66bf2013-09-16 14:02:29 -04001414 values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
Adam Cohendcd297f2013-06-18 13:13:40 -07001415 values.put(Favorites._ID, generateNewItemId());
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001416 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -07001417
1418 allocatedAppWidgets = true;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001419
Michael Jurka8b805b12012-04-18 14:23:14 -07001420 // TODO: need to check return value
1421 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
Winson Chungb3302ae2012-05-01 10:19:14 -07001422
1423 // Send a broadcast to configure the widget
1424 if (extras != null && !extras.isEmpty()) {
1425 Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
1426 intent.setComponent(cn);
1427 intent.putExtras(extras);
1428 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1429 mContext.sendBroadcast(intent);
1430 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001431 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001432 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -07001433 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001434
Mike Cleronb87bd162009-10-30 16:36:56 -07001435 return allocatedAppWidgets;
1436 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001437
1438 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -07001439 TypedArray a) {
1440 Resources r = mContext.getResources();
1441
1442 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
1443 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
1444
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001445 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -07001446 String uri = null;
1447 try {
1448 uri = a.getString(R.styleable.Favorite_uri);
1449 intent = Intent.parseUri(uri, 0);
1450 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001451 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -07001452 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -07001453 }
1454
1455 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001456 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001457 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001458 }
1459
Adam Cohendcd297f2013-06-18 13:13:40 -07001460 long id = generateNewItemId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001461 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1462 values.put(Favorites.INTENT, intent.toUri(0));
1463 values.put(Favorites.TITLE, r.getString(titleResId));
1464 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1465 values.put(Favorites.SPANX, 1);
1466 values.put(Favorites.SPANY, 1);
1467 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1468 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1469 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001470 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001471
Adam Cohen228da5a2011-07-27 22:23:47 -07001472 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1473 return -1;
1474 }
1475 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001476 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001477
1478 public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
1479 final ContentResolver resolver = mContext.getContentResolver();
1480 Cursor c = null;
1481 int count = 0;
1482 int curScreen = 0;
1483
1484 try {
1485 c = resolver.query(uri, null, null, null, "title ASC");
1486 } catch (Exception e) {
1487 // Ignore
1488 }
1489
Dan Sandlerd5024042014-01-09 15:01:33 -05001490 // We already have a favorites database in the old provider
1491 if (c != null) {
1492 try {
1493 if (c.getCount() > 0) {
1494 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1495 final int intentIndex
1496 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1497 final int titleIndex
1498 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1499 final int iconTypeIndex
1500 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
1501 final int iconIndex
1502 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1503 final int iconPackageIndex
1504 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
1505 final int iconResourceIndex
1506 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
1507 final int containerIndex
1508 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1509 final int itemTypeIndex
1510 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1511 final int screenIndex
1512 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1513 final int cellXIndex
1514 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1515 final int cellYIndex
1516 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1517 final int uriIndex
1518 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1519 final int displayModeIndex
1520 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
1521
1522 int i = 0;
1523 int curX = 0;
1524 int curY = 0;
1525
1526 final LauncherAppState app = LauncherAppState.getInstance();
1527 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1528 final int width = (int) grid.numColumns;
1529 final int height = (int) grid.numRows;
1530 final int hotseatWidth = (int) grid.numHotseatIcons;
Adam Cohen556f6132014-01-15 15:18:08 -08001531 PackageManager pm = mContext.getPackageManager();
Dan Sandlerd5024042014-01-09 15:01:33 -05001532
1533 final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
1534
Adam Cohen72960972014-01-15 18:13:55 -08001535 final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
1536 final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
Dan Sandlerd5024042014-01-09 15:01:33 -05001537
1538 while (c.moveToNext()) {
1539 final int itemType = c.getInt(itemTypeIndex);
1540 if (itemType != Favorites.ITEM_TYPE_APPLICATION
1541 && itemType != Favorites.ITEM_TYPE_SHORTCUT
1542 && itemType != Favorites.ITEM_TYPE_FOLDER) {
1543 continue;
1544 }
1545
1546 final int cellX = c.getInt(cellXIndex);
1547 final int cellY = c.getInt(cellYIndex);
1548 final int screen = c.getInt(screenIndex);
1549 int container = c.getInt(containerIndex);
1550 final String intentStr = c.getString(intentIndex);
1551 Launcher.addDumpLog(TAG, "migrating \""
1552 + c.getString(titleIndex) + "\": " + intentStr, true);
1553
1554 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
Adam Cohen556f6132014-01-15 15:18:08 -08001555
1556 final Intent intent;
1557 final ComponentName cn;
1558 try {
1559 intent = Intent.parseUri(intentStr, 0);
1560 } catch (URISyntaxException e) {
1561 // bogus intent?
1562 Launcher.addDumpLog(TAG,
1563 "skipping invalid intent uri", true);
1564 continue;
1565 }
1566
1567 cn = intent.getComponent();
Dan Sandlerd5024042014-01-09 15:01:33 -05001568 if (TextUtils.isEmpty(intentStr)) {
1569 // no intent? no icon
1570 Launcher.addDumpLog(TAG, "skipping empty intent", true);
1571 continue;
Adam Cohen72960972014-01-15 18:13:55 -08001572 } else if (cn != null &&
1573 !LauncherModel.isValidPackageComponent(pm, cn)) {
Adam Cohen556f6132014-01-15 15:18:08 -08001574 // component no longer exists.
Adam Cohen72960972014-01-15 18:13:55 -08001575 Launcher.addDumpLog(TAG, "skipping item whose component " +
Adam Cohen556f6132014-01-15 15:18:08 -08001576 "no longer exists.", true);
1577 continue;
Adam Cohen72960972014-01-15 18:13:55 -08001578 } else if (container ==
1579 LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1580 // Dedupe icons directly on the workspace
1581
Adam Cohen556f6132014-01-15 15:18:08 -08001582 // Canonicalize
1583 // the Play Store sets the package parameter, but Launcher
1584 // does not, so we clear that out to keep them the same
1585 intent.setPackage(null);
1586 final String key = intent.toUri(0);
1587 if (seenIntents.contains(key)) {
1588 Launcher.addDumpLog(TAG, "skipping duplicate", true);
Dan Sandlerd5024042014-01-09 15:01:33 -05001589 continue;
Adam Cohen556f6132014-01-15 15:18:08 -08001590 } else {
1591 seenIntents.add(key);
Dan Sandlerd5024042014-01-09 15:01:33 -05001592 }
1593 }
1594 }
1595
1596 ContentValues values = new ContentValues(c.getColumnCount());
1597 values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
1598 values.put(LauncherSettings.Favorites.INTENT, intentStr);
1599 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
1600 values.put(LauncherSettings.Favorites.ICON_TYPE,
1601 c.getInt(iconTypeIndex));
1602 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
1603 values.put(LauncherSettings.Favorites.ICON_PACKAGE,
1604 c.getString(iconPackageIndex));
1605 values.put(LauncherSettings.Favorites.ICON_RESOURCE,
1606 c.getString(iconResourceIndex));
1607 values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
1608 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
1609 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
1610 values.put(LauncherSettings.Favorites.DISPLAY_MODE,
1611 c.getInt(displayModeIndex));
1612
1613 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
Adam Cohen72960972014-01-15 18:13:55 -08001614 && (screen >= hotseatWidth ||
1615 screen == grid.hotseatAllAppsRank)) {
Dan Sandlerd5024042014-01-09 15:01:33 -05001616 // no room for you in the hotseat? it's off to the desktop with you
1617 container = Favorites.CONTAINER_DESKTOP;
1618 }
1619
1620 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1621 // In a folder or in the hotseat, preserve position
1622 values.put(LauncherSettings.Favorites.SCREEN, screen);
1623 values.put(LauncherSettings.Favorites.CELLX, cellX);
1624 values.put(LauncherSettings.Favorites.CELLY, cellY);
1625 } else {
Adam Cohen72960972014-01-15 18:13:55 -08001626 // For items contained directly on one of the workspace screen,
1627 // we'll determine their location (screen, x, y) in a second pass.
Dan Sandlerd5024042014-01-09 15:01:33 -05001628 }
1629
1630 values.put(LauncherSettings.Favorites.CONTAINER, container);
1631
Adam Cohen72960972014-01-15 18:13:55 -08001632 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
1633 shortcuts.add(values);
1634 } else {
1635 folders.add(values);
1636 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001637 }
1638
Adam Cohen72960972014-01-15 18:13:55 -08001639 final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
1640 // Folders first
1641 allItems.addAll(folders);
1642 // Then shortcuts
1643 allItems.addAll(shortcuts);
1644
1645 // Layout all the folders
1646 for (ContentValues values: allItems) {
1647 if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
1648 LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1649 // Hotseat items and folder items have already had their
1650 // location information set. Nothing to be done here.
1651 continue;
1652 }
1653 values.put(LauncherSettings.Favorites.SCREEN, curScreen);
1654 values.put(LauncherSettings.Favorites.CELLX, curX);
1655 values.put(LauncherSettings.Favorites.CELLY, curY);
1656 curX = (curX + 1) % width;
1657 if (curX == 0) {
1658 curY = (curY + 1);
1659 }
1660 // Leave the last row of icons blank on every screen
1661 if (curY == height - 1) {
1662 curScreen = (int) generateNewScreenId();
1663 curY = 0;
1664 }
1665 }
1666
1667 if (allItems.size() > 0) {
Dan Sandlerd5024042014-01-09 15:01:33 -05001668 db.beginTransaction();
1669 try {
Adam Cohen72960972014-01-15 18:13:55 -08001670 for (ContentValues row: allItems) {
1671 if (row == null) continue;
1672 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
Dan Sandlerd5024042014-01-09 15:01:33 -05001673 < 0) {
1674 return;
1675 } else {
1676 count++;
1677 }
1678 }
1679 db.setTransactionSuccessful();
1680 } finally {
1681 db.endTransaction();
1682 }
1683 }
1684
1685 db.beginTransaction();
1686 try {
1687 for (i=0; i<=curScreen; i++) {
1688 final ContentValues values = new ContentValues();
1689 values.put(LauncherSettings.WorkspaceScreens._ID, i);
1690 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1691 if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
1692 < 0) {
1693 return;
1694 }
1695 }
1696 db.setTransactionSuccessful();
1697 } finally {
1698 db.endTransaction();
1699 }
1700 }
1701 } finally {
1702 c.close();
1703 }
1704 }
1705
1706 Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
1707 + (curScreen+1) + " screens", true);
1708
1709 // ensure that new screens are created to hold these icons
1710 setFlagJustLoadedOldDb();
1711
1712 // Update max IDs; very important since we just grabbed IDs from another database
1713 mMaxItemId = initializeMaxItemId(db);
1714 mMaxScreenId = initializeMaxScreenId(db);
1715 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
1716 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001717 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001718
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001719 /**
1720 * Build a query string that will match any row where the column matches
1721 * anything in the values list.
1722 */
1723 static String buildOrWhereString(String column, int[] values) {
1724 StringBuilder selectWhere = new StringBuilder();
1725 for (int i = values.length - 1; i >= 0; i--) {
1726 selectWhere.append(column).append("=").append(values[i]);
1727 if (i > 0) {
1728 selectWhere.append(" OR ");
1729 }
1730 }
1731 return selectWhere.toString();
1732 }
1733
1734 static class SqlArguments {
1735 public final String table;
1736 public final String where;
1737 public final String[] args;
1738
1739 SqlArguments(Uri url, String where, String[] args) {
1740 if (url.getPathSegments().size() == 1) {
1741 this.table = url.getPathSegments().get(0);
1742 this.where = where;
1743 this.args = args;
1744 } else if (url.getPathSegments().size() != 2) {
1745 throw new IllegalArgumentException("Invalid URI: " + url);
1746 } else if (!TextUtils.isEmpty(where)) {
1747 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1748 } else {
1749 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001750 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001751 this.args = null;
1752 }
1753 }
1754
1755 SqlArguments(Uri url) {
1756 if (url.getPathSegments().size() == 1) {
1757 table = url.getPathSegments().get(0);
1758 where = null;
1759 args = null;
1760 } else {
1761 throw new IllegalArgumentException("Invalid URI: " + url);
1762 }
1763 }
1764 }
Adam Cohen72960972014-01-15 18:13:55 -08001765}