blob: 2256179c3fe13a15ab6d2971e82dc2a282496ff5 [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;
Yura085c8532014-02-11 15:15:29 +000025import android.content.ContentProviderOperation;
26import android.content.ContentProviderResult;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080027import android.content.ContentResolver;
Adam Cohen228da5a2011-07-27 22:23:47 -070028import android.content.ContentUris;
29import android.content.ContentValues;
30import android.content.Context;
31import android.content.Intent;
Yura085c8532014-02-11 15:15:29 +000032import android.content.OperationApplicationException;
Michael Jurkab85f8a42012-04-25 15:48:32 -070033import android.content.SharedPreferences;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080034import android.content.pm.ActivityInfo;
Jason Monk41314972014-03-03 16:11:30 -050035import android.content.pm.ApplicationInfo;
Jeff Sharkey5aeef582014-04-14 13:26:42 -070036import android.content.pm.PackageInfo;
Adam Cohen228da5a2011-07-27 22:23:47 -070037import android.content.pm.PackageManager;
Jason Monk41314972014-03-03 16:11:30 -050038import android.content.pm.ResolveInfo;
Adam Cohen228da5a2011-07-27 22:23:47 -070039import android.content.res.Resources;
40import android.content.res.TypedArray;
41import android.content.res.XmlResourceParser;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080042import android.database.Cursor;
43import android.database.SQLException;
Adam Cohen228da5a2011-07-27 22:23:47 -070044import android.database.sqlite.SQLiteDatabase;
45import android.database.sqlite.SQLiteOpenHelper;
46import android.database.sqlite.SQLiteQueryBuilder;
47import android.database.sqlite.SQLiteStatement;
Joe Onorato0589f0f2010-02-08 13:44:00 -080048import android.graphics.Bitmap;
49import android.graphics.BitmapFactory;
Adam Cohen228da5a2011-07-27 22:23:47 -070050import android.net.Uri;
Winson Chungb3302ae2012-05-01 10:19:14 -070051import android.os.Bundle;
Adam Cohen228da5a2011-07-27 22:23:47 -070052import android.provider.Settings;
53import android.text.TextUtils;
54import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080055import android.util.Log;
Dan Sandlerab5fa3a2014-03-06 23:48:04 -050056import android.util.SparseArray;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080057import android.util.Xml;
Adam Cohen228da5a2011-07-27 22:23:47 -070058
Kenny Guyed131872014-04-30 03:02:21 +010059import com.android.launcher3.compat.UserHandleCompat;
60import com.android.launcher3.compat.UserManagerCompat;
Chris Wrene523e702013-10-09 10:36:55 -040061import com.android.launcher3.config.ProviderConfig;
Kenny Guyed131872014-04-30 03:02:21 +010062import com.android.launcher3.LauncherSettings.Favorites;
Michael Jurka8b805b12012-04-18 14:23:14 -070063
64import org.xmlpull.v1.XmlPullParser;
65import org.xmlpull.v1.XmlPullParserException;
66
Dan Sandlerd5024042014-01-09 15:01:33 -050067import java.io.File;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080068import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070069import java.net.URISyntaxException;
Adam Cohen228da5a2011-07-27 22:23:47 -070070import java.util.ArrayList;
Adam Cohen71483f42014-05-15 14:04:01 -070071import java.util.Collections;
Dan Sandlerd5024042014-01-09 15:01:33 -050072import java.util.HashSet;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000073import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080074
The Android Open Source Project31dd5032009-03-03 19:32:27 -080075public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080076 private static final String TAG = "Launcher.LauncherProvider";
77 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080078
79 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070080
Kenny Guyed131872014-04-30 03:02:21 +010081 private static final int DATABASE_VERSION = 20;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080082
Adam Cohene25af792013-06-06 23:08:25 -070083 static final String OLD_AUTHORITY = "com.android.launcher2.settings";
Chris Wrene523e702013-10-09 10:36:55 -040084 static final String AUTHORITY = ProviderConfig.AUTHORITY;
Winson Chung3d503fb2011-07-13 17:25:49 -070085
Dan Sandlerf0b8dac2013-11-19 12:21:25 -050086 // Should we attempt to load anything from the com.android.launcher2 provider?
Dan Sandlerd5024042014-01-09 15:01:33 -050087 static final boolean IMPORT_LAUNCHER2_DATABASE = false;
Dan Sandlerf0b8dac2013-11-19 12:21:25 -050088
The Android Open Source Project31dd5032009-03-03 19:32:27 -080089 static final String TABLE_FAVORITES = "favorites";
Adam Cohendcd297f2013-06-18 13:13:40 -070090 static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080091 static final String PARAMETER_NOTIFY = "notify";
Winson Chungc763c4e2013-07-19 13:49:06 -070092 static final String UPGRADED_FROM_OLD_DATABASE =
93 "UPGRADED_FROM_OLD_DATABASE";
94 static final String EMPTY_DATABASE_CREATED =
95 "EMPTY_DATABASE_CREATED";
Michael Jurka45355c42012-10-08 13:21:35 +020096 static final String DEFAULT_WORKSPACE_RESOURCE_ID =
97 "DEFAULT_WORKSPACE_RESOURCE_ID";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080098
Winson Chungb3302ae2012-05-01 10:19:14 -070099 private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
Adam Cohene25af792013-06-06 23:08:25 -0700100 "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
Winson Chungb3302ae2012-05-01 10:19:14 -0700101
Anjali Koppal67e7cae2014-03-13 12:14:12 -0700102 private LauncherProviderChangeListener mListener;
103
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700104 /**
Romain Guy73b979d2009-06-09 12:57:21 -0700105 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700106 * {@link AppWidgetHost#deleteHost()} is called during database creation.
107 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
108 */
109 static final Uri CONTENT_APPWIDGET_RESET_URI =
110 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700111
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700112 private DatabaseHelper mOpenHelper;
Winson Chungc763c4e2013-07-19 13:49:06 -0700113 private static boolean sJustLoadedFromOldDb;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800114
115 @Override
116 public boolean onCreate() {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400117 final Context context = getContext();
118 mOpenHelper = new DatabaseHelper(context);
119 LauncherAppState.setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800120 return true;
121 }
122
Winson Chung0b560dd2014-01-21 13:00:26 -0800123 public boolean wasNewDbCreated() {
124 return mOpenHelper.wasNewDbCreated();
125 }
126
Anjali Koppal67e7cae2014-03-13 12:14:12 -0700127 public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
128 mListener = listener;
129 }
130
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800131 @Override
132 public String getType(Uri uri) {
133 SqlArguments args = new SqlArguments(uri, null, null);
134 if (TextUtils.isEmpty(args.where)) {
135 return "vnd.android.cursor.dir/" + args.table;
136 } else {
137 return "vnd.android.cursor.item/" + args.table;
138 }
139 }
140
141 @Override
142 public Cursor query(Uri uri, String[] projection, String selection,
143 String[] selectionArgs, String sortOrder) {
144
145 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
146 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
147 qb.setTables(args.table);
148
Romain Guy73b979d2009-06-09 12:57:21 -0700149 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800150 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
151 result.setNotificationUri(getContext().getContentResolver(), uri);
152
153 return result;
154 }
155
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700156 private static long dbInsertAndCheck(DatabaseHelper helper,
157 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
Dan Sandlerd5024042014-01-09 15:01:33 -0500158 if (values == null) {
159 throw new RuntimeException("Error: attempting to insert null values");
160 }
Adam Cohen71483f42014-05-15 14:04:01 -0700161 if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700162 throw new RuntimeException("Error: attempting to add item without specifying an id");
163 }
Chris Wren5dee7af2013-12-20 17:22:11 -0500164 helper.checkId(table, values);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700165 return db.insert(table, nullColumnHack, values);
166 }
167
Adam Cohen228da5a2011-07-27 22:23:47 -0700168 private static void deleteId(SQLiteDatabase db, long id) {
169 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
170 SqlArguments args = new SqlArguments(uri, null, null);
171 db.delete(args.table, args.where, args.args);
172 }
173
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800174 @Override
175 public Uri insert(Uri uri, ContentValues initialValues) {
176 SqlArguments args = new SqlArguments(uri);
177
178 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Chris Wren1ada10d2013-09-13 18:01:38 -0400179 addModifiedTime(initialValues);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700180 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800181 if (rowId <= 0) return null;
182
183 uri = ContentUris.withAppendedId(uri, rowId);
184 sendNotify(uri);
185
186 return uri;
187 }
188
189 @Override
190 public int bulkInsert(Uri uri, ContentValues[] values) {
191 SqlArguments args = new SqlArguments(uri);
192
193 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
194 db.beginTransaction();
195 try {
196 int numValues = values.length;
197 for (int i = 0; i < numValues; i++) {
Chris Wren1ada10d2013-09-13 18:01:38 -0400198 addModifiedTime(values[i]);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700199 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
200 return 0;
201 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800202 }
203 db.setTransactionSuccessful();
204 } finally {
205 db.endTransaction();
206 }
207
208 sendNotify(uri);
209 return values.length;
210 }
211
212 @Override
Yura085c8532014-02-11 15:15:29 +0000213 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
214 throws OperationApplicationException {
215 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
216 db.beginTransaction();
217 try {
218 ContentProviderResult[] result = super.applyBatch(operations);
219 db.setTransactionSuccessful();
220 return result;
221 } finally {
222 db.endTransaction();
223 }
224 }
225
226 @Override
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800227 public int delete(Uri uri, String selection, String[] selectionArgs) {
228 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
229
230 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
231 int count = db.delete(args.table, args.where, args.args);
232 if (count > 0) sendNotify(uri);
233
234 return count;
235 }
236
237 @Override
238 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
239 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
240
Chris Wren1ada10d2013-09-13 18:01:38 -0400241 addModifiedTime(values);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800242 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
243 int count = db.update(args.table, values, args.where, args.args);
244 if (count > 0) sendNotify(uri);
245
246 return count;
247 }
248
249 private void sendNotify(Uri uri) {
250 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
251 if (notify == null || "true".equals(notify)) {
252 getContext().getContentResolver().notifyChange(uri, null);
253 }
Chris Wren1ada10d2013-09-13 18:01:38 -0400254
255 // always notify the backup agent
Chris Wren92aa4232013-10-04 11:29:36 -0400256 LauncherBackupAgentHelper.dataChanged(getContext());
Anjali Koppal67e7cae2014-03-13 12:14:12 -0700257 if (mListener != null) {
258 mListener.onLauncherProviderChange();
259 }
Chris Wren1ada10d2013-09-13 18:01:38 -0400260 }
261
262 private void addModifiedTime(ContentValues values) {
263 values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800264 }
265
Adam Cohendcd297f2013-06-18 13:13:40 -0700266 public long generateNewItemId() {
267 return mOpenHelper.generateNewItemId();
268 }
269
Winson Chungc763c4e2013-07-19 13:49:06 -0700270 public void updateMaxItemId(long id) {
271 mOpenHelper.updateMaxItemId(id);
272 }
273
Adam Cohendcd297f2013-06-18 13:13:40 -0700274 public long generateNewScreenId() {
275 return mOpenHelper.generateNewScreenId();
276 }
277
278 // This is only required one time while loading the workspace during the
279 // upgrade path, and should never be called from anywhere else.
280 public void updateMaxScreenId(long maxScreenId) {
281 mOpenHelper.updateMaxScreenId(maxScreenId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700282 }
283
Brian Muramatsu5524b492012-10-02 16:55:54 -0700284 /**
Adam Cohene25af792013-06-06 23:08:25 -0700285 * @param Should we load the old db for upgrade? first run only.
286 */
Winson Chungc763c4e2013-07-19 13:49:06 -0700287 synchronized public boolean justLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400288 String spKey = LauncherAppState.getSharedPreferencesKey();
Adam Cohene25af792013-06-06 23:08:25 -0700289 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
290
Winson Chungc763c4e2013-07-19 13:49:06 -0700291 boolean loadedOldDb = false || sJustLoadedFromOldDb;
Adam Cohendcd297f2013-06-18 13:13:40 -0700292
Winson Chungc763c4e2013-07-19 13:49:06 -0700293 sJustLoadedFromOldDb = false;
294 if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) {
Adam Cohene25af792013-06-06 23:08:25 -0700295
296 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700297 editor.remove(UPGRADED_FROM_OLD_DATABASE);
Adam Cohene25af792013-06-06 23:08:25 -0700298 editor.commit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700299 loadedOldDb = true;
Adam Cohene25af792013-06-06 23:08:25 -0700300 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700301 return loadedOldDb;
Adam Cohene25af792013-06-06 23:08:25 -0700302 }
303
304 /**
Brian Muramatsu5524b492012-10-02 16:55:54 -0700305 * @param workspaceResId that can be 0 to use default or non-zero for specific resource
306 */
Michael Jurka45355c42012-10-08 13:21:35 +0200307 synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400308 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700309 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
Adam Cohene25af792013-06-06 23:08:25 -0700310
Winson Chungc763c4e2013-07-19 13:49:06 -0700311 if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Chris Wren5dee7af2013-12-20 17:22:11 -0500312 Log.d(TAG, "loading default workspace");
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700313 // By default we use our resources
314 Resources res = getContext().getResources();
Michael Jurka45355c42012-10-08 13:21:35 +0200315 int workspaceResId = origWorkspaceResId;
316
Brian Muramatsu5524b492012-10-02 16:55:54 -0700317 // Use default workspace resource if none provided
318 if (workspaceResId == 0) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700319 final Partner partner = Partner.get(getContext().getPackageManager());
320 if (partner != null && partner.hasDefaultLayout()) {
321 final Resources partnerRes = partner.getResources();
322 workspaceResId = partnerRes.getIdentifier(Partner.RESOURCE_DEFAULT_LAYOUT,
323 "xml", partner.getPackageName());
324 res = partnerRes;
325 }
326 }
327 if (workspaceResId == 0) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800328 workspaceResId =
329 sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
Brian Muramatsu5524b492012-10-02 16:55:54 -0700330 }
331
Michael Jurkab85f8a42012-04-25 15:48:32 -0700332 // Populate favorites table with initial favorites
333 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700334 editor.remove(EMPTY_DATABASE_CREATED);
Michael Jurka45355c42012-10-08 13:21:35 +0200335 if (origWorkspaceResId != 0) {
336 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
337 }
Adam Cohene25af792013-06-06 23:08:25 -0700338
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700339 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), res, workspaceResId);
Michael Jurkab85f8a42012-04-25 15:48:32 -0700340 editor.commit();
341 }
342 }
343
Dan Sandlerd5024042014-01-09 15:01:33 -0500344 public void migrateLauncher2Shortcuts() {
345 mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
Jason Monk0bfcceb2014-03-21 15:42:06 -0400346 Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
Dan Sandlerd5024042014-01-09 15:01:33 -0500347 }
348
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800349 private static int getDefaultWorkspaceResourceId() {
Winson Chungbe876472014-05-14 14:15:20 -0700350 LauncherAppState app = LauncherAppState.getInstance();
351 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Nilesh Agrawal16f3ea82014-01-09 17:14:01 -0800352 if (LauncherAppState.isDisableAllApps()) {
Winson Chungbe876472014-05-14 14:15:20 -0700353 return grid.defaultNoAllAppsLayoutId;
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800354 } else {
Winson Chungbe876472014-05-14 14:15:20 -0700355 return grid.defaultLayoutId;
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800356 }
357 }
358
Winson Chungc763c4e2013-07-19 13:49:06 -0700359 private static interface ContentValuesCallback {
360 public void onRow(ContentValues values);
361 }
362
Adam Cohen6dbe0492013-12-02 17:00:14 -0800363 private static boolean shouldImportLauncher2Database(Context context) {
364 boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet);
365
366 // We don't import the old databse for tablets, as the grid size has changed.
367 return !isTablet && IMPORT_LAUNCHER2_DATABASE;
368 }
369
Dan Sandlerd5024042014-01-09 15:01:33 -0500370 public void deleteDatabase() {
371 // Are you sure? (y/n)
372 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Dan Sandler2b471742014-01-21 14:14:41 -0500373 final File dbFile = new File(db.getPath());
Dan Sandlerd5024042014-01-09 15:01:33 -0500374 mOpenHelper.close();
Dan Sandler2b471742014-01-21 14:14:41 -0500375 if (dbFile.exists()) {
376 SQLiteDatabase.deleteDatabase(dbFile);
377 }
Dan Sandlerd5024042014-01-09 15:01:33 -0500378 mOpenHelper = new DatabaseHelper(getContext());
379 }
380
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800381 private static class DatabaseHelper extends SQLiteOpenHelper {
Jason Monk41314972014-03-03 16:11:30 -0500382 private static final String TAG_RESOLVE = "resolve";
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800383 private static final String TAG_FAVORITES = "favorites";
384 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700385 private static final String TAG_CLOCK = "clock";
386 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700387 private static final String TAG_APPWIDGET = "appwidget";
388 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700389 private static final String TAG_FOLDER = "folder";
Jeff Sharkey5aeef582014-04-14 13:26:42 -0700390 private static final String TAG_PARTNER_FOLDER = "partner-folder";
Winson Chungb3302ae2012-05-01 10:19:14 -0700391 private static final String TAG_EXTRA = "extra";
Daniel Sandler57dac262013-10-03 13:28:36 -0400392 private static final String TAG_INCLUDE = "include";
Winson Chung3d503fb2011-07-13 17:25:49 -0700393
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700394 // Style attrs -- "Favorite"
Jeff Sharkey5aeef582014-04-14 13:26:42 -0700395 private static final String ATTR_CLASS_NAME = "className";
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700396 private static final String ATTR_PACKAGE_NAME = "packageName";
397 private static final String ATTR_CONTAINER = "container";
398 private static final String ATTR_SCREEN = "screen";
399 private static final String ATTR_X = "x";
400 private static final String ATTR_Y = "y";
401 private static final String ATTR_SPAN_X = "spanX";
402 private static final String ATTR_SPAN_Y = "spanY";
403 private static final String ATTR_ICON = "icon";
404 private static final String ATTR_TITLE = "title";
405 private static final String ATTR_URI = "uri";
406
407 // Style attrs -- "Include"
408 private static final String ATTR_WORKSPACE = "workspace";
409
410 // Style attrs -- "Extra"
411 private static final String ATTR_KEY = "key";
412 private static final String ATTR_VALUE = "value";
Jeff Sharkey5aeef582014-04-14 13:26:42 -0700413
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800414 private final Context mContext;
Jeff Sharkey5aeef582014-04-14 13:26:42 -0700415 private final PackageManager mPackageManager;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700416 private final AppWidgetHost mAppWidgetHost;
Adam Cohendcd297f2013-06-18 13:13:40 -0700417 private long mMaxItemId = -1;
418 private long mMaxScreenId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800419
Winson Chung0b560dd2014-01-21 13:00:26 -0800420 private boolean mNewDbCreated = false;
421
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800422 DatabaseHelper(Context context) {
423 super(context, DATABASE_NAME, null, DATABASE_VERSION);
424 mContext = context;
Jeff Sharkey5aeef582014-04-14 13:26:42 -0700425 mPackageManager = context.getPackageManager();
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700426 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700427
428 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
429 // the DB here
Adam Cohendcd297f2013-06-18 13:13:40 -0700430 if (mMaxItemId == -1) {
431 mMaxItemId = initializeMaxItemId(getWritableDatabase());
432 }
433 if (mMaxScreenId == -1) {
434 mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
Winson Chung3d503fb2011-07-13 17:25:49 -0700435 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800436 }
437
Winson Chung0b560dd2014-01-21 13:00:26 -0800438 public boolean wasNewDbCreated() {
439 return mNewDbCreated;
440 }
441
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700442 /**
443 * Send notification that we've deleted the {@link AppWidgetHost},
444 * probably as part of the initial database creation. The receiver may
445 * want to re-call {@link AppWidgetHost#startListening()} to ensure
446 * callbacks are correctly set.
447 */
448 private void sendAppWidgetResetNotify() {
449 final ContentResolver resolver = mContext.getContentResolver();
450 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
451 }
452
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800453 @Override
454 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800455 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700456
Adam Cohendcd297f2013-06-18 13:13:40 -0700457 mMaxItemId = 1;
458 mMaxScreenId = 0;
Winson Chung0b560dd2014-01-21 13:00:26 -0800459 mNewDbCreated = true;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700460
Kenny Guyed131872014-04-30 03:02:21 +0100461 UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
462 long userSerialNumber = userManager.getSerialNumberForUser(
463 UserHandleCompat.myUserHandle());
464
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800465 db.execSQL("CREATE TABLE favorites (" +
466 "_id INTEGER PRIMARY KEY," +
467 "title TEXT," +
468 "intent TEXT," +
469 "container INTEGER," +
470 "screen INTEGER," +
471 "cellX INTEGER," +
472 "cellY INTEGER," +
473 "spanX INTEGER," +
474 "spanY INTEGER," +
475 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700476 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800477 "isShortcut INTEGER," +
478 "iconType INTEGER," +
479 "iconPackage TEXT," +
480 "iconResource TEXT," +
481 "icon BLOB," +
482 "uri TEXT," +
Chris Wrend5e66bf2013-09-16 14:02:29 -0400483 "displayMode INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400484 "appWidgetProvider TEXT," +
Chris Wrenf4d08112014-01-16 18:13:56 -0500485 "modified INTEGER NOT NULL DEFAULT 0," +
Kenny Guyed131872014-04-30 03:02:21 +0100486 "restored INTEGER NOT NULL DEFAULT 0," +
487 "profileId INTEGER DEFAULT " + userSerialNumber +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800488 ");");
Adam Cohendcd297f2013-06-18 13:13:40 -0700489 addWorkspacesTable(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800490
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700491 // Database was just created, so wipe any previous widgets
492 if (mAppWidgetHost != null) {
493 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700494 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800495 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700496
Adam Cohen6dbe0492013-12-02 17:00:14 -0800497 if (shouldImportLauncher2Database(mContext)) {
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500498 // Try converting the old database
499 ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
500 public void onRow(ContentValues values) {
501 int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
502 if (container == Favorites.CONTAINER_DESKTOP) {
503 int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
504 screen = (int) upgradeLauncherDb_permuteScreens(screen);
505 values.put(LauncherSettings.Favorites.SCREEN, screen);
506 }
507 }
508 };
509 Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
510 "/old_favorites?notify=true");
511 if (!convertDatabase(db, uri, permuteScreensCb, true)) {
512 // Try and upgrade from the Launcher2 db
Jason Monk0bfcceb2014-03-21 15:42:06 -0400513 uri = Uri.parse(mContext.getString(R.string.old_launcher_provider_uri));
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500514 if (!convertDatabase(db, uri, permuteScreensCb, false)) {
515 // If we fail, then set a flag to load the default workspace
516 setFlagEmptyDbCreated();
517 return;
Winson Chungc763c4e2013-07-19 13:49:06 -0700518 }
519 }
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500520 // Right now, in non-default workspace cases, we want to run the final
521 // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
522 // set that flag too.
523 setFlagJustLoadedOldDb();
524 } else {
525 // Fresh and clean launcher DB.
526 mMaxItemId = initializeMaxItemId(db);
527 setFlagEmptyDbCreated();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800528 }
529 }
530
Adam Cohendcd297f2013-06-18 13:13:40 -0700531 private void addWorkspacesTable(SQLiteDatabase db) {
532 db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
533 LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400534 LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
535 LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
Adam Cohendcd297f2013-06-18 13:13:40 -0700536 ");");
537 }
538
Adam Cohen119285e2014-04-02 16:59:08 -0700539 private void removeOrphanedItems(SQLiteDatabase db) {
Adam Cohenf9c14de2014-04-17 18:20:45 -0700540 // Delete items directly on the workspace who's screen id doesn't exist
541 // "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
542 // AND container = -100"
543 String removeOrphanedDesktopItems = "DELETE FROM " + TABLE_FAVORITES +
544 " WHERE " +
Adam Cohen119285e2014-04-02 16:59:08 -0700545 LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
Adam Cohenf9c14de2014-04-17 18:20:45 -0700546 LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS + ")" +
547 " AND " +
548 LauncherSettings.Favorites.CONTAINER + " = " +
549 LauncherSettings.Favorites.CONTAINER_DESKTOP;
550 db.execSQL(removeOrphanedDesktopItems);
551
552 // Delete items contained in folders which no longer exist (after above statement)
553 // "DELETE FROM favorites WHERE container <> -100 AND container <> -101 AND container
554 // NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
555 String removeOrphanedFolderItems = "DELETE FROM " + TABLE_FAVORITES +
556 " WHERE " +
557 LauncherSettings.Favorites.CONTAINER + " <> " +
558 LauncherSettings.Favorites.CONTAINER_DESKTOP +
559 " AND "
560 + LauncherSettings.Favorites.CONTAINER + " <> " +
561 LauncherSettings.Favorites.CONTAINER_HOTSEAT +
562 " AND "
563 + LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
564 LauncherSettings.Favorites._ID + " FROM " + TABLE_FAVORITES +
565 " WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
566 LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
567 db.execSQL(removeOrphanedFolderItems);
Adam Cohen119285e2014-04-02 16:59:08 -0700568 }
569
Winson Chungc763c4e2013-07-19 13:49:06 -0700570 private void setFlagJustLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400571 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700572 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
573 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700574 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
575 editor.putBoolean(EMPTY_DATABASE_CREATED, false);
Michael Jurkab85f8a42012-04-25 15:48:32 -0700576 editor.commit();
577 }
578
Winson Chungc763c4e2013-07-19 13:49:06 -0700579 private void setFlagEmptyDbCreated() {
580 String spKey = LauncherAppState.getSharedPreferencesKey();
581 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
582 SharedPreferences.Editor editor = sp.edit();
583 editor.putBoolean(EMPTY_DATABASE_CREATED, true);
584 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
585 editor.commit();
586 }
587
588 // We rearrange the screens from the old launcher
589 // 12345 -> 34512
590 private long upgradeLauncherDb_permuteScreens(long screen) {
591 if (screen >= 2) {
592 return screen - 2;
593 } else {
594 return screen + 3;
595 }
596 }
597
598 private boolean convertDatabase(SQLiteDatabase db, Uri uri,
599 ContentValuesCallback cb, boolean deleteRows) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800600 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800601 boolean converted = false;
602
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800603 final ContentResolver resolver = mContext.getContentResolver();
604 Cursor cursor = null;
605
606 try {
607 cursor = resolver.query(uri, null, null, null, null);
608 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700609 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800610 }
611
612 // We already have a favorites database in the old provider
Winson Chungc763c4e2013-07-19 13:49:06 -0700613 if (cursor != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800614 try {
Winson Chungc763c4e2013-07-19 13:49:06 -0700615 if (cursor.getCount() > 0) {
616 converted = copyFromCursor(db, cursor, cb) > 0;
617 if (converted && deleteRows) {
618 resolver.delete(uri, null, null);
619 }
620 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800621 } finally {
622 cursor.close();
623 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800624 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700625
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800626 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700627 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800628 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800629 convertWidgets(db);
Winson Chungc763c4e2013-07-19 13:49:06 -0700630
631 // Update max item id
632 mMaxItemId = initializeMaxItemId(db);
633 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800634 }
635
636 return converted;
637 }
638
Winson Chungc763c4e2013-07-19 13:49:06 -0700639 private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) {
Romain Guy73b979d2009-06-09 12:57:21 -0700640 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800641 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
642 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
643 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
644 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
645 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
646 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
647 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
648 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
649 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
650 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
651 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
652 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
653 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
654
655 ContentValues[] rows = new ContentValues[c.getCount()];
656 int i = 0;
657 while (c.moveToNext()) {
658 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700659 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800660 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
661 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
662 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
663 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
664 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
665 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
666 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
667 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700668 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800669 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
670 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
671 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
672 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
673 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
Winson Chungc763c4e2013-07-19 13:49:06 -0700674 if (cb != null) {
675 cb.onRow(values);
676 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800677 rows[i++] = values;
678 }
679
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800680 int total = 0;
Winson Chungc763c4e2013-07-19 13:49:06 -0700681 if (i > 0) {
682 db.beginTransaction();
683 try {
684 int numValues = rows.length;
685 for (i = 0; i < numValues; i++) {
686 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
687 return 0;
688 } else {
689 total++;
690 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800691 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700692 db.setTransactionSuccessful();
693 } finally {
694 db.endTransaction();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800695 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800696 }
697
698 return total;
699 }
700
701 @Override
702 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Winson Chungc763c4e2013-07-19 13:49:06 -0700703 if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700704
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800705 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700706 if (version < 3) {
707 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800708 db.beginTransaction();
709 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700710 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800711 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700712 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800713 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700714 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800715 } catch (SQLException ex) {
716 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800717 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800718 } finally {
719 db.endTransaction();
720 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700721
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800722 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700723 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800724 convertWidgets(db);
725 }
726 }
Romain Guy73b979d2009-06-09 12:57:21 -0700727
728 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800729 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700730 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700731
Romain Guy509cd6a2010-03-23 15:10:56 -0700732 // Where's version 5?
733 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
Daniel Sandler325dc232013-06-05 22:57:57 -0400734 // - Passion shipped on 2.1 with version 6 of launcher3
Romain Guy509cd6a2010-03-23 15:10:56 -0700735 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
736 // but version 5 on there was the updateContactsShortcuts change
737 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
738 // The updateContactsShortcuts change is idempotent, so running it twice
739 // is okay so we'll do that when upgrading the devices that shipped with it.
740 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800741 // We went from 3 to 5 screens. Move everything 1 to the right
742 db.beginTransaction();
743 try {
744 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
745 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800746 } catch (SQLException ex) {
747 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800748 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800749 } finally {
750 db.endTransaction();
751 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700752
Romain Guy509cd6a2010-03-23 15:10:56 -0700753 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800754 if (updateContactsShortcuts(db)) {
755 version = 6;
756 }
757 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000758
759 if (version < 7) {
760 // Version 7 gets rid of the special search widget.
761 convertWidgets(db);
762 version = 7;
763 }
764
Joe Onorato0589f0f2010-02-08 13:44:00 -0800765 if (version < 8) {
766 // Version 8 (froyo) has the icons all normalized. This should
767 // already be the case in practice, but we now rely on it and don't
768 // resample the images each time.
769 normalizeIcons(db);
770 version = 8;
771 }
772
Winson Chung3d503fb2011-07-13 17:25:49 -0700773 if (version < 9) {
774 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
775 // 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 -0700776 if (mMaxItemId == -1) {
777 mMaxItemId = initializeMaxItemId(db);
Winson Chung3d503fb2011-07-13 17:25:49 -0700778 }
779
780 // Add default hotseat icons
Adam Cohen9b8f51f2014-05-30 15:34:09 -0700781 loadFavorites(db, mContext.getResources(), R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700782 version = 9;
783 }
784
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700785 // We bumped the version three time during JB, once to update the launch flags, once to
786 // update the override for the default launch animation and once to set the mimetype
787 // to improve startup performance
788 if (version < 12) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700789 // Contact shortcuts need a different set of flags to be launched now
790 // The updateContactsShortcuts change is idempotent, so we can keep using it like
791 // back in the Donut days
792 updateContactsShortcuts(db);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700793 version = 12;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700794 }
795
Adam Cohendcd297f2013-06-18 13:13:40 -0700796 if (version < 13) {
797 // With the new shrink-wrapped and re-orderable workspaces, it makes sense
798 // to persist workspace screens and their relative order.
799 mMaxScreenId = 0;
800
801 // This will never happen in the wild, but when we switch to using workspace
802 // screen ids, redo the import from old launcher.
Winson Chungc763c4e2013-07-19 13:49:06 -0700803 sJustLoadedFromOldDb = true;
Adam Cohendcd297f2013-06-18 13:13:40 -0700804
805 addWorkspacesTable(db);
806 version = 13;
807 }
808
Chris Wrend5e66bf2013-09-16 14:02:29 -0400809 if (version < 14) {
810 db.beginTransaction();
811 try {
812 // Insert new column for holding widget provider name
813 db.execSQL("ALTER TABLE favorites " +
814 "ADD COLUMN appWidgetProvider TEXT;");
815 db.setTransactionSuccessful();
816 version = 14;
817 } catch (SQLException ex) {
818 // Old version remains, which means we wipe old data
819 Log.e(TAG, ex.getMessage(), ex);
820 } finally {
821 db.endTransaction();
822 }
823 }
824
Chris Wren1ada10d2013-09-13 18:01:38 -0400825 if (version < 15) {
826 db.beginTransaction();
827 try {
828 // Insert new column for holding update timestamp
829 db.execSQL("ALTER TABLE favorites " +
830 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
831 db.execSQL("ALTER TABLE workspaceScreens " +
832 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
833 db.setTransactionSuccessful();
834 version = 15;
835 } catch (SQLException ex) {
836 // Old version remains, which means we wipe old data
837 Log.e(TAG, ex.getMessage(), ex);
838 } finally {
839 db.endTransaction();
840 }
841 }
842
Chris Wrenf4d08112014-01-16 18:13:56 -0500843
844 if (version < 16) {
845 db.beginTransaction();
846 try {
847 // Insert new column for holding restore status
848 db.execSQL("ALTER TABLE favorites " +
849 "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;");
850 db.setTransactionSuccessful();
851 version = 16;
852 } catch (SQLException ex) {
853 // Old version remains, which means we wipe old data
854 Log.e(TAG, ex.getMessage(), ex);
855 } finally {
856 db.endTransaction();
857 }
858 }
859
Adam Cohen71e03b92014-02-21 14:09:53 -0800860 if (version < 17) {
861 // We use the db version upgrade here to identify users who may not have seen
862 // clings yet (because they weren't available), but for whom the clings are now
863 // available (tablet users). Because one of the possible cling flows (migration)
864 // is very destructive (wipes out workspaces), we want to prevent this from showing
865 // until clear data. We do so by marking that the clings have been shown.
866 LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext);
867 version = 17;
868 }
869
Adam Cohen119285e2014-04-02 16:59:08 -0700870 if (version < 18) {
Adam Cohenf9c14de2014-04-17 18:20:45 -0700871 // No-op
872 version = 18;
873 }
874
875 if (version < 19) {
Adam Cohen119285e2014-04-02 16:59:08 -0700876 // Due to a data loss bug, some users may have items associated with screen ids
877 // which no longer exist. Since this can cause other problems, and since the user
878 // will never see these items anyway, we use database upgrade as an opportunity to
879 // clean things up.
Adam Cohenf9c14de2014-04-17 18:20:45 -0700880 removeOrphanedItems(db);
881 version = 19;
Adam Cohen119285e2014-04-02 16:59:08 -0700882 }
883
Kenny Guyed131872014-04-30 03:02:21 +0100884 if (version < 20) {
885 // Add userId column
886 if (addProfileColumn(db)) {
887 version = 20;
888 }
889 // else old version remains, which means we wipe old data
890 }
891
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800892 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800893 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800894 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
Adam Cohendcd297f2013-06-18 13:13:40 -0700895 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
896
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800897 onCreate(db);
898 }
899 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800900
Adam Cohen9b1d0622014-05-21 19:01:57 -0700901 @Override
902 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
903 // This shouldn't happen -- throw our hands up in the air and start over.
904 Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
905 ". Wiping databse.");
906
907 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
908 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
909 onCreate(db);
910 }
911
Kenny Guyed131872014-04-30 03:02:21 +0100912 private boolean addProfileColumn(SQLiteDatabase db) {
913 db.beginTransaction();
914 try {
915 UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
916 // Default to the serial number of this user, for older
917 // shortcuts.
918 long userSerialNumber = userManager.getSerialNumberForUser(
919 UserHandleCompat.myUserHandle());
920 // Insert new column for holding user serial number
921 db.execSQL("ALTER TABLE favorites " +
922 "ADD COLUMN profileId INTEGER DEFAULT "
923 + userSerialNumber + ";");
924 db.setTransactionSuccessful();
925 } catch (SQLException ex) {
926 // Old version remains, which means we wipe old data
927 Log.e(TAG, ex.getMessage(), ex);
928 return false;
929 } finally {
930 db.endTransaction();
931 }
932 return true;
933 }
934
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800935 private boolean updateContactsShortcuts(SQLiteDatabase db) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800936 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
937 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
938
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700939 Cursor c = null;
940 final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800941 db.beginTransaction();
942 try {
943 // Select and iterate through each matching widget
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700944 c = db.query(TABLE_FAVORITES,
945 new String[] { Favorites._ID, Favorites.INTENT },
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800946 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700947 if (c == null) return false;
948
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800949 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700950
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800951 final int idIndex = c.getColumnIndex(Favorites._ID);
952 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700953
954 while (c.moveToNext()) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800955 long favoriteId = c.getLong(idIndex);
956 final String intentUri = c.getString(intentIndex);
957 if (intentUri != null) {
958 try {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700959 final Intent intent = Intent.parseUri(intentUri, 0);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800960 android.util.Log.d("Home", intent.toString());
961 final Uri uri = intent.getData();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700962 if (uri != null) {
963 final String data = uri.toString();
964 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
965 actionQuickContact.equals(intent.getAction())) &&
966 (data.startsWith("content://contacts/people/") ||
967 data.startsWith("content://com.android.contacts/" +
968 "contacts/lookup/"))) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800969
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700970 final Intent newIntent = new Intent(actionQuickContact);
971 // When starting from the launcher, start in a new, cleared task
972 // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
973 // clear the whole thing preemptively here since
974 // QuickContactActivity will finish itself when launching other
975 // detail activities.
976 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
977 Intent.FLAG_ACTIVITY_CLEAR_TASK);
Winson Chung2672ff92012-05-04 16:22:30 -0700978 newIntent.putExtra(
979 Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700980 newIntent.setData(uri);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700981 // Determine the type and also put that in the shortcut
982 // (that can speed up launch a bit)
983 newIntent.setDataAndType(uri, newIntent.resolveType(mContext));
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800984
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700985 final ContentValues values = new ContentValues();
986 values.put(LauncherSettings.Favorites.INTENT,
987 newIntent.toUri(0));
988
989 String updateWhere = Favorites._ID + "=" + favoriteId;
990 db.update(TABLE_FAVORITES, values, updateWhere, null);
991 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800992 }
993 } catch (RuntimeException ex) {
994 Log.e(TAG, "Problem upgrading shortcut", ex);
995 } catch (URISyntaxException e) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700996 Log.e(TAG, "Problem upgrading shortcut", e);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800997 }
998 }
999 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001000
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001001 db.setTransactionSuccessful();
1002 } catch (SQLException ex) {
1003 Log.w(TAG, "Problem while upgrading contacts", ex);
1004 return false;
1005 } finally {
1006 db.endTransaction();
1007 if (c != null) {
1008 c.close();
1009 }
1010 }
1011
1012 return true;
1013 }
1014
Joe Onorato0589f0f2010-02-08 13:44:00 -08001015 private void normalizeIcons(SQLiteDatabase db) {
1016 Log.d(TAG, "normalizing icons");
1017
Joe Onorato346e1292010-02-18 10:34:24 -05001018 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001019 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -04001020 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001021 try {
1022 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -04001023 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -06001024 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -08001025
1026 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
1027 Favorites.ICON_TYPE_BITMAP, null);
1028
1029 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
1030 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
1031
1032 while (c.moveToNext()) {
1033 long id = c.getLong(idIndex);
1034 byte[] data = c.getBlob(iconIndex);
1035 try {
1036 Bitmap bitmap = Utilities.resampleIconBitmap(
1037 BitmapFactory.decodeByteArray(data, 0, data.length),
1038 mContext);
1039 if (bitmap != null) {
1040 update.bindLong(1, id);
1041 data = ItemInfo.flattenBitmap(bitmap);
1042 if (data != null) {
1043 update.bindBlob(2, data);
1044 update.execute();
1045 }
1046 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001047 }
1048 } catch (Exception e) {
1049 if (!logged) {
1050 Log.e(TAG, "Failed normalizing icon " + id, e);
1051 } else {
1052 Log.e(TAG, "Also failed normalizing icon " + id);
1053 }
1054 logged = true;
1055 }
1056 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +00001057 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -08001058 } catch (SQLException ex) {
1059 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
1060 } finally {
1061 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -04001062 if (update != null) {
1063 update.close();
1064 }
Joe Onorato0589f0f2010-02-08 13:44:00 -08001065 if (c != null) {
1066 c.close();
1067 }
1068 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001069 }
1070
1071 // Generates a new ID to use for an object in your database. This method should be only
1072 // called from the main UI thread. As an exception, we do call it when we call the
1073 // constructor from the worker thread; however, this doesn't extend until after the
1074 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
1075 // after that point
Adam Cohendcd297f2013-06-18 13:13:40 -07001076 public long generateNewItemId() {
1077 if (mMaxItemId < 0) {
1078 throw new RuntimeException("Error: max item id was not initialized");
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001079 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001080 mMaxItemId += 1;
1081 return mMaxItemId;
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001082 }
1083
Winson Chungc763c4e2013-07-19 13:49:06 -07001084 public void updateMaxItemId(long id) {
1085 mMaxItemId = id + 1;
1086 }
1087
Chris Wren5dee7af2013-12-20 17:22:11 -05001088 public void checkId(String table, ContentValues values) {
1089 long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
1090 if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
1091 mMaxScreenId = Math.max(id, mMaxScreenId);
1092 } else {
1093 mMaxItemId = Math.max(id, mMaxItemId);
1094 }
1095 }
1096
Adam Cohendcd297f2013-06-18 13:13:40 -07001097 private long initializeMaxItemId(SQLiteDatabase db) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001098 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
1099
1100 // get the result
1101 final int maxIdIndex = 0;
1102 long id = -1;
1103 if (c != null && c.moveToNext()) {
1104 id = c.getLong(maxIdIndex);
1105 }
Michael Jurka5130e402011-10-13 04:55:35 -07001106 if (c != null) {
1107 c.close();
1108 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001109
1110 if (id == -1) {
Adam Cohendcd297f2013-06-18 13:13:40 -07001111 throw new RuntimeException("Error: could not query max item id");
1112 }
1113
1114 return id;
1115 }
1116
1117 // Generates a new ID to use for an workspace screen in your database. This method
1118 // should be only called from the main UI thread. As an exception, we do call it when we
1119 // call the constructor from the worker thread; however, this doesn't extend until after the
1120 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
1121 // after that point
1122 public long generateNewScreenId() {
1123 if (mMaxScreenId < 0) {
1124 throw new RuntimeException("Error: max screen id was not initialized");
1125 }
1126 mMaxScreenId += 1;
Winson Chunga90303b2013-11-15 13:05:06 -08001127 // Log to disk
1128 Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -07001129 return mMaxScreenId;
1130 }
1131
1132 public void updateMaxScreenId(long maxScreenId) {
Winson Chunga90303b2013-11-15 13:05:06 -08001133 // Log to disk
1134 Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -07001135 mMaxScreenId = maxScreenId;
1136 }
1137
1138 private long initializeMaxScreenId(SQLiteDatabase db) {
1139 Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
1140
1141 // get the result
1142 final int maxIdIndex = 0;
1143 long id = -1;
1144 if (c != null && c.moveToNext()) {
1145 id = c.getLong(maxIdIndex);
1146 }
1147 if (c != null) {
1148 c.close();
1149 }
1150
1151 if (id == -1) {
1152 throw new RuntimeException("Error: could not query max screen id");
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001153 }
1154
Winson Chunga90303b2013-11-15 13:05:06 -08001155 // Log to disk
1156 Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001157 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -08001158 }
1159
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001160 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001161 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +00001162 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001163 */
1164 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +00001165 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001166 final int[] bindSources = new int[] {
1167 Favorites.ITEM_TYPE_WIDGET_CLOCK,
1168 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +00001169 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001170 };
Bjorn Bringert7984c942009-12-09 15:38:25 +00001171
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001172 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001173
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001174 Cursor c = null;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001175
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001176 db.beginTransaction();
1177 try {
1178 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +00001179 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001180 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001181
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001182 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001183
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001184 final ContentValues values = new ContentValues();
1185 while (c != null && c.moveToNext()) {
1186 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +00001187 int favoriteType = c.getInt(1);
1188
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001189 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001190 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001191 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001192
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001193 if (LOGD) {
1194 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
1195 + " for favoriteId=" + favoriteId);
1196 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001197 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +00001198 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
1199 values.put(Favorites.APPWIDGET_ID, appWidgetId);
1200
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001201 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +00001202 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
1203 values.put(LauncherSettings.Favorites.SPANX, 4);
1204 values.put(LauncherSettings.Favorites.SPANY, 1);
1205 } else {
1206 values.put(LauncherSettings.Favorites.SPANX, 2);
1207 values.put(LauncherSettings.Favorites.SPANY, 2);
1208 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001209
1210 String updateWhere = Favorites._ID + "=" + favoriteId;
1211 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +00001212
Bjorn Bringert34251342009-12-15 13:33:11 +00001213 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001214 // TODO: check return value
1215 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001216 new ComponentName("com.android.alarmclock",
1217 "com.android.alarmclock.AnalogAppWidgetProvider"));
1218 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001219 // TODO: check return value
1220 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001221 new ComponentName("com.android.camera",
1222 "com.android.camera.PhotoAppWidgetProvider"));
1223 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001224 // TODO: check return value
1225 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001226 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +00001227 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001228 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001229 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001230 }
1231 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001232
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001233 db.setTransactionSuccessful();
1234 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001235 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001236 } finally {
1237 db.endTransaction();
1238 if (c != null) {
1239 c.close();
1240 }
1241 }
Winson Chungc763c4e2013-07-19 13:49:06 -07001242
1243 // Update max item id
1244 mMaxItemId = initializeMaxItemId(db);
1245 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001246 }
1247
Michael Jurka8b805b12012-04-18 14:23:14 -07001248 private static final void beginDocument(XmlPullParser parser, String firstElementName)
1249 throws XmlPullParserException, IOException {
1250 int type;
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001251 while ((type = parser.next()) != XmlPullParser.START_TAG
1252 && type != XmlPullParser.END_DOCUMENT) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001253 ;
1254 }
1255
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001256 if (type != XmlPullParser.START_TAG) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001257 throw new XmlPullParserException("No start tag found");
1258 }
1259
1260 if (!parser.getName().equals(firstElementName)) {
1261 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1262 ", expected " + firstElementName);
1263 }
1264 }
1265
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001266 private static Intent buildMainIntent() {
1267 Intent intent = new Intent(Intent.ACTION_MAIN, null);
1268 intent.addCategory(Intent.CATEGORY_LAUNCHER);
1269 return intent;
1270 }
1271
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001272 private int loadFavorites(SQLiteDatabase db, Resources res, int workspaceResourceId) {
Adam Cohen71483f42014-05-15 14:04:01 -07001273 ArrayList<Long> screenIds = new ArrayList<Long>();
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001274 int count = loadFavoritesRecursive(db, res, workspaceResourceId, screenIds);
Adam Cohen71483f42014-05-15 14:04:01 -07001275
1276 // Add the screens specified by the items above
1277 Collections.sort(screenIds);
1278 int rank = 0;
1279 ContentValues values = new ContentValues();
1280 for (Long id : screenIds) {
1281 values.clear();
1282 values.put(LauncherSettings.WorkspaceScreens._ID, id);
1283 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
1284 if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values) < 0) {
1285 throw new RuntimeException("Failed initialize screen table"
1286 + "from default layout");
1287 }
1288 rank++;
1289 }
1290
1291 // Ensure that the max ids are initialized
1292 mMaxItemId = initializeMaxItemId(db);
1293 mMaxScreenId = initializeMaxScreenId(db);
1294 return count;
1295 }
1296
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001297 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001298 * Loads the default set of favorite packages from an xml file.
1299 *
1300 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -07001301 * @param filterContainerId The specific container id of items to load
Adam Cohen71483f42014-05-15 14:04:01 -07001302 * @param the set of screenIds which are used by the favorites
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001303 */
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001304 private int loadFavoritesRecursive(SQLiteDatabase db, Resources res, int workspaceResourceId,
Adam Cohen71483f42014-05-15 14:04:01 -07001305 ArrayList<Long> screenIds) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001306
Adam Cohen71483f42014-05-15 14:04:01 -07001307 ContentValues values = new ContentValues();
Daniel Sandler57dac262013-10-03 13:28:36 -04001308 if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
1309
Adam Cohen71483f42014-05-15 14:04:01 -07001310 int count = 0;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001311 try {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001312 XmlResourceParser parser = res.getXml(workspaceResourceId);
Michael Jurka8b805b12012-04-18 14:23:14 -07001313 beginDocument(parser, TAG_FAVORITES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001314
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001315 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001316
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001317 int type;
1318 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1319 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
1320
1321 if (type != XmlPullParser.START_TAG) {
1322 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001323 }
1324
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001325 boolean added = false;
1326 final String name = parser.getName();
1327
Daniel Sandler57dac262013-10-03 13:28:36 -04001328 if (TAG_INCLUDE.equals(name)) {
Daniel Sandler57dac262013-10-03 13:28:36 -04001329
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001330 final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
Daniel Sandler57dac262013-10-03 13:28:36 -04001331
1332 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
1333 "", resId));
1334
1335 if (resId != 0 && resId != workspaceResourceId) {
1336 // recursively load some more favorites, why not?
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001337 count += loadFavoritesRecursive(db, res, resId, screenIds);
Daniel Sandler57dac262013-10-03 13:28:36 -04001338 added = false;
Daniel Sandler57dac262013-10-03 13:28:36 -04001339 } else {
1340 Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
1341 }
1342
Daniel Sandler57dac262013-10-03 13:28:36 -04001343 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
1344 continue;
1345 }
1346
1347 // Assuming it's a <favorite> at this point
Winson Chung3d503fb2011-07-13 17:25:49 -07001348 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001349 String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
1350 if (strContainer != null) {
1351 container = Long.valueOf(strContainer);
Winson Chung3d503fb2011-07-13 17:25:49 -07001352 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001353
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001354 String screen = getAttributeValue(parser, ATTR_SCREEN);
1355 String x = getAttributeValue(parser, ATTR_X);
1356 String y = getAttributeValue(parser, ATTR_Y);
Winson Chung6d092682011-11-16 18:43:26 -08001357
Winson Chung6d092682011-11-16 18:43:26 -08001358 values.clear();
1359 values.put(LauncherSettings.Favorites.CONTAINER, container);
1360 values.put(LauncherSettings.Favorites.SCREEN, screen);
1361 values.put(LauncherSettings.Favorites.CELLX, x);
1362 values.put(LauncherSettings.Favorites.CELLY, y);
1363
Daniel Sandler57dac262013-10-03 13:28:36 -04001364 if (LOGD) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001365 final String title = getAttributeValue(parser, ATTR_TITLE);
1366 final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
Daniel Sandler57dac262013-10-03 13:28:36 -04001367 final String something = title != null ? title : pkg;
1368 Log.v(TAG, String.format(
1369 ("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
1370 "", name,
1371 (something == null ? "" : (" \"" + something + "\"")),
1372 container, screen, x, y));
1373 }
1374
Winson Chung6d092682011-11-16 18:43:26 -08001375 if (TAG_FAVORITE.equals(name)) {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001376 long id = addAppShortcut(db, values, parser);
Winson Chung6d092682011-11-16 18:43:26 -08001377 added = id >= 0;
Winson Chung6d092682011-11-16 18:43:26 -08001378 } else if (TAG_APPWIDGET.equals(name)) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001379 added = addAppWidget(parser, type, db, values);
Winson Chung6d092682011-11-16 18:43:26 -08001380 } else if (TAG_SHORTCUT.equals(name)) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001381 long id = addUriShortcut(db, values, res, parser);
Winson Chung6d092682011-11-16 18:43:26 -08001382 added = id >= 0;
Jason Monk41314972014-03-03 16:11:30 -05001383 } else if (TAG_RESOLVE.equals(name)) {
1384 // This looks through the contained favorites (or meta-favorites) and
1385 // attempts to add them as shortcuts in the fallback group's location
1386 // until one is added successfully.
1387 added = false;
1388 final int groupDepth = parser.getDepth();
1389 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1390 parser.getDepth() > groupDepth) {
1391 if (type != XmlPullParser.START_TAG) {
1392 continue;
1393 }
1394 final String fallback_item_name = parser.getName();
Jason Monk41314972014-03-03 16:11:30 -05001395 if (!added) {
1396 if (TAG_FAVORITE.equals(fallback_item_name)) {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001397 final long id = addAppShortcut(db, values, parser);
Jason Monk41314972014-03-03 16:11:30 -05001398 added = id >= 0;
1399 } else {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001400 Log.e(TAG, "Fallback groups can contain only favorites, found "
1401 + fallback_item_name);
Jason Monk41314972014-03-03 16:11:30 -05001402 }
1403 }
Jason Monk41314972014-03-03 16:11:30 -05001404 }
Winson Chung6d092682011-11-16 18:43:26 -08001405 } else if (TAG_FOLDER.equals(name)) {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001406 // Folder contents are nested in this XML file
1407 added = loadFolder(db, values, mContext.getResources(), parser);
Winson Chung3d503fb2011-07-13 17:25:49 -07001408
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001409 } else if (TAG_PARTNER_FOLDER.equals(name)) {
1410 // Folder contents come from an external XML resource
1411 final Partner partner = Partner.get(mPackageManager);
1412 if (partner != null) {
1413 final Resources partnerRes = partner.getResources();
1414 final int resId = partnerRes.getIdentifier(Partner.RESOURCE_FOLDER,
1415 "xml", partner.getPackageName());
1416 if (resId != 0) {
1417 final XmlResourceParser partnerParser = partnerRes.getXml(resId);
1418 beginDocument(partnerParser, TAG_FOLDER);
1419 added = loadFolder(db, values, partnerRes, partnerParser);
Winson Chung6d092682011-11-16 18:43:26 -08001420 }
Winson Chung3d503fb2011-07-13 17:25:49 -07001421 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001422 }
Adam Cohen71483f42014-05-15 14:04:01 -07001423 if (added) {
1424 long screenId = Long.parseLong(screen);
1425 // Keep track of the set of screens which need to be added to the db.
1426 if (!screenIds.contains(screenId) &&
1427 container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1428 screenIds.add(screenId);
1429 }
1430 count++;
1431 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001432 }
1433 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001434 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001435 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001436 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -07001437 } catch (RuntimeException e) {
1438 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001439 }
Adam Cohen71483f42014-05-15 14:04:01 -07001440 return count;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001441 }
1442
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001443 /**
1444 * Parse folder starting at current {@link XmlPullParser} location.
1445 */
1446 private boolean loadFolder(SQLiteDatabase db, ContentValues values, Resources res,
1447 XmlResourceParser parser) throws IOException, XmlPullParserException {
1448 final String title;
1449 final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
1450 if (titleResId != 0) {
1451 title = res.getString(titleResId);
1452 } else {
1453 title = mContext.getResources().getString(R.string.folder_name);
1454 }
1455
1456 values.put(LauncherSettings.Favorites.TITLE, title);
1457 long folderId = addFolder(db, values);
1458 boolean added = folderId >= 0;
1459
1460 ArrayList<Long> folderItems = new ArrayList<Long>();
1461
1462 int type;
1463 int folderDepth = parser.getDepth();
1464 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1465 parser.getDepth() > folderDepth) {
1466 if (type != XmlPullParser.START_TAG) {
1467 continue;
1468 }
1469 final String tag = parser.getName();
1470
1471 final ContentValues childValues = new ContentValues();
1472 childValues.put(LauncherSettings.Favorites.CONTAINER, folderId);
1473
1474 if (LOGD) {
1475 final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
1476 final String uri = getAttributeValue(parser, ATTR_URI);
1477 Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
1478 tag, uri != null ? uri : pkg));
1479 }
1480
1481 if (TAG_FAVORITE.equals(tag) && folderId >= 0) {
1482 final long id = addAppShortcut(db, childValues, parser);
1483 if (id >= 0) {
1484 folderItems.add(id);
1485 }
1486 } else if (TAG_SHORTCUT.equals(tag) && folderId >= 0) {
1487 final long id = addUriShortcut(db, childValues, res, parser);
1488 if (id >= 0) {
1489 folderItems.add(id);
1490 }
1491 } else {
1492 throw new RuntimeException("Folders can contain only shortcuts");
1493 }
1494 }
1495
1496 // We can only have folders with >= 2 items, so we need to remove the
1497 // folder and clean up if less than 2 items were included, or some
1498 // failed to add, and less than 2 were actually added
1499 if (folderItems.size() < 2 && folderId >= 0) {
1500 // Delete the folder
1501 deleteId(db, folderId);
1502
1503 // If we have a single item, promote it to where the folder
1504 // would have been.
1505 if (folderItems.size() == 1) {
1506 final ContentValues childValues = new ContentValues();
1507 copyInteger(values, childValues, LauncherSettings.Favorites.CONTAINER);
1508 copyInteger(values, childValues, LauncherSettings.Favorites.SCREEN);
1509 copyInteger(values, childValues, LauncherSettings.Favorites.CELLX);
1510 copyInteger(values, childValues, LauncherSettings.Favorites.CELLY);
1511
1512 final long id = folderItems.get(0);
1513 db.update(TABLE_FAVORITES, childValues,
1514 LauncherSettings.Favorites._ID + "=" + id, null);
1515 } else {
1516 added = false;
1517 }
1518 }
1519 return added;
1520 }
1521
Jason Monk41314972014-03-03 16:11:30 -05001522 // A meta shortcut attempts to resolve an intent specified as a URI in the XML, if a
1523 // logical choice for what shortcut should be used for that intent exists, then it is
1524 // added. Otherwise add nothing.
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001525 private long addAppShortcutByUri(SQLiteDatabase db, ContentValues values,
1526 String intentUri) {
Jason Monk41314972014-03-03 16:11:30 -05001527 Intent metaIntent;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001528 try {
Jason Monk41314972014-03-03 16:11:30 -05001529 metaIntent = Intent.parseUri(intentUri, 0);
1530 } catch (URISyntaxException e) {
1531 Log.e(TAG, "Unable to add meta-favorite: " + intentUri, e);
1532 return -1;
1533 }
1534
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001535 ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
Jason Monk41314972014-03-03 16:11:30 -05001536 PackageManager.MATCH_DEFAULT_ONLY);
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001537 final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
Jason Monk41314972014-03-03 16:11:30 -05001538 metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
1539
1540 // Verify that the result is an app and not just the resolver dialog asking which
1541 // app to use.
1542 if (wouldLaunchResolverActivity(resolved, appList)) {
1543 // If only one of the results is a system app then choose that as the default.
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001544 final ResolveInfo systemApp = getSingleSystemActivity(appList);
Jason Monk41314972014-03-03 16:11:30 -05001545 if (systemApp == null) {
1546 // There is no logical choice for this meta-favorite, so rather than making
1547 // a bad choice just add nothing.
1548 Log.w(TAG, "No preference or single system activity found for "
1549 + metaIntent.toString());
Adam Cohen228da5a2011-07-27 22:23:47 -07001550 return -1;
1551 }
Jason Monk41314972014-03-03 16:11:30 -05001552 resolved = systemApp;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001553 }
Jason Monk41314972014-03-03 16:11:30 -05001554 final ActivityInfo info = resolved.activityInfo;
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001555 final Intent intent = buildMainIntent();
Jason Monk41314972014-03-03 16:11:30 -05001556 intent.setComponent(new ComponentName(info.packageName, info.name));
1557 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1558 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1559
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001560 return addAppShortcut(db, values, info.loadLabel(mPackageManager).toString(), intent);
Jason Monk41314972014-03-03 16:11:30 -05001561 }
1562
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001563 private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
Jason Monk41314972014-03-03 16:11:30 -05001564 ResolveInfo systemResolve = null;
1565 final int N = appList.size();
1566 for (int i = 0; i < N; ++i) {
1567 try {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001568 ApplicationInfo info = mPackageManager.getApplicationInfo(
Jason Monk41314972014-03-03 16:11:30 -05001569 appList.get(i).activityInfo.packageName, 0);
1570 if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1571 if (systemResolve != null) {
1572 return null;
1573 } else {
1574 systemResolve = appList.get(i);
1575 }
1576 }
1577 } catch (PackageManager.NameNotFoundException e) {
1578 Log.w(TAG, "Unable to get info about resolve results", e);
1579 return null;
1580 }
1581 }
1582 return systemResolve;
1583 }
1584
1585 private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
1586 List<ResolveInfo> appList) {
1587 // If the list contains the above resolved activity, then it can't be
1588 // ResolverActivity itself.
1589 for (int i = 0; i < appList.size(); ++i) {
1590 ResolveInfo tmp = appList.get(i);
1591 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
1592 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
1593 return false;
1594 }
1595 }
1596 return true;
1597 }
1598
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001599 private long addAppShortcut(SQLiteDatabase db, ContentValues values,
1600 XmlResourceParser parser) {
1601 final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
1602 final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
1603 final String uri = getAttributeValue(parser, ATTR_URI);
1604
1605 if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
Jason Monk41314972014-03-03 16:11:30 -05001606 ActivityInfo info;
Jason Monk41314972014-03-03 16:11:30 -05001607 try {
1608 ComponentName cn;
1609 try {
1610 cn = new ComponentName(packageName, className);
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001611 info = mPackageManager.getActivityInfo(cn, 0);
Jason Monk41314972014-03-03 16:11:30 -05001612 } catch (PackageManager.NameNotFoundException nnfe) {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001613 String[] packages = mPackageManager.currentToCanonicalPackageNames(
Jason Monk41314972014-03-03 16:11:30 -05001614 new String[] { packageName });
1615 cn = new ComponentName(packages[0], className);
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001616 info = mPackageManager.getActivityInfo(cn, 0);
Jason Monk41314972014-03-03 16:11:30 -05001617 }
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001618 final Intent intent = buildMainIntent();
Jason Monk41314972014-03-03 16:11:30 -05001619 intent.setComponent(cn);
1620 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1621 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
1622
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001623 return addAppShortcut(db, values, info.loadLabel(mPackageManager).toString(),
Jason Monk41314972014-03-03 16:11:30 -05001624 intent);
1625 } catch (PackageManager.NameNotFoundException e) {
1626 Log.w(TAG, "Unable to add favorite: " + packageName +
1627 "/" + className, e);
1628 }
1629 return -1;
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001630 } else if (!TextUtils.isEmpty(uri)) {
Jason Monk41314972014-03-03 16:11:30 -05001631 // If no component specified try to find a shortcut to add from the URI.
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001632 return addAppShortcutByUri(db, values, uri);
Jason Monk41314972014-03-03 16:11:30 -05001633 } else {
1634 Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
1635 return -1;
1636 }
1637 }
1638
1639 private long addAppShortcut(SQLiteDatabase db, ContentValues values, String title,
1640 Intent intent) {
1641 long id = generateNewItemId();
1642 values.put(Favorites.INTENT, intent.toUri(0));
1643 values.put(Favorites.TITLE, title);
1644 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
1645 values.put(Favorites.SPANX, 1);
1646 values.put(Favorites.SPANY, 1);
1647 values.put(Favorites._ID, id);
1648 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1649 return -1;
1650 } else {
1651 return id;
1652 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001653 }
1654
1655 private long addFolder(SQLiteDatabase db, ContentValues values) {
1656 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
1657 values.put(Favorites.SPANX, 1);
1658 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001659 long id = generateNewItemId();
Adam Cohen228da5a2011-07-27 22:23:47 -07001660 values.put(Favorites._ID, id);
1661 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
1662 return -1;
1663 } else {
1664 return id;
1665 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001666 }
1667
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001668 private ComponentName getSearchWidgetProvider() {
1669 SearchManager searchManager =
1670 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
1671 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
1672 if (searchComponent == null) return null;
1673 return getProviderInPackage(searchComponent.getPackageName());
1674 }
1675
1676 /**
1677 * Gets an appwidget provider from the given package. If the package contains more than
1678 * one appwidget provider, an arbitrary one is returned.
1679 */
1680 private ComponentName getProviderInPackage(String packageName) {
1681 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1682 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
1683 if (providers == null) return null;
1684 final int providerCount = providers.size();
1685 for (int i = 0; i < providerCount; i++) {
1686 ComponentName provider = providers.get(i).provider;
1687 if (provider != null && provider.getPackageName().equals(packageName)) {
1688 return provider;
1689 }
1690 }
1691 return null;
1692 }
1693
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001694 private boolean addAppWidget(XmlResourceParser parser, int type,
1695 SQLiteDatabase db, ContentValues values)
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001696 throws XmlPullParserException, IOException {
Romain Guy693599f2010-03-23 10:58:18 -07001697
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001698 String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
1699 String className = getAttributeValue(parser, ATTR_CLASS_NAME);
Mike Cleronb87bd162009-10-30 16:36:56 -07001700
1701 if (packageName == null || className == null) {
1702 return false;
1703 }
Romain Guy693599f2010-03-23 10:58:18 -07001704
1705 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -07001706 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -07001707 try {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001708 mPackageManager.getReceiverInfo(cn, 0);
Romain Guy693599f2010-03-23 10:58:18 -07001709 } catch (Exception e) {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001710 String[] packages = mPackageManager.currentToCanonicalPackageNames(
Romain Guy693599f2010-03-23 10:58:18 -07001711 new String[] { packageName });
1712 cn = new ComponentName(packages[0], className);
1713 try {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001714 mPackageManager.getReceiverInfo(cn, 0);
Romain Guy693599f2010-03-23 10:58:18 -07001715 } catch (Exception e1) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001716 System.out.println("Can't find widget provider: " + className);
Romain Guy693599f2010-03-23 10:58:18 -07001717 hasPackage = false;
1718 }
1719 }
1720
1721 if (hasPackage) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001722 String spanX = getAttributeValue(parser, ATTR_SPAN_X);
1723 String spanY = getAttributeValue(parser, ATTR_SPAN_Y);
1724
1725 values.put(Favorites.SPANX, spanX);
1726 values.put(Favorites.SPANY, spanY);
Winson Chungb3302ae2012-05-01 10:19:14 -07001727
1728 // Read the extras
1729 Bundle extras = new Bundle();
1730 int widgetDepth = parser.getDepth();
1731 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1732 parser.getDepth() > widgetDepth) {
1733 if (type != XmlPullParser.START_TAG) {
1734 continue;
1735 }
1736
Winson Chungb3302ae2012-05-01 10:19:14 -07001737 if (TAG_EXTRA.equals(parser.getName())) {
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001738 String key = getAttributeValue(parser, ATTR_KEY);
1739 String value = getAttributeValue(parser, ATTR_VALUE);
Winson Chungb3302ae2012-05-01 10:19:14 -07001740 if (key != null && value != null) {
1741 extras.putString(key, value);
1742 } else {
1743 throw new RuntimeException("Widget extras must have a key and value");
1744 }
1745 } else {
1746 throw new RuntimeException("Widgets can contain only extras");
1747 }
Winson Chungb3302ae2012-05-01 10:19:14 -07001748 }
1749
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001750 return addAppWidget(db, values, cn, extras);
Romain Guy693599f2010-03-23 10:58:18 -07001751 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001752
Romain Guy693599f2010-03-23 10:58:18 -07001753 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +00001754 }
Bjorn Bringert7984c942009-12-09 15:38:25 +00001755 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
Adam Cohen9b8f51f2014-05-30 15:34:09 -07001756 Bundle extras) {
Mike Cleronb87bd162009-10-30 16:36:56 -07001757 boolean allocatedAppWidgets = false;
1758 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1759
1760 try {
1761 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001762
Mike Cleronb87bd162009-10-30 16:36:56 -07001763 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Mike Cleronb87bd162009-10-30 16:36:56 -07001764 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Chris Wrend5e66bf2013-09-16 14:02:29 -04001765 values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
Adam Cohendcd297f2013-06-18 13:13:40 -07001766 values.put(Favorites._ID, generateNewItemId());
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001767 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -07001768
1769 allocatedAppWidgets = true;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001770
Michael Jurka8b805b12012-04-18 14:23:14 -07001771 // TODO: need to check return value
1772 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
Winson Chungb3302ae2012-05-01 10:19:14 -07001773
1774 // Send a broadcast to configure the widget
1775 if (extras != null && !extras.isEmpty()) {
1776 Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
1777 intent.setComponent(cn);
1778 intent.putExtras(extras);
1779 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1780 mContext.sendBroadcast(intent);
1781 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001782 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001783 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -07001784 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001785
Mike Cleronb87bd162009-10-30 16:36:56 -07001786 return allocatedAppWidgets;
1787 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001788
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001789 private long addUriShortcut(SQLiteDatabase db, ContentValues values, Resources res,
1790 XmlResourceParser parser) {
1791 final int iconResId = getAttributeResourceValue(parser, ATTR_ICON, 0);
1792 final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
Mike Cleronb87bd162009-10-30 16:36:56 -07001793
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001794 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -07001795 String uri = null;
1796 try {
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001797 uri = getAttributeValue(parser, ATTR_URI);
Mike Cleronb87bd162009-10-30 16:36:56 -07001798 intent = Intent.parseUri(uri, 0);
1799 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001800 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -07001801 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -07001802 }
1803
1804 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001805 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001806 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001807 }
1808
Adam Cohendcd297f2013-06-18 13:13:40 -07001809 long id = generateNewItemId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001810 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1811 values.put(Favorites.INTENT, intent.toUri(0));
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001812 values.put(Favorites.TITLE, res.getString(titleResId));
Mike Cleronb87bd162009-10-30 16:36:56 -07001813 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1814 values.put(Favorites.SPANX, 1);
1815 values.put(Favorites.SPANY, 1);
1816 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
Jeff Sharkey5aeef582014-04-14 13:26:42 -07001817 values.put(Favorites.ICON_PACKAGE, res.getResourcePackageName(iconResId));
1818 values.put(Favorites.ICON_RESOURCE, res.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001819 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001820
Adam Cohen228da5a2011-07-27 22:23:47 -07001821 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1822 return -1;
1823 }
1824 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001825 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001826
1827 public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
1828 final ContentResolver resolver = mContext.getContentResolver();
1829 Cursor c = null;
1830 int count = 0;
1831 int curScreen = 0;
1832
1833 try {
1834 c = resolver.query(uri, null, null, null, "title ASC");
1835 } catch (Exception e) {
1836 // Ignore
1837 }
1838
Dan Sandlerd5024042014-01-09 15:01:33 -05001839 // We already have a favorites database in the old provider
1840 if (c != null) {
1841 try {
1842 if (c.getCount() > 0) {
1843 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1844 final int intentIndex
1845 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1846 final int titleIndex
1847 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1848 final int iconTypeIndex
1849 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
1850 final int iconIndex
1851 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1852 final int iconPackageIndex
1853 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
1854 final int iconResourceIndex
1855 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
1856 final int containerIndex
1857 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1858 final int itemTypeIndex
1859 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1860 final int screenIndex
1861 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1862 final int cellXIndex
1863 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1864 final int cellYIndex
1865 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1866 final int uriIndex
1867 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1868 final int displayModeIndex
1869 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
Kenny Guyed131872014-04-30 03:02:21 +01001870 final int profileIndex
1871 = c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID);
Dan Sandlerd5024042014-01-09 15:01:33 -05001872
1873 int i = 0;
1874 int curX = 0;
1875 int curY = 0;
1876
1877 final LauncherAppState app = LauncherAppState.getInstance();
1878 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1879 final int width = (int) grid.numColumns;
1880 final int height = (int) grid.numRows;
1881 final int hotseatWidth = (int) grid.numHotseatIcons;
Adam Cohen556f6132014-01-15 15:18:08 -08001882 PackageManager pm = mContext.getPackageManager();
Dan Sandlerd5024042014-01-09 15:01:33 -05001883
1884 final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
1885
Adam Cohen72960972014-01-15 18:13:55 -08001886 final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
1887 final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
Dan Sandlerab5fa3a2014-03-06 23:48:04 -05001888 final SparseArray<ContentValues> hotseat = new SparseArray<ContentValues>();
Dan Sandlerd5024042014-01-09 15:01:33 -05001889
1890 while (c.moveToNext()) {
1891 final int itemType = c.getInt(itemTypeIndex);
1892 if (itemType != Favorites.ITEM_TYPE_APPLICATION
1893 && itemType != Favorites.ITEM_TYPE_SHORTCUT
1894 && itemType != Favorites.ITEM_TYPE_FOLDER) {
1895 continue;
1896 }
1897
1898 final int cellX = c.getInt(cellXIndex);
1899 final int cellY = c.getInt(cellYIndex);
1900 final int screen = c.getInt(screenIndex);
1901 int container = c.getInt(containerIndex);
1902 final String intentStr = c.getString(intentIndex);
Kenny Guyed131872014-04-30 03:02:21 +01001903
1904 UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
1905 UserHandleCompat userHandle;
1906 final long userSerialNumber;
1907 if (profileIndex != -1 && !c.isNull(profileIndex)) {
1908 userSerialNumber = c.getInt(profileIndex);
1909 userHandle = userManager.getUserForSerialNumber(userSerialNumber);
1910 } else {
1911 // Default to the serial number of this user, for older
1912 // shortcuts.
1913 userHandle = UserHandleCompat.myUserHandle();
1914 userSerialNumber = userManager.getSerialNumberForUser(userHandle);
1915 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001916 Launcher.addDumpLog(TAG, "migrating \""
Dan Sandlerab5fa3a2014-03-06 23:48:04 -05001917 + c.getString(titleIndex) + "\" ("
1918 + cellX + "," + cellY + "@"
1919 + LauncherSettings.Favorites.containerToString(container)
1920 + "/" + screen
1921 + "): " + intentStr, true);
Dan Sandlerd5024042014-01-09 15:01:33 -05001922
1923 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
Adam Cohen556f6132014-01-15 15:18:08 -08001924
1925 final Intent intent;
1926 final ComponentName cn;
1927 try {
1928 intent = Intent.parseUri(intentStr, 0);
1929 } catch (URISyntaxException e) {
1930 // bogus intent?
1931 Launcher.addDumpLog(TAG,
1932 "skipping invalid intent uri", true);
1933 continue;
1934 }
1935
1936 cn = intent.getComponent();
Dan Sandlerd5024042014-01-09 15:01:33 -05001937 if (TextUtils.isEmpty(intentStr)) {
1938 // no intent? no icon
1939 Launcher.addDumpLog(TAG, "skipping empty intent", true);
1940 continue;
Adam Cohen72960972014-01-15 18:13:55 -08001941 } else if (cn != null &&
Kenny Guyed131872014-04-30 03:02:21 +01001942 !LauncherModel.isValidPackageActivity(mContext, cn,
1943 userHandle)) {
Adam Cohen556f6132014-01-15 15:18:08 -08001944 // component no longer exists.
Adam Cohen72960972014-01-15 18:13:55 -08001945 Launcher.addDumpLog(TAG, "skipping item whose component " +
Adam Cohen556f6132014-01-15 15:18:08 -08001946 "no longer exists.", true);
1947 continue;
Adam Cohen72960972014-01-15 18:13:55 -08001948 } else if (container ==
1949 LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1950 // Dedupe icons directly on the workspace
1951
Adam Cohen556f6132014-01-15 15:18:08 -08001952 // Canonicalize
1953 // the Play Store sets the package parameter, but Launcher
1954 // does not, so we clear that out to keep them the same
1955 intent.setPackage(null);
1956 final String key = intent.toUri(0);
1957 if (seenIntents.contains(key)) {
1958 Launcher.addDumpLog(TAG, "skipping duplicate", true);
Dan Sandlerd5024042014-01-09 15:01:33 -05001959 continue;
Adam Cohen556f6132014-01-15 15:18:08 -08001960 } else {
1961 seenIntents.add(key);
Dan Sandlerd5024042014-01-09 15:01:33 -05001962 }
1963 }
1964 }
1965
1966 ContentValues values = new ContentValues(c.getColumnCount());
1967 values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
1968 values.put(LauncherSettings.Favorites.INTENT, intentStr);
1969 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
1970 values.put(LauncherSettings.Favorites.ICON_TYPE,
1971 c.getInt(iconTypeIndex));
1972 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
1973 values.put(LauncherSettings.Favorites.ICON_PACKAGE,
1974 c.getString(iconPackageIndex));
1975 values.put(LauncherSettings.Favorites.ICON_RESOURCE,
1976 c.getString(iconResourceIndex));
1977 values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
1978 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
1979 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
1980 values.put(LauncherSettings.Favorites.DISPLAY_MODE,
1981 c.getInt(displayModeIndex));
Kenny Guyed131872014-04-30 03:02:21 +01001982 values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
Dan Sandlerd5024042014-01-09 15:01:33 -05001983
Dan Sandlerab5fa3a2014-03-06 23:48:04 -05001984 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
1985 hotseat.put(screen, values);
Dan Sandlerd5024042014-01-09 15:01:33 -05001986 }
1987
1988 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1989 // In a folder or in the hotseat, preserve position
1990 values.put(LauncherSettings.Favorites.SCREEN, screen);
1991 values.put(LauncherSettings.Favorites.CELLX, cellX);
1992 values.put(LauncherSettings.Favorites.CELLY, cellY);
1993 } else {
Adam Cohen72960972014-01-15 18:13:55 -08001994 // For items contained directly on one of the workspace screen,
1995 // we'll determine their location (screen, x, y) in a second pass.
Dan Sandlerd5024042014-01-09 15:01:33 -05001996 }
1997
1998 values.put(LauncherSettings.Favorites.CONTAINER, container);
1999
Adam Cohen72960972014-01-15 18:13:55 -08002000 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
2001 shortcuts.add(values);
2002 } else {
2003 folders.add(values);
2004 }
Dan Sandlerd5024042014-01-09 15:01:33 -05002005 }
2006
Dan Sandlerab5fa3a2014-03-06 23:48:04 -05002007 // Now that we have all the hotseat icons, let's go through them left-right
2008 // and assign valid locations for them in the new hotseat
2009 final int N = hotseat.size();
2010 for (int idx=0; idx<N; idx++) {
2011 int hotseatX = hotseat.keyAt(idx);
2012 ContentValues values = hotseat.valueAt(idx);
2013
2014 if (hotseatX == grid.hotseatAllAppsRank) {
2015 // let's drop this in the next available hole in the hotseat
2016 while (++hotseatX < hotseatWidth) {
2017 if (hotseat.get(hotseatX) == null) {
2018 // found a spot! move it here
2019 values.put(LauncherSettings.Favorites.SCREEN,
2020 hotseatX);
2021 break;
2022 }
2023 }
2024 }
2025 if (hotseatX >= hotseatWidth) {
2026 // no room for you in the hotseat? it's off to the desktop with you
2027 values.put(LauncherSettings.Favorites.CONTAINER,
2028 Favorites.CONTAINER_DESKTOP);
2029 }
2030 }
2031
Adam Cohen72960972014-01-15 18:13:55 -08002032 final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
2033 // Folders first
2034 allItems.addAll(folders);
2035 // Then shortcuts
2036 allItems.addAll(shortcuts);
2037
2038 // Layout all the folders
2039 for (ContentValues values: allItems) {
2040 if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
2041 LauncherSettings.Favorites.CONTAINER_DESKTOP) {
2042 // Hotseat items and folder items have already had their
2043 // location information set. Nothing to be done here.
2044 continue;
2045 }
2046 values.put(LauncherSettings.Favorites.SCREEN, curScreen);
2047 values.put(LauncherSettings.Favorites.CELLX, curX);
2048 values.put(LauncherSettings.Favorites.CELLY, curY);
2049 curX = (curX + 1) % width;
2050 if (curX == 0) {
2051 curY = (curY + 1);
2052 }
2053 // Leave the last row of icons blank on every screen
2054 if (curY == height - 1) {
2055 curScreen = (int) generateNewScreenId();
2056 curY = 0;
2057 }
2058 }
2059
2060 if (allItems.size() > 0) {
Dan Sandlerd5024042014-01-09 15:01:33 -05002061 db.beginTransaction();
2062 try {
Adam Cohen72960972014-01-15 18:13:55 -08002063 for (ContentValues row: allItems) {
2064 if (row == null) continue;
2065 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
Dan Sandlerd5024042014-01-09 15:01:33 -05002066 < 0) {
2067 return;
2068 } else {
2069 count++;
2070 }
2071 }
2072 db.setTransactionSuccessful();
2073 } finally {
2074 db.endTransaction();
2075 }
2076 }
2077
2078 db.beginTransaction();
2079 try {
2080 for (i=0; i<=curScreen; i++) {
2081 final ContentValues values = new ContentValues();
2082 values.put(LauncherSettings.WorkspaceScreens._ID, i);
2083 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
2084 if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
2085 < 0) {
2086 return;
2087 }
2088 }
2089 db.setTransactionSuccessful();
2090 } finally {
2091 db.endTransaction();
2092 }
2093 }
2094 } finally {
2095 c.close();
2096 }
2097 }
2098
2099 Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
2100 + (curScreen+1) + " screens", true);
2101
2102 // ensure that new screens are created to hold these icons
2103 setFlagJustLoadedOldDb();
2104
2105 // Update max IDs; very important since we just grabbed IDs from another database
2106 mMaxItemId = initializeMaxItemId(db);
2107 mMaxScreenId = initializeMaxScreenId(db);
2108 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
2109 }
Mike Cleronb87bd162009-10-30 16:36:56 -07002110 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07002111
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002112 /**
2113 * Build a query string that will match any row where the column matches
2114 * anything in the values list.
2115 */
2116 static String buildOrWhereString(String column, int[] values) {
2117 StringBuilder selectWhere = new StringBuilder();
2118 for (int i = values.length - 1; i >= 0; i--) {
2119 selectWhere.append(column).append("=").append(values[i]);
2120 if (i > 0) {
2121 selectWhere.append(" OR ");
2122 }
2123 }
2124 return selectWhere.toString();
2125 }
2126
Jeff Sharkey5aeef582014-04-14 13:26:42 -07002127 /**
2128 * Return attribute value, attempting launcher-specific namespace first
2129 * before falling back to anonymous attribute.
2130 */
2131 static String getAttributeValue(XmlResourceParser parser, String attribute) {
2132 String value = parser.getAttributeValue(
2133 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
2134 if (value == null) {
2135 value = parser.getAttributeValue(null, attribute);
2136 }
2137 return value;
2138 }
2139
2140 /**
2141 * Return attribute resource value, attempting launcher-specific namespace
2142 * first before falling back to anonymous attribute.
2143 */
2144 static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
2145 int defaultValue) {
2146 int value = parser.getAttributeResourceValue(
2147 "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
2148 defaultValue);
2149 if (value == defaultValue) {
2150 value = parser.getAttributeResourceValue(null, attribute, defaultValue);
2151 }
2152 return value;
2153 }
2154
2155 private static void copyInteger(ContentValues from, ContentValues to, String key) {
2156 to.put(key, from.getAsInteger(key));
2157 }
2158
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002159 static class SqlArguments {
2160 public final String table;
2161 public final String where;
2162 public final String[] args;
2163
2164 SqlArguments(Uri url, String where, String[] args) {
2165 if (url.getPathSegments().size() == 1) {
2166 this.table = url.getPathSegments().get(0);
2167 this.where = where;
2168 this.args = args;
2169 } else if (url.getPathSegments().size() != 2) {
2170 throw new IllegalArgumentException("Invalid URI: " + url);
2171 } else if (!TextUtils.isEmpty(where)) {
2172 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
2173 } else {
2174 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07002175 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08002176 this.args = null;
2177 }
2178 }
2179
2180 SqlArguments(Uri url) {
2181 if (url.getPathSegments().size() == 1) {
2182 table = url.getPathSegments().get(0);
2183 where = null;
2184 args = null;
2185 } else {
2186 throw new IllegalArgumentException("Invalid URI: " + url);
2187 }
2188 }
2189 }
Adam Cohen72960972014-01-15 18:13:55 -08002190}