blob: cec9167f39ae4266164085b3a7b5f6da2fd2b54c [file] [log] [blame]
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080018
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000019import android.app.SearchManager;
The Android Open Source Project7376fae2009-03-11 12:11:58 -070020import android.appwidget.AppWidgetHost;
Mike Cleronb87bd162009-10-30 16:36:56 -070021import android.appwidget.AppWidgetManager;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000022import android.appwidget.AppWidgetProviderInfo;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080023import android.content.ComponentName;
Adam Cohen228da5a2011-07-27 22:23:47 -070024import android.content.ContentProvider;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080025import android.content.ContentResolver;
Adam Cohen228da5a2011-07-27 22:23:47 -070026import android.content.ContentUris;
27import android.content.ContentValues;
28import android.content.Context;
29import android.content.Intent;
Michael Jurkab85f8a42012-04-25 15:48:32 -070030import android.content.SharedPreferences;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080031import android.content.pm.ActivityInfo;
Adam Cohen228da5a2011-07-27 22:23:47 -070032import android.content.pm.PackageManager;
33import android.content.res.Resources;
34import android.content.res.TypedArray;
35import android.content.res.XmlResourceParser;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080036import android.database.Cursor;
37import android.database.SQLException;
Adam Cohen228da5a2011-07-27 22:23:47 -070038import android.database.sqlite.SQLiteDatabase;
39import android.database.sqlite.SQLiteOpenHelper;
40import android.database.sqlite.SQLiteQueryBuilder;
41import android.database.sqlite.SQLiteStatement;
Joe Onorato0589f0f2010-02-08 13:44:00 -080042import android.graphics.Bitmap;
43import android.graphics.BitmapFactory;
Adam Cohen228da5a2011-07-27 22:23:47 -070044import android.net.Uri;
Winson Chungb3302ae2012-05-01 10:19:14 -070045import android.os.Bundle;
Adam Cohen228da5a2011-07-27 22:23:47 -070046import android.provider.Settings;
47import android.text.TextUtils;
48import android.util.AttributeSet;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080049import android.util.Log;
50import android.util.Xml;
Adam Cohen228da5a2011-07-27 22:23:47 -070051
Daniel Sandler325dc232013-06-05 22:57:57 -040052import com.android.launcher3.LauncherSettings.Favorites;
Chris Wrene523e702013-10-09 10:36:55 -040053import com.android.launcher3.config.ProviderConfig;
Michael Jurka8b805b12012-04-18 14:23:14 -070054
55import org.xmlpull.v1.XmlPullParser;
56import org.xmlpull.v1.XmlPullParserException;
57
Dan Sandlerd5024042014-01-09 15:01:33 -050058import java.io.File;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080059import java.io.IOException;
Mike Cleronb87bd162009-10-30 16:36:56 -070060import java.net.URISyntaxException;
Adam Cohen228da5a2011-07-27 22:23:47 -070061import java.util.ArrayList;
Dan Sandlerd5024042014-01-09 15:01:33 -050062import java.util.HashSet;
Bjorn Bringertcd8fec02010-01-14 13:26:43 +000063import java.util.List;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080064
The Android Open Source Project31dd5032009-03-03 19:32:27 -080065public class LauncherProvider extends ContentProvider {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -080066 private static final String TAG = "Launcher.LauncherProvider";
67 private static final boolean LOGD = false;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080068
69 private static final String DATABASE_NAME = "launcher.db";
Winson Chung3d503fb2011-07-13 17:25:49 -070070
Chris Wrenf4d08112014-01-16 18:13:56 -050071 private static final int DATABASE_VERSION = 16;
The Android Open Source Project31dd5032009-03-03 19:32:27 -080072
Adam Cohene25af792013-06-06 23:08:25 -070073 static final String OLD_AUTHORITY = "com.android.launcher2.settings";
Chris Wrene523e702013-10-09 10:36:55 -040074 static final String AUTHORITY = ProviderConfig.AUTHORITY;
Winson Chung3d503fb2011-07-13 17:25:49 -070075
Dan Sandlerf0b8dac2013-11-19 12:21:25 -050076 // Should we attempt to load anything from the com.android.launcher2 provider?
Dan Sandlerd5024042014-01-09 15:01:33 -050077 static final boolean IMPORT_LAUNCHER2_DATABASE = false;
Dan Sandlerf0b8dac2013-11-19 12:21:25 -050078
The Android Open Source Project31dd5032009-03-03 19:32:27 -080079 static final String TABLE_FAVORITES = "favorites";
Adam Cohendcd297f2013-06-18 13:13:40 -070080 static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080081 static final String PARAMETER_NOTIFY = "notify";
Winson Chungc763c4e2013-07-19 13:49:06 -070082 static final String UPGRADED_FROM_OLD_DATABASE =
83 "UPGRADED_FROM_OLD_DATABASE";
84 static final String EMPTY_DATABASE_CREATED =
85 "EMPTY_DATABASE_CREATED";
Michael Jurka45355c42012-10-08 13:21:35 +020086 static final String DEFAULT_WORKSPACE_RESOURCE_ID =
87 "DEFAULT_WORKSPACE_RESOURCE_ID";
The Android Open Source Project31dd5032009-03-03 19:32:27 -080088
Winson Chungb3302ae2012-05-01 10:19:14 -070089 private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
Adam Cohene25af792013-06-06 23:08:25 -070090 "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
Winson Chungb3302ae2012-05-01 10:19:14 -070091
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070092 /**
Romain Guy73b979d2009-06-09 12:57:21 -070093 * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -070094 * {@link AppWidgetHost#deleteHost()} is called during database creation.
95 * Use this to recall {@link AppWidgetHost#startListening()} if needed.
96 */
97 static final Uri CONTENT_APPWIDGET_RESET_URI =
98 Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
Daniel Lehmannc3a80402012-04-23 21:35:11 -070099
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700100 private DatabaseHelper mOpenHelper;
Winson Chungc763c4e2013-07-19 13:49:06 -0700101 private static boolean sJustLoadedFromOldDb;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800102
103 @Override
104 public boolean onCreate() {
Daniel Sandlere4f98912013-06-25 15:13:26 -0400105 final Context context = getContext();
106 mOpenHelper = new DatabaseHelper(context);
107 LauncherAppState.setLauncherProvider(this);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800108 return true;
109 }
110
Winson Chung0b560dd2014-01-21 13:00:26 -0800111 public boolean wasNewDbCreated() {
112 return mOpenHelper.wasNewDbCreated();
113 }
114
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800115 @Override
116 public String getType(Uri uri) {
117 SqlArguments args = new SqlArguments(uri, null, null);
118 if (TextUtils.isEmpty(args.where)) {
119 return "vnd.android.cursor.dir/" + args.table;
120 } else {
121 return "vnd.android.cursor.item/" + args.table;
122 }
123 }
124
125 @Override
126 public Cursor query(Uri uri, String[] projection, String selection,
127 String[] selectionArgs, String sortOrder) {
128
129 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
130 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
131 qb.setTables(args.table);
132
Romain Guy73b979d2009-06-09 12:57:21 -0700133 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800134 Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
135 result.setNotificationUri(getContext().getContentResolver(), uri);
136
137 return result;
138 }
139
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700140 private static long dbInsertAndCheck(DatabaseHelper helper,
141 SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
Dan Sandlerd5024042014-01-09 15:01:33 -0500142 if (values == null) {
143 throw new RuntimeException("Error: attempting to insert null values");
144 }
Chris Wren5dee7af2013-12-20 17:22:11 -0500145 if (!values.containsKey(LauncherSettings.BaseLauncherColumns._ID)) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700146 throw new RuntimeException("Error: attempting to add item without specifying an id");
147 }
Chris Wren5dee7af2013-12-20 17:22:11 -0500148 helper.checkId(table, values);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700149 return db.insert(table, nullColumnHack, values);
150 }
151
Adam Cohen228da5a2011-07-27 22:23:47 -0700152 private static void deleteId(SQLiteDatabase db, long id) {
153 Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
154 SqlArguments args = new SqlArguments(uri, null, null);
155 db.delete(args.table, args.where, args.args);
156 }
157
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800158 @Override
159 public Uri insert(Uri uri, ContentValues initialValues) {
160 SqlArguments args = new SqlArguments(uri);
161
162 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Chris Wren1ada10d2013-09-13 18:01:38 -0400163 addModifiedTime(initialValues);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700164 final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800165 if (rowId <= 0) return null;
166
167 uri = ContentUris.withAppendedId(uri, rowId);
168 sendNotify(uri);
169
170 return uri;
171 }
172
173 @Override
174 public int bulkInsert(Uri uri, ContentValues[] values) {
175 SqlArguments args = new SqlArguments(uri);
176
177 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
178 db.beginTransaction();
179 try {
180 int numValues = values.length;
181 for (int i = 0; i < numValues; i++) {
Chris Wren1ada10d2013-09-13 18:01:38 -0400182 addModifiedTime(values[i]);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700183 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
184 return 0;
185 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800186 }
187 db.setTransactionSuccessful();
188 } finally {
189 db.endTransaction();
190 }
191
192 sendNotify(uri);
193 return values.length;
194 }
195
196 @Override
197 public int delete(Uri uri, String selection, String[] selectionArgs) {
198 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
199
200 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
201 int count = db.delete(args.table, args.where, args.args);
202 if (count > 0) sendNotify(uri);
203
204 return count;
205 }
206
207 @Override
208 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
209 SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
210
Chris Wren1ada10d2013-09-13 18:01:38 -0400211 addModifiedTime(values);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800212 SQLiteDatabase db = mOpenHelper.getWritableDatabase();
213 int count = db.update(args.table, values, args.where, args.args);
214 if (count > 0) sendNotify(uri);
215
216 return count;
217 }
218
219 private void sendNotify(Uri uri) {
220 String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
221 if (notify == null || "true".equals(notify)) {
222 getContext().getContentResolver().notifyChange(uri, null);
223 }
Chris Wren1ada10d2013-09-13 18:01:38 -0400224
225 // always notify the backup agent
Chris Wren92aa4232013-10-04 11:29:36 -0400226 LauncherBackupAgentHelper.dataChanged(getContext());
Chris Wren1ada10d2013-09-13 18:01:38 -0400227 }
228
229 private void addModifiedTime(ContentValues values) {
230 values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800231 }
232
Adam Cohendcd297f2013-06-18 13:13:40 -0700233 public long generateNewItemId() {
234 return mOpenHelper.generateNewItemId();
235 }
236
Winson Chungc763c4e2013-07-19 13:49:06 -0700237 public void updateMaxItemId(long id) {
238 mOpenHelper.updateMaxItemId(id);
239 }
240
Adam Cohendcd297f2013-06-18 13:13:40 -0700241 public long generateNewScreenId() {
242 return mOpenHelper.generateNewScreenId();
243 }
244
245 // This is only required one time while loading the workspace during the
246 // upgrade path, and should never be called from anywhere else.
247 public void updateMaxScreenId(long maxScreenId) {
248 mOpenHelper.updateMaxScreenId(maxScreenId);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700249 }
250
Brian Muramatsu5524b492012-10-02 16:55:54 -0700251 /**
Adam Cohene25af792013-06-06 23:08:25 -0700252 * @param Should we load the old db for upgrade? first run only.
253 */
Winson Chungc763c4e2013-07-19 13:49:06 -0700254 synchronized public boolean justLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400255 String spKey = LauncherAppState.getSharedPreferencesKey();
Adam Cohene25af792013-06-06 23:08:25 -0700256 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
257
Winson Chungc763c4e2013-07-19 13:49:06 -0700258 boolean loadedOldDb = false || sJustLoadedFromOldDb;
Adam Cohendcd297f2013-06-18 13:13:40 -0700259
Winson Chungc763c4e2013-07-19 13:49:06 -0700260 sJustLoadedFromOldDb = false;
261 if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) {
Adam Cohene25af792013-06-06 23:08:25 -0700262
263 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700264 editor.remove(UPGRADED_FROM_OLD_DATABASE);
Adam Cohene25af792013-06-06 23:08:25 -0700265 editor.commit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700266 loadedOldDb = true;
Adam Cohene25af792013-06-06 23:08:25 -0700267 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700268 return loadedOldDb;
Adam Cohene25af792013-06-06 23:08:25 -0700269 }
270
271 /**
Brian Muramatsu5524b492012-10-02 16:55:54 -0700272 * @param workspaceResId that can be 0 to use default or non-zero for specific resource
273 */
Michael Jurka45355c42012-10-08 13:21:35 +0200274 synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400275 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700276 SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
Adam Cohene25af792013-06-06 23:08:25 -0700277
Winson Chungc763c4e2013-07-19 13:49:06 -0700278 if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Chris Wren5dee7af2013-12-20 17:22:11 -0500279 Log.d(TAG, "loading default workspace");
Michael Jurka45355c42012-10-08 13:21:35 +0200280 int workspaceResId = origWorkspaceResId;
281
Brian Muramatsu5524b492012-10-02 16:55:54 -0700282 // Use default workspace resource if none provided
283 if (workspaceResId == 0) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800284 workspaceResId =
285 sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
Brian Muramatsu5524b492012-10-02 16:55:54 -0700286 }
287
Michael Jurkab85f8a42012-04-25 15:48:32 -0700288 // Populate favorites table with initial favorites
289 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700290 editor.remove(EMPTY_DATABASE_CREATED);
Michael Jurka45355c42012-10-08 13:21:35 +0200291 if (origWorkspaceResId != 0) {
292 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
293 }
Adam Cohene25af792013-06-06 23:08:25 -0700294
Brian Muramatsu5524b492012-10-02 16:55:54 -0700295 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
Winson Chungc763c4e2013-07-19 13:49:06 -0700296 mOpenHelper.setFlagJustLoadedOldDb();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700297 editor.commit();
298 }
299 }
300
Dan Sandlerd5024042014-01-09 15:01:33 -0500301 public void migrateLauncher2Shortcuts() {
302 mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
303 LauncherSettings.Favorites.OLD_CONTENT_URI);
304 }
305
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800306 private static int getDefaultWorkspaceResourceId() {
Nilesh Agrawal16f3ea82014-01-09 17:14:01 -0800307 if (LauncherAppState.isDisableAllApps()) {
Nilesh Agrawalda41ea62013-12-09 14:17:49 -0800308 return R.xml.default_workspace_no_all_apps;
309 } else {
310 return R.xml.default_workspace;
311 }
312 }
313
Winson Chungc763c4e2013-07-19 13:49:06 -0700314 private static interface ContentValuesCallback {
315 public void onRow(ContentValues values);
316 }
317
Adam Cohen6dbe0492013-12-02 17:00:14 -0800318 private static boolean shouldImportLauncher2Database(Context context) {
319 boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet);
320
321 // We don't import the old databse for tablets, as the grid size has changed.
322 return !isTablet && IMPORT_LAUNCHER2_DATABASE;
323 }
324
Dan Sandlerd5024042014-01-09 15:01:33 -0500325 public void deleteDatabase() {
326 // Are you sure? (y/n)
327 final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Dan Sandler2b471742014-01-21 14:14:41 -0500328 final File dbFile = new File(db.getPath());
Dan Sandlerd5024042014-01-09 15:01:33 -0500329 mOpenHelper.close();
Dan Sandler2b471742014-01-21 14:14:41 -0500330 if (dbFile.exists()) {
331 SQLiteDatabase.deleteDatabase(dbFile);
332 }
Dan Sandlerd5024042014-01-09 15:01:33 -0500333 mOpenHelper = new DatabaseHelper(getContext());
334 }
335
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800336 private static class DatabaseHelper extends SQLiteOpenHelper {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800337 private static final String TAG_FAVORITES = "favorites";
338 private static final String TAG_FAVORITE = "favorite";
The Android Open Source Projectf96811c2009-03-18 17:39:48 -0700339 private static final String TAG_CLOCK = "clock";
340 private static final String TAG_SEARCH = "search";
Mike Cleronb87bd162009-10-30 16:36:56 -0700341 private static final String TAG_APPWIDGET = "appwidget";
342 private static final String TAG_SHORTCUT = "shortcut";
Adam Cohen228da5a2011-07-27 22:23:47 -0700343 private static final String TAG_FOLDER = "folder";
Winson Chungb3302ae2012-05-01 10:19:14 -0700344 private static final String TAG_EXTRA = "extra";
Daniel Sandler57dac262013-10-03 13:28:36 -0400345 private static final String TAG_INCLUDE = "include";
Winson Chung3d503fb2011-07-13 17:25:49 -0700346
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800347 private final Context mContext;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700348 private final AppWidgetHost mAppWidgetHost;
Adam Cohendcd297f2013-06-18 13:13:40 -0700349 private long mMaxItemId = -1;
350 private long mMaxScreenId = -1;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800351
Winson Chung0b560dd2014-01-21 13:00:26 -0800352 private boolean mNewDbCreated = false;
353
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800354 DatabaseHelper(Context context) {
355 super(context, DATABASE_NAME, null, DATABASE_VERSION);
356 mContext = context;
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700357 mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
Winson Chung3d503fb2011-07-13 17:25:49 -0700358
359 // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
360 // the DB here
Adam Cohendcd297f2013-06-18 13:13:40 -0700361 if (mMaxItemId == -1) {
362 mMaxItemId = initializeMaxItemId(getWritableDatabase());
363 }
364 if (mMaxScreenId == -1) {
365 mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
Winson Chung3d503fb2011-07-13 17:25:49 -0700366 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800367 }
368
Winson Chung0b560dd2014-01-21 13:00:26 -0800369 public boolean wasNewDbCreated() {
370 return mNewDbCreated;
371 }
372
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700373 /**
374 * Send notification that we've deleted the {@link AppWidgetHost},
375 * probably as part of the initial database creation. The receiver may
376 * want to re-call {@link AppWidgetHost#startListening()} to ensure
377 * callbacks are correctly set.
378 */
379 private void sendAppWidgetResetNotify() {
380 final ContentResolver resolver = mContext.getContentResolver();
381 resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
382 }
383
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800384 @Override
385 public void onCreate(SQLiteDatabase db) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800386 if (LOGD) Log.d(TAG, "creating new launcher database");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700387
Adam Cohendcd297f2013-06-18 13:13:40 -0700388 mMaxItemId = 1;
389 mMaxScreenId = 0;
Winson Chung0b560dd2014-01-21 13:00:26 -0800390 mNewDbCreated = true;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700391
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800392 db.execSQL("CREATE TABLE favorites (" +
393 "_id INTEGER PRIMARY KEY," +
394 "title TEXT," +
395 "intent TEXT," +
396 "container INTEGER," +
397 "screen INTEGER," +
398 "cellX INTEGER," +
399 "cellY INTEGER," +
400 "spanX INTEGER," +
401 "spanY INTEGER," +
402 "itemType INTEGER," +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700403 "appWidgetId INTEGER NOT NULL DEFAULT -1," +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800404 "isShortcut INTEGER," +
405 "iconType INTEGER," +
406 "iconPackage TEXT," +
407 "iconResource TEXT," +
408 "icon BLOB," +
409 "uri TEXT," +
Chris Wrend5e66bf2013-09-16 14:02:29 -0400410 "displayMode INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400411 "appWidgetProvider TEXT," +
Chris Wrenf4d08112014-01-16 18:13:56 -0500412 "modified INTEGER NOT NULL DEFAULT 0," +
413 "restored INTEGER NOT NULL DEFAULT 0" +
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800414 ");");
Adam Cohendcd297f2013-06-18 13:13:40 -0700415 addWorkspacesTable(db);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800416
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700417 // Database was just created, so wipe any previous widgets
418 if (mAppWidgetHost != null) {
419 mAppWidgetHost.deleteHost();
Jeffrey Sharkey2bbcae12009-03-31 14:37:57 -0700420 sendAppWidgetResetNotify();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800421 }
Winson Chung3d503fb2011-07-13 17:25:49 -0700422
Adam Cohen6dbe0492013-12-02 17:00:14 -0800423 if (shouldImportLauncher2Database(mContext)) {
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500424 // Try converting the old database
425 ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
426 public void onRow(ContentValues values) {
427 int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
428 if (container == Favorites.CONTAINER_DESKTOP) {
429 int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
430 screen = (int) upgradeLauncherDb_permuteScreens(screen);
431 values.put(LauncherSettings.Favorites.SCREEN, screen);
432 }
433 }
434 };
435 Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
436 "/old_favorites?notify=true");
437 if (!convertDatabase(db, uri, permuteScreensCb, true)) {
438 // Try and upgrade from the Launcher2 db
439 uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
440 if (!convertDatabase(db, uri, permuteScreensCb, false)) {
441 // If we fail, then set a flag to load the default workspace
442 setFlagEmptyDbCreated();
443 return;
Winson Chungc763c4e2013-07-19 13:49:06 -0700444 }
445 }
Dan Sandlerf0b8dac2013-11-19 12:21:25 -0500446 // Right now, in non-default workspace cases, we want to run the final
447 // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
448 // set that flag too.
449 setFlagJustLoadedOldDb();
450 } else {
451 // Fresh and clean launcher DB.
452 mMaxItemId = initializeMaxItemId(db);
453 setFlagEmptyDbCreated();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800454 }
455 }
456
Adam Cohendcd297f2013-06-18 13:13:40 -0700457 private void addWorkspacesTable(SQLiteDatabase db) {
458 db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
459 LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
Chris Wren1ada10d2013-09-13 18:01:38 -0400460 LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
461 LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
Adam Cohendcd297f2013-06-18 13:13:40 -0700462 ");");
463 }
464
Winson Chungc763c4e2013-07-19 13:49:06 -0700465 private void setFlagJustLoadedOldDb() {
Daniel Sandlercc8befa2013-06-11 14:45:48 -0400466 String spKey = LauncherAppState.getSharedPreferencesKey();
Michael Jurkab85f8a42012-04-25 15:48:32 -0700467 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
468 SharedPreferences.Editor editor = sp.edit();
Winson Chungc763c4e2013-07-19 13:49:06 -0700469 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
470 editor.putBoolean(EMPTY_DATABASE_CREATED, false);
Michael Jurkab85f8a42012-04-25 15:48:32 -0700471 editor.commit();
472 }
473
Winson Chungc763c4e2013-07-19 13:49:06 -0700474 private void setFlagEmptyDbCreated() {
475 String spKey = LauncherAppState.getSharedPreferencesKey();
476 SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
477 SharedPreferences.Editor editor = sp.edit();
478 editor.putBoolean(EMPTY_DATABASE_CREATED, true);
479 editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
480 editor.commit();
481 }
482
483 // We rearrange the screens from the old launcher
484 // 12345 -> 34512
485 private long upgradeLauncherDb_permuteScreens(long screen) {
486 if (screen >= 2) {
487 return screen - 2;
488 } else {
489 return screen + 3;
490 }
491 }
492
493 private boolean convertDatabase(SQLiteDatabase db, Uri uri,
494 ContentValuesCallback cb, boolean deleteRows) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800495 if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800496 boolean converted = false;
497
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800498 final ContentResolver resolver = mContext.getContentResolver();
499 Cursor cursor = null;
500
501 try {
502 cursor = resolver.query(uri, null, null, null, null);
503 } catch (Exception e) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700504 // Ignore
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800505 }
506
507 // We already have a favorites database in the old provider
Winson Chungc763c4e2013-07-19 13:49:06 -0700508 if (cursor != null) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800509 try {
Winson Chungc763c4e2013-07-19 13:49:06 -0700510 if (cursor.getCount() > 0) {
511 converted = copyFromCursor(db, cursor, cb) > 0;
512 if (converted && deleteRows) {
513 resolver.delete(uri, null, null);
514 }
515 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800516 } finally {
517 cursor.close();
518 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800519 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700520
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800521 if (converted) {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700522 // Convert widgets from this import into widgets
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800523 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800524 convertWidgets(db);
Winson Chungc763c4e2013-07-19 13:49:06 -0700525
526 // Update max item id
527 mMaxItemId = initializeMaxItemId(db);
528 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800529 }
530
531 return converted;
532 }
533
Winson Chungc763c4e2013-07-19 13:49:06 -0700534 private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) {
Romain Guy73b979d2009-06-09 12:57:21 -0700535 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800536 final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
537 final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
538 final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
539 final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
540 final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
541 final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
542 final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
543 final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
544 final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
545 final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
546 final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
547 final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
548 final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
549
550 ContentValues[] rows = new ContentValues[c.getCount()];
551 int i = 0;
552 while (c.moveToNext()) {
553 ContentValues values = new ContentValues(c.getColumnCount());
Romain Guy73b979d2009-06-09 12:57:21 -0700554 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800555 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
556 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
557 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
558 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
559 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
560 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
561 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
562 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700563 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800564 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
565 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
566 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
567 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
568 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
Winson Chungc763c4e2013-07-19 13:49:06 -0700569 if (cb != null) {
570 cb.onRow(values);
571 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800572 rows[i++] = values;
573 }
574
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800575 int total = 0;
Winson Chungc763c4e2013-07-19 13:49:06 -0700576 if (i > 0) {
577 db.beginTransaction();
578 try {
579 int numValues = rows.length;
580 for (i = 0; i < numValues; i++) {
581 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
582 return 0;
583 } else {
584 total++;
585 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800586 }
Winson Chungc763c4e2013-07-19 13:49:06 -0700587 db.setTransactionSuccessful();
588 } finally {
589 db.endTransaction();
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800590 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800591 }
592
593 return total;
594 }
595
596 @Override
597 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Winson Chungc763c4e2013-07-19 13:49:06 -0700598 if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700599
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800600 int version = oldVersion;
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700601 if (version < 3) {
602 // upgrade 1,2 -> 3 added appWidgetId column
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800603 db.beginTransaction();
604 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700605 // Insert new column for holding appWidgetIds
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800606 db.execSQL("ALTER TABLE favorites " +
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700607 "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800608 db.setTransactionSuccessful();
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700609 version = 3;
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800610 } catch (SQLException ex) {
611 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800612 Log.e(TAG, ex.getMessage(), ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800613 } finally {
614 db.endTransaction();
615 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700616
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800617 // Convert existing widgets only if table upgrade was successful
The Android Open Source Projectca9475f2009-03-13 13:04:24 -0700618 if (version == 3) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800619 convertWidgets(db);
620 }
621 }
Romain Guy73b979d2009-06-09 12:57:21 -0700622
623 if (version < 4) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800624 version = 4;
Romain Guy73b979d2009-06-09 12:57:21 -0700625 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700626
Romain Guy509cd6a2010-03-23 15:10:56 -0700627 // Where's version 5?
628 // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
Daniel Sandler325dc232013-06-05 22:57:57 -0400629 // - Passion shipped on 2.1 with version 6 of launcher3
Romain Guy509cd6a2010-03-23 15:10:56 -0700630 // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
631 // but version 5 on there was the updateContactsShortcuts change
632 // which was version 6 in launcher 2 (first shipped on passion 2.1r1).
633 // The updateContactsShortcuts change is idempotent, so running it twice
634 // is okay so we'll do that when upgrading the devices that shipped with it.
635 if (version < 6) {
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800636 // We went from 3 to 5 screens. Move everything 1 to the right
637 db.beginTransaction();
638 try {
639 db.execSQL("UPDATE favorites SET screen=(screen + 1);");
640 db.setTransactionSuccessful();
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800641 } catch (SQLException ex) {
642 // Old version remains, which means we wipe old data
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800643 Log.e(TAG, ex.getMessage(), ex);
Mike Cleron3a2b3f22009-11-05 17:17:42 -0800644 } finally {
645 db.endTransaction();
646 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700647
Romain Guy509cd6a2010-03-23 15:10:56 -0700648 // We added the fast track.
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800649 if (updateContactsShortcuts(db)) {
650 version = 6;
651 }
652 }
Bjorn Bringert7984c942009-12-09 15:38:25 +0000653
654 if (version < 7) {
655 // Version 7 gets rid of the special search widget.
656 convertWidgets(db);
657 version = 7;
658 }
659
Joe Onorato0589f0f2010-02-08 13:44:00 -0800660 if (version < 8) {
661 // Version 8 (froyo) has the icons all normalized. This should
662 // already be the case in practice, but we now rely on it and don't
663 // resample the images each time.
664 normalizeIcons(db);
665 version = 8;
666 }
667
Winson Chung3d503fb2011-07-13 17:25:49 -0700668 if (version < 9) {
669 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
670 // 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 -0700671 if (mMaxItemId == -1) {
672 mMaxItemId = initializeMaxItemId(db);
Winson Chung3d503fb2011-07-13 17:25:49 -0700673 }
674
675 // Add default hotseat icons
Winson Chung6d092682011-11-16 18:43:26 -0800676 loadFavorites(db, R.xml.update_workspace);
Winson Chung3d503fb2011-07-13 17:25:49 -0700677 version = 9;
678 }
679
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700680 // We bumped the version three time during JB, once to update the launch flags, once to
681 // update the override for the default launch animation and once to set the mimetype
682 // to improve startup performance
683 if (version < 12) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700684 // Contact shortcuts need a different set of flags to be launched now
685 // The updateContactsShortcuts change is idempotent, so we can keep using it like
686 // back in the Donut days
687 updateContactsShortcuts(db);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700688 version = 12;
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700689 }
690
Adam Cohendcd297f2013-06-18 13:13:40 -0700691 if (version < 13) {
692 // With the new shrink-wrapped and re-orderable workspaces, it makes sense
693 // to persist workspace screens and their relative order.
694 mMaxScreenId = 0;
695
696 // This will never happen in the wild, but when we switch to using workspace
697 // screen ids, redo the import from old launcher.
Winson Chungc763c4e2013-07-19 13:49:06 -0700698 sJustLoadedFromOldDb = true;
Adam Cohendcd297f2013-06-18 13:13:40 -0700699
700 addWorkspacesTable(db);
701 version = 13;
702 }
703
Chris Wrend5e66bf2013-09-16 14:02:29 -0400704 if (version < 14) {
705 db.beginTransaction();
706 try {
707 // Insert new column for holding widget provider name
708 db.execSQL("ALTER TABLE favorites " +
709 "ADD COLUMN appWidgetProvider TEXT;");
710 db.setTransactionSuccessful();
711 version = 14;
712 } catch (SQLException ex) {
713 // Old version remains, which means we wipe old data
714 Log.e(TAG, ex.getMessage(), ex);
715 } finally {
716 db.endTransaction();
717 }
718 }
719
Chris Wren1ada10d2013-09-13 18:01:38 -0400720 if (version < 15) {
721 db.beginTransaction();
722 try {
723 // Insert new column for holding update timestamp
724 db.execSQL("ALTER TABLE favorites " +
725 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
726 db.execSQL("ALTER TABLE workspaceScreens " +
727 "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
728 db.setTransactionSuccessful();
729 version = 15;
730 } catch (SQLException ex) {
731 // Old version remains, which means we wipe old data
732 Log.e(TAG, ex.getMessage(), ex);
733 } finally {
734 db.endTransaction();
735 }
736 }
737
Chris Wrenf4d08112014-01-16 18:13:56 -0500738
739 if (version < 16) {
740 db.beginTransaction();
741 try {
742 // Insert new column for holding restore status
743 db.execSQL("ALTER TABLE favorites " +
744 "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;");
745 db.setTransactionSuccessful();
746 version = 16;
747 } catch (SQLException ex) {
748 // Old version remains, which means we wipe old data
749 Log.e(TAG, ex.getMessage(), ex);
750 } finally {
751 db.endTransaction();
752 }
753 }
754
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800755 if (version != DATABASE_VERSION) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -0800756 Log.w(TAG, "Destroying all old data.");
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800757 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
Adam Cohendcd297f2013-06-18 13:13:40 -0700758 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
759
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800760 onCreate(db);
761 }
762 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800763
764 private boolean updateContactsShortcuts(SQLiteDatabase db) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800765 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
766 new int[] { Favorites.ITEM_TYPE_SHORTCUT });
767
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700768 Cursor c = null;
769 final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800770 db.beginTransaction();
771 try {
772 // Select and iterate through each matching widget
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700773 c = db.query(TABLE_FAVORITES,
774 new String[] { Favorites._ID, Favorites.INTENT },
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800775 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700776 if (c == null) return false;
777
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800778 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700779
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800780 final int idIndex = c.getColumnIndex(Favorites._ID);
781 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700782
783 while (c.moveToNext()) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800784 long favoriteId = c.getLong(idIndex);
785 final String intentUri = c.getString(intentIndex);
786 if (intentUri != null) {
787 try {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700788 final Intent intent = Intent.parseUri(intentUri, 0);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800789 android.util.Log.d("Home", intent.toString());
790 final Uri uri = intent.getData();
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700791 if (uri != null) {
792 final String data = uri.toString();
793 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
794 actionQuickContact.equals(intent.getAction())) &&
795 (data.startsWith("content://contacts/people/") ||
796 data.startsWith("content://com.android.contacts/" +
797 "contacts/lookup/"))) {
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800798
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700799 final Intent newIntent = new Intent(actionQuickContact);
800 // When starting from the launcher, start in a new, cleared task
801 // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
802 // clear the whole thing preemptively here since
803 // QuickContactActivity will finish itself when launching other
804 // detail activities.
805 newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
806 Intent.FLAG_ACTIVITY_CLEAR_TASK);
Winson Chung2672ff92012-05-04 16:22:30 -0700807 newIntent.putExtra(
808 Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700809 newIntent.setData(uri);
Daniel Lehmannd02402c2012-05-14 18:30:53 -0700810 // Determine the type and also put that in the shortcut
811 // (that can speed up launch a bit)
812 newIntent.setDataAndType(uri, newIntent.resolveType(mContext));
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800813
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700814 final ContentValues values = new ContentValues();
815 values.put(LauncherSettings.Favorites.INTENT,
816 newIntent.toUri(0));
817
818 String updateWhere = Favorites._ID + "=" + favoriteId;
819 db.update(TABLE_FAVORITES, values, updateWhere, null);
820 }
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800821 }
822 } catch (RuntimeException ex) {
823 Log.e(TAG, "Problem upgrading shortcut", ex);
824 } catch (URISyntaxException e) {
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700825 Log.e(TAG, "Problem upgrading shortcut", e);
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800826 }
827 }
828 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -0700829
Romain Guy7eb9e5e2009-12-02 20:10:07 -0800830 db.setTransactionSuccessful();
831 } catch (SQLException ex) {
832 Log.w(TAG, "Problem while upgrading contacts", ex);
833 return false;
834 } finally {
835 db.endTransaction();
836 if (c != null) {
837 c.close();
838 }
839 }
840
841 return true;
842 }
843
Joe Onorato0589f0f2010-02-08 13:44:00 -0800844 private void normalizeIcons(SQLiteDatabase db) {
845 Log.d(TAG, "normalizing icons");
846
Joe Onorato346e1292010-02-18 10:34:24 -0500847 db.beginTransaction();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800848 Cursor c = null;
Joe Onorato9690b392010-03-23 17:34:37 -0400849 SQLiteStatement update = null;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800850 try {
851 boolean logged = false;
Joe Onorato9690b392010-03-23 17:34:37 -0400852 update = db.compileStatement("UPDATE favorites "
Jeff Hamiltoneaf77d62010-02-13 00:08:17 -0600853 + "SET icon=? WHERE _id=?");
Joe Onorato0589f0f2010-02-08 13:44:00 -0800854
855 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
856 Favorites.ICON_TYPE_BITMAP, null);
857
858 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
859 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
860
861 while (c.moveToNext()) {
862 long id = c.getLong(idIndex);
863 byte[] data = c.getBlob(iconIndex);
864 try {
865 Bitmap bitmap = Utilities.resampleIconBitmap(
866 BitmapFactory.decodeByteArray(data, 0, data.length),
867 mContext);
868 if (bitmap != null) {
869 update.bindLong(1, id);
870 data = ItemInfo.flattenBitmap(bitmap);
871 if (data != null) {
872 update.bindBlob(2, data);
873 update.execute();
874 }
875 bitmap.recycle();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800876 }
877 } catch (Exception e) {
878 if (!logged) {
879 Log.e(TAG, "Failed normalizing icon " + id, e);
880 } else {
881 Log.e(TAG, "Also failed normalizing icon " + id);
882 }
883 logged = true;
884 }
885 }
Bjorn Bringert3a928e42010-02-19 11:15:40 +0000886 db.setTransactionSuccessful();
Joe Onorato0589f0f2010-02-08 13:44:00 -0800887 } catch (SQLException ex) {
888 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
889 } finally {
890 db.endTransaction();
Joe Onorato9690b392010-03-23 17:34:37 -0400891 if (update != null) {
892 update.close();
893 }
Joe Onorato0589f0f2010-02-08 13:44:00 -0800894 if (c != null) {
895 c.close();
896 }
897 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700898 }
899
900 // Generates a new ID to use for an object in your database. This method should be only
901 // called from the main UI thread. As an exception, we do call it when we call the
902 // constructor from the worker thread; however, this doesn't extend until after the
903 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
904 // after that point
Adam Cohendcd297f2013-06-18 13:13:40 -0700905 public long generateNewItemId() {
906 if (mMaxItemId < 0) {
907 throw new RuntimeException("Error: max item id was not initialized");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700908 }
Adam Cohendcd297f2013-06-18 13:13:40 -0700909 mMaxItemId += 1;
910 return mMaxItemId;
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700911 }
912
Winson Chungc763c4e2013-07-19 13:49:06 -0700913 public void updateMaxItemId(long id) {
914 mMaxItemId = id + 1;
915 }
916
Chris Wren5dee7af2013-12-20 17:22:11 -0500917 public void checkId(String table, ContentValues values) {
918 long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
919 if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
920 mMaxScreenId = Math.max(id, mMaxScreenId);
921 } else {
922 mMaxItemId = Math.max(id, mMaxItemId);
923 }
924 }
925
Adam Cohendcd297f2013-06-18 13:13:40 -0700926 private long initializeMaxItemId(SQLiteDatabase db) {
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700927 Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
928
929 // get the result
930 final int maxIdIndex = 0;
931 long id = -1;
932 if (c != null && c.moveToNext()) {
933 id = c.getLong(maxIdIndex);
934 }
Michael Jurka5130e402011-10-13 04:55:35 -0700935 if (c != null) {
936 c.close();
937 }
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700938
939 if (id == -1) {
Adam Cohendcd297f2013-06-18 13:13:40 -0700940 throw new RuntimeException("Error: could not query max item id");
941 }
942
943 return id;
944 }
945
946 // Generates a new ID to use for an workspace screen in your database. This method
947 // should be only called from the main UI thread. As an exception, we do call it when we
948 // call the constructor from the worker thread; however, this doesn't extend until after the
949 // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
950 // after that point
951 public long generateNewScreenId() {
952 if (mMaxScreenId < 0) {
953 throw new RuntimeException("Error: max screen id was not initialized");
954 }
955 mMaxScreenId += 1;
Winson Chunga90303b2013-11-15 13:05:06 -0800956 // Log to disk
957 Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700958 return mMaxScreenId;
959 }
960
961 public void updateMaxScreenId(long maxScreenId) {
Winson Chunga90303b2013-11-15 13:05:06 -0800962 // Log to disk
963 Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
Adam Cohendcd297f2013-06-18 13:13:40 -0700964 mMaxScreenId = maxScreenId;
965 }
966
967 private long initializeMaxScreenId(SQLiteDatabase db) {
968 Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
969
970 // get the result
971 final int maxIdIndex = 0;
972 long id = -1;
973 if (c != null && c.moveToNext()) {
974 id = c.getLong(maxIdIndex);
975 }
976 if (c != null) {
977 c.close();
978 }
979
980 if (id == -1) {
981 throw new RuntimeException("Error: could not query max screen id");
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700982 }
983
Winson Chunga90303b2013-11-15 13:05:06 -0800984 // Log to disk
985 Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
Michael Jurkaa8c760d2011-04-28 14:59:33 -0700986 return id;
Joe Onorato0589f0f2010-02-08 13:44:00 -0800987 }
988
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800989 /**
The Android Open Source Project7376fae2009-03-11 12:11:58 -0700990 * Upgrade existing clock and photo frame widgets into their new widget
Bjorn Bringert93c45762009-12-16 13:19:47 +0000991 * equivalents.
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800992 */
993 private void convertWidgets(SQLiteDatabase db) {
Bjorn Bringert34251342009-12-15 13:33:11 +0000994 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800995 final int[] bindSources = new int[] {
996 Favorites.ITEM_TYPE_WIDGET_CLOCK,
997 Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
Bjorn Bringert7984c942009-12-09 15:38:25 +0000998 Favorites.ITEM_TYPE_WIDGET_SEARCH,
The Android Open Source Project31dd5032009-03-03 19:32:27 -0800999 };
Bjorn Bringert7984c942009-12-09 15:38:25 +00001000
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001001 final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001002
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001003 Cursor c = null;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001004
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001005 db.beginTransaction();
1006 try {
1007 // Select and iterate through each matching widget
Bjorn Bringert7984c942009-12-09 15:38:25 +00001008 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001009 selectWhere, null, null, null, null);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001010
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001011 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001012
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001013 final ContentValues values = new ContentValues();
1014 while (c != null && c.moveToNext()) {
1015 long favoriteId = c.getLong(0);
Bjorn Bringert7984c942009-12-09 15:38:25 +00001016 int favoriteType = c.getInt(1);
1017
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001018 // Allocate and update database with new appWidgetId
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001019 try {
The Android Open Source Project7376fae2009-03-11 12:11:58 -07001020 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001021
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001022 if (LOGD) {
1023 Log.d(TAG, "allocated appWidgetId=" + appWidgetId
1024 + " for favoriteId=" + favoriteId);
1025 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001026 values.clear();
Bjorn Bringert7984c942009-12-09 15:38:25 +00001027 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
1028 values.put(Favorites.APPWIDGET_ID, appWidgetId);
1029
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001030 // Original widgets might not have valid spans when upgrading
Bjorn Bringert7984c942009-12-09 15:38:25 +00001031 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
1032 values.put(LauncherSettings.Favorites.SPANX, 4);
1033 values.put(LauncherSettings.Favorites.SPANY, 1);
1034 } else {
1035 values.put(LauncherSettings.Favorites.SPANX, 2);
1036 values.put(LauncherSettings.Favorites.SPANY, 2);
1037 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001038
1039 String updateWhere = Favorites._ID + "=" + favoriteId;
1040 db.update(TABLE_FAVORITES, values, updateWhere, null);
Bjorn Bringert34251342009-12-15 13:33:11 +00001041
Bjorn Bringert34251342009-12-15 13:33:11 +00001042 if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001043 // TODO: check return value
1044 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001045 new ComponentName("com.android.alarmclock",
1046 "com.android.alarmclock.AnalogAppWidgetProvider"));
1047 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001048 // TODO: check return value
1049 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringert34251342009-12-15 13:33:11 +00001050 new ComponentName("com.android.camera",
1051 "com.android.camera.PhotoAppWidgetProvider"));
1052 } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001053 // TODO: check return value
1054 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001055 getSearchWidgetProvider());
Bjorn Bringert34251342009-12-15 13:33:11 +00001056 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001057 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001058 Log.e(TAG, "Problem allocating appWidgetId", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001059 }
1060 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001061
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001062 db.setTransactionSuccessful();
1063 } catch (SQLException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001064 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001065 } finally {
1066 db.endTransaction();
1067 if (c != null) {
1068 c.close();
1069 }
1070 }
Winson Chungc763c4e2013-07-19 13:49:06 -07001071
1072 // Update max item id
1073 mMaxItemId = initializeMaxItemId(db);
1074 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001075 }
1076
Michael Jurka8b805b12012-04-18 14:23:14 -07001077 private static final void beginDocument(XmlPullParser parser, String firstElementName)
1078 throws XmlPullParserException, IOException {
1079 int type;
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001080 while ((type = parser.next()) != XmlPullParser.START_TAG
1081 && type != XmlPullParser.END_DOCUMENT) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001082 ;
1083 }
1084
Michael Jurka9bc8eba2012-05-21 20:36:44 -07001085 if (type != XmlPullParser.START_TAG) {
Michael Jurka8b805b12012-04-18 14:23:14 -07001086 throw new XmlPullParserException("No start tag found");
1087 }
1088
1089 if (!parser.getName().equals(firstElementName)) {
1090 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1091 ", expected " + firstElementName);
1092 }
1093 }
1094
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001095 /**
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001096 * Loads the default set of favorite packages from an xml file.
1097 *
1098 * @param db The database to write the values into
Winson Chung3d503fb2011-07-13 17:25:49 -07001099 * @param filterContainerId The specific container id of items to load
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001100 */
Winson Chung6d092682011-11-16 18:43:26 -08001101 private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001102 Intent intent = new Intent(Intent.ACTION_MAIN, null);
1103 intent.addCategory(Intent.CATEGORY_LAUNCHER);
1104 ContentValues values = new ContentValues();
1105
Daniel Sandler57dac262013-10-03 13:28:36 -04001106 if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
1107
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001108 PackageManager packageManager = mContext.getPackageManager();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001109 int i = 0;
1110 try {
Winson Chung6d092682011-11-16 18:43:26 -08001111 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001112 AttributeSet attrs = Xml.asAttributeSet(parser);
Michael Jurka8b805b12012-04-18 14:23:14 -07001113 beginDocument(parser, TAG_FAVORITES);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001114
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001115 final int depth = parser.getDepth();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001116
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001117 int type;
1118 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1119 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
1120
1121 if (type != XmlPullParser.START_TAG) {
1122 continue;
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001123 }
1124
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001125 boolean added = false;
1126 final String name = parser.getName();
1127
Daniel Sandler57dac262013-10-03 13:28:36 -04001128 if (TAG_INCLUDE.equals(name)) {
1129 final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Include);
1130
1131 final int resId = a.getResourceId(R.styleable.Include_workspace, 0);
1132
1133 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
1134 "", resId));
1135
1136 if (resId != 0 && resId != workspaceResourceId) {
1137 // recursively load some more favorites, why not?
1138 i += loadFavorites(db, resId);
1139 added = false;
Daniel Sandler57dac262013-10-03 13:28:36 -04001140 } else {
1141 Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
1142 }
1143
1144 a.recycle();
1145
1146 if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
1147 continue;
1148 }
1149
1150 // Assuming it's a <favorite> at this point
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001151 TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
1152
Winson Chung3d503fb2011-07-13 17:25:49 -07001153 long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
1154 if (a.hasValue(R.styleable.Favorite_container)) {
1155 container = Long.valueOf(a.getString(R.styleable.Favorite_container));
1156 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001157
Winson Chung6d092682011-11-16 18:43:26 -08001158 String screen = a.getString(R.styleable.Favorite_screen);
1159 String x = a.getString(R.styleable.Favorite_x);
1160 String y = a.getString(R.styleable.Favorite_y);
1161
Winson Chung6d092682011-11-16 18:43:26 -08001162 values.clear();
1163 values.put(LauncherSettings.Favorites.CONTAINER, container);
1164 values.put(LauncherSettings.Favorites.SCREEN, screen);
1165 values.put(LauncherSettings.Favorites.CELLX, x);
1166 values.put(LauncherSettings.Favorites.CELLY, y);
1167
Daniel Sandler57dac262013-10-03 13:28:36 -04001168 if (LOGD) {
1169 final String title = a.getString(R.styleable.Favorite_title);
1170 final String pkg = a.getString(R.styleable.Favorite_packageName);
1171 final String something = title != null ? title : pkg;
1172 Log.v(TAG, String.format(
1173 ("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
1174 "", name,
1175 (something == null ? "" : (" \"" + something + "\"")),
1176 container, screen, x, y));
1177 }
1178
Winson Chung6d092682011-11-16 18:43:26 -08001179 if (TAG_FAVORITE.equals(name)) {
1180 long id = addAppShortcut(db, values, a, packageManager, intent);
1181 added = id >= 0;
1182 } else if (TAG_SEARCH.equals(name)) {
1183 added = addSearchWidget(db, values);
1184 } else if (TAG_CLOCK.equals(name)) {
1185 added = addClockWidget(db, values);
1186 } else if (TAG_APPWIDGET.equals(name)) {
Winson Chungb3302ae2012-05-01 10:19:14 -07001187 added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
Winson Chung6d092682011-11-16 18:43:26 -08001188 } else if (TAG_SHORTCUT.equals(name)) {
1189 long id = addUriShortcut(db, values, a);
1190 added = id >= 0;
1191 } else if (TAG_FOLDER.equals(name)) {
1192 String title;
1193 int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
1194 if (titleResId != -1) {
1195 title = mContext.getResources().getString(titleResId);
1196 } else {
1197 title = mContext.getResources().getString(R.string.folder_name);
Winson Chung3d503fb2011-07-13 17:25:49 -07001198 }
Winson Chung6d092682011-11-16 18:43:26 -08001199 values.put(LauncherSettings.Favorites.TITLE, title);
1200 long folderId = addFolder(db, values);
1201 added = folderId >= 0;
Winson Chung3d503fb2011-07-13 17:25:49 -07001202
Winson Chung6d092682011-11-16 18:43:26 -08001203 ArrayList<Long> folderItems = new ArrayList<Long>();
Winson Chung3d503fb2011-07-13 17:25:49 -07001204
Winson Chung6d092682011-11-16 18:43:26 -08001205 int folderDepth = parser.getDepth();
1206 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1207 parser.getDepth() > folderDepth) {
1208 if (type != XmlPullParser.START_TAG) {
1209 continue;
1210 }
1211 final String folder_item_name = parser.getName();
1212
1213 TypedArray ar = mContext.obtainStyledAttributes(attrs,
1214 R.styleable.Favorite);
1215 values.clear();
1216 values.put(LauncherSettings.Favorites.CONTAINER, folderId);
1217
Daniel Sandler57dac262013-10-03 13:28:36 -04001218 if (LOGD) {
1219 final String pkg = ar.getString(R.styleable.Favorite_packageName);
1220 final String uri = ar.getString(R.styleable.Favorite_uri);
1221 Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
1222 folder_item_name, uri != null ? uri : pkg));
1223 }
1224
Winson Chung6d092682011-11-16 18:43:26 -08001225 if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
1226 long id =
1227 addAppShortcut(db, values, ar, packageManager, intent);
1228 if (id >= 0) {
1229 folderItems.add(id);
1230 }
1231 } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
1232 long id = addUriShortcut(db, values, ar);
1233 if (id >= 0) {
1234 folderItems.add(id);
1235 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001236 } else {
Winson Chung6d092682011-11-16 18:43:26 -08001237 throw new RuntimeException("Folders can " +
1238 "contain only shortcuts");
Adam Cohen228da5a2011-07-27 22:23:47 -07001239 }
Winson Chung6d092682011-11-16 18:43:26 -08001240 ar.recycle();
1241 }
1242 // We can only have folders with >= 2 items, so we need to remove the
1243 // folder and clean up if less than 2 items were included, or some
1244 // failed to add, and less than 2 were actually added
1245 if (folderItems.size() < 2 && folderId >= 0) {
1246 // We just delete the folder and any items that made it
1247 deleteId(db, folderId);
1248 if (folderItems.size() > 0) {
1249 deleteId(db, folderItems.get(0));
Adam Cohen228da5a2011-07-27 22:23:47 -07001250 }
Winson Chung6d092682011-11-16 18:43:26 -08001251 added = false;
Winson Chung3d503fb2011-07-13 17:25:49 -07001252 }
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001253 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001254 if (added) i++;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001255 a.recycle();
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001256 }
1257 } catch (XmlPullParserException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001258 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001259 } catch (IOException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001260 Log.w(TAG, "Got exception parsing favorites.", e);
Winson Chung3d503fb2011-07-13 17:25:49 -07001261 } catch (RuntimeException e) {
1262 Log.w(TAG, "Got exception parsing favorites.", e);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001263 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001264
Winson Chungc763c4e2013-07-19 13:49:06 -07001265 // Update the max item id after we have loaded the database
1266 if (mMaxItemId == -1) {
1267 mMaxItemId = initializeMaxItemId(db);
1268 }
1269
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001270 return i;
1271 }
1272
Adam Cohen228da5a2011-07-27 22:23:47 -07001273 private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001274 PackageManager packageManager, Intent intent) {
Adam Cohen228da5a2011-07-27 22:23:47 -07001275 long id = -1;
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001276 ActivityInfo info;
1277 String packageName = a.getString(R.styleable.Favorite_packageName);
1278 String className = a.getString(R.styleable.Favorite_className);
1279 try {
Romain Guy693599f2010-03-23 10:58:18 -07001280 ComponentName cn;
1281 try {
1282 cn = new ComponentName(packageName, className);
1283 info = packageManager.getActivityInfo(cn, 0);
1284 } catch (PackageManager.NameNotFoundException nnfe) {
1285 String[] packages = packageManager.currentToCanonicalPackageNames(
1286 new String[] { packageName });
1287 cn = new ComponentName(packages[0], className);
1288 info = packageManager.getActivityInfo(cn, 0);
1289 }
Adam Cohendcd297f2013-06-18 13:13:40 -07001290 id = generateNewItemId();
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001291 intent.setComponent(cn);
Romain Guy693599f2010-03-23 10:58:18 -07001292 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1293 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Romain Guy1ce1a242009-06-23 17:34:54 -07001294 values.put(Favorites.INTENT, intent.toUri(0));
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001295 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
1296 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
1297 values.put(Favorites.SPANX, 1);
1298 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001299 values.put(Favorites._ID, generateNewItemId());
Adam Cohen228da5a2011-07-27 22:23:47 -07001300 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1301 return -1;
1302 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001303 } catch (PackageManager.NameNotFoundException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001304 Log.w(TAG, "Unable to add favorite: " + packageName +
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001305 "/" + className, e);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001306 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001307 return id;
1308 }
1309
1310 private long addFolder(SQLiteDatabase db, ContentValues values) {
1311 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
1312 values.put(Favorites.SPANX, 1);
1313 values.put(Favorites.SPANY, 1);
Adam Cohendcd297f2013-06-18 13:13:40 -07001314 long id = generateNewItemId();
Adam Cohen228da5a2011-07-27 22:23:47 -07001315 values.put(Favorites._ID, id);
1316 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
1317 return -1;
1318 } else {
1319 return id;
1320 }
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001321 }
1322
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001323 private ComponentName getSearchWidgetProvider() {
1324 SearchManager searchManager =
1325 (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
1326 ComponentName searchComponent = searchManager.getGlobalSearchActivity();
1327 if (searchComponent == null) return null;
1328 return getProviderInPackage(searchComponent.getPackageName());
1329 }
1330
1331 /**
1332 * Gets an appwidget provider from the given package. If the package contains more than
1333 * one appwidget provider, an arbitrary one is returned.
1334 */
1335 private ComponentName getProviderInPackage(String packageName) {
1336 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1337 List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
1338 if (providers == null) return null;
1339 final int providerCount = providers.size();
1340 for (int i = 0; i < providerCount; i++) {
1341 ComponentName provider = providers.get(i).provider;
1342 if (provider != null && provider.getPackageName().equals(packageName)) {
1343 return provider;
1344 }
1345 }
1346 return null;
1347 }
1348
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001349 private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringertcd8fec02010-01-14 13:26:43 +00001350 ComponentName cn = getSearchWidgetProvider();
Winson Chungb3302ae2012-05-01 10:19:14 -07001351 return addAppWidget(db, values, cn, 4, 1, null);
The Android Open Source Projectf96811c2009-03-18 17:39:48 -07001352 }
1353
1354 private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
Bjorn Bringert34251342009-12-15 13:33:11 +00001355 ComponentName cn = new ComponentName("com.android.alarmclock",
1356 "com.android.alarmclock.AnalogAppWidgetProvider");
Winson Chungb3302ae2012-05-01 10:19:14 -07001357 return addAppWidget(db, values, cn, 2, 2, null);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001358 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001359
Winson Chungb3302ae2012-05-01 10:19:14 -07001360 private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
1361 SQLiteDatabase db, ContentValues values, TypedArray a,
1362 PackageManager packageManager) throws XmlPullParserException, IOException {
Romain Guy693599f2010-03-23 10:58:18 -07001363
Mike Cleronb87bd162009-10-30 16:36:56 -07001364 String packageName = a.getString(R.styleable.Favorite_packageName);
1365 String className = a.getString(R.styleable.Favorite_className);
1366
1367 if (packageName == null || className == null) {
1368 return false;
1369 }
Romain Guy693599f2010-03-23 10:58:18 -07001370
1371 boolean hasPackage = true;
Mike Cleronb87bd162009-10-30 16:36:56 -07001372 ComponentName cn = new ComponentName(packageName, className);
Romain Guy693599f2010-03-23 10:58:18 -07001373 try {
1374 packageManager.getReceiverInfo(cn, 0);
1375 } catch (Exception e) {
1376 String[] packages = packageManager.currentToCanonicalPackageNames(
1377 new String[] { packageName });
1378 cn = new ComponentName(packages[0], className);
1379 try {
1380 packageManager.getReceiverInfo(cn, 0);
1381 } catch (Exception e1) {
1382 hasPackage = false;
1383 }
1384 }
1385
1386 if (hasPackage) {
1387 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
1388 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
Winson Chungb3302ae2012-05-01 10:19:14 -07001389
1390 // Read the extras
1391 Bundle extras = new Bundle();
1392 int widgetDepth = parser.getDepth();
1393 while ((type = parser.next()) != XmlPullParser.END_TAG ||
1394 parser.getDepth() > widgetDepth) {
1395 if (type != XmlPullParser.START_TAG) {
1396 continue;
1397 }
1398
1399 TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
1400 if (TAG_EXTRA.equals(parser.getName())) {
1401 String key = ar.getString(R.styleable.Extra_key);
1402 String value = ar.getString(R.styleable.Extra_value);
1403 if (key != null && value != null) {
1404 extras.putString(key, value);
1405 } else {
1406 throw new RuntimeException("Widget extras must have a key and value");
1407 }
1408 } else {
1409 throw new RuntimeException("Widgets can contain only extras");
1410 }
1411 ar.recycle();
1412 }
1413
1414 return addAppWidget(db, values, cn, spanX, spanY, extras);
Romain Guy693599f2010-03-23 10:58:18 -07001415 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001416
Romain Guy693599f2010-03-23 10:58:18 -07001417 return false;
Bjorn Bringert7984c942009-12-09 15:38:25 +00001418 }
Bjorn Bringert7984c942009-12-09 15:38:25 +00001419 private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
Winson Chungb3302ae2012-05-01 10:19:14 -07001420 int spanX, int spanY, Bundle extras) {
Mike Cleronb87bd162009-10-30 16:36:56 -07001421 boolean allocatedAppWidgets = false;
1422 final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
1423
1424 try {
1425 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001426
Mike Cleronb87bd162009-10-30 16:36:56 -07001427 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
Bjorn Bringert7984c942009-12-09 15:38:25 +00001428 values.put(Favorites.SPANX, spanX);
1429 values.put(Favorites.SPANY, spanY);
Mike Cleronb87bd162009-10-30 16:36:56 -07001430 values.put(Favorites.APPWIDGET_ID, appWidgetId);
Chris Wrend5e66bf2013-09-16 14:02:29 -04001431 values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
Adam Cohendcd297f2013-06-18 13:13:40 -07001432 values.put(Favorites._ID, generateNewItemId());
Michael Jurkaa8c760d2011-04-28 14:59:33 -07001433 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
Mike Cleronb87bd162009-10-30 16:36:56 -07001434
1435 allocatedAppWidgets = true;
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001436
Michael Jurka8b805b12012-04-18 14:23:14 -07001437 // TODO: need to check return value
1438 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
Winson Chungb3302ae2012-05-01 10:19:14 -07001439
1440 // Send a broadcast to configure the widget
1441 if (extras != null && !extras.isEmpty()) {
1442 Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
1443 intent.setComponent(cn);
1444 intent.putExtras(extras);
1445 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
1446 mContext.sendBroadcast(intent);
1447 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001448 } catch (RuntimeException ex) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001449 Log.e(TAG, "Problem allocating appWidgetId", ex);
Mike Cleronb87bd162009-10-30 16:36:56 -07001450 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001451
Mike Cleronb87bd162009-10-30 16:36:56 -07001452 return allocatedAppWidgets;
1453 }
Adam Cohen228da5a2011-07-27 22:23:47 -07001454
1455 private long addUriShortcut(SQLiteDatabase db, ContentValues values,
Mike Cleronb87bd162009-10-30 16:36:56 -07001456 TypedArray a) {
1457 Resources r = mContext.getResources();
1458
1459 final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
1460 final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
1461
Romain Guy7eb9e5e2009-12-02 20:10:07 -08001462 Intent intent;
Mike Cleronb87bd162009-10-30 16:36:56 -07001463 String uri = null;
1464 try {
1465 uri = a.getString(R.styleable.Favorite_uri);
1466 intent = Intent.parseUri(uri, 0);
1467 } catch (URISyntaxException e) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001468 Log.w(TAG, "Shortcut has malformed uri: " + uri);
Adam Cohen228da5a2011-07-27 22:23:47 -07001469 return -1; // Oh well
Mike Cleronb87bd162009-10-30 16:36:56 -07001470 }
1471
1472 if (iconResId == 0 || titleResId == 0) {
Joe Onoratoa30ce8e2009-11-11 08:16:49 -08001473 Log.w(TAG, "Shortcut is missing title or icon resource ID");
Adam Cohen228da5a2011-07-27 22:23:47 -07001474 return -1;
Mike Cleronb87bd162009-10-30 16:36:56 -07001475 }
1476
Adam Cohendcd297f2013-06-18 13:13:40 -07001477 long id = generateNewItemId();
Mike Cleronb87bd162009-10-30 16:36:56 -07001478 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1479 values.put(Favorites.INTENT, intent.toUri(0));
1480 values.put(Favorites.TITLE, r.getString(titleResId));
1481 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
1482 values.put(Favorites.SPANX, 1);
1483 values.put(Favorites.SPANY, 1);
1484 values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
1485 values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
1486 values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
Adam Cohen228da5a2011-07-27 22:23:47 -07001487 values.put(Favorites._ID, id);
Mike Cleronb87bd162009-10-30 16:36:56 -07001488
Adam Cohen228da5a2011-07-27 22:23:47 -07001489 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
1490 return -1;
1491 }
1492 return id;
Mike Cleronb87bd162009-10-30 16:36:56 -07001493 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001494
1495 public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
1496 final ContentResolver resolver = mContext.getContentResolver();
1497 Cursor c = null;
1498 int count = 0;
1499 int curScreen = 0;
1500
1501 try {
1502 c = resolver.query(uri, null, null, null, "title ASC");
1503 } catch (Exception e) {
1504 // Ignore
1505 }
1506
Dan Sandlerd5024042014-01-09 15:01:33 -05001507 // We already have a favorites database in the old provider
1508 if (c != null) {
1509 try {
1510 if (c.getCount() > 0) {
1511 final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
1512 final int intentIndex
1513 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
1514 final int titleIndex
1515 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
1516 final int iconTypeIndex
1517 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
1518 final int iconIndex
1519 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
1520 final int iconPackageIndex
1521 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
1522 final int iconResourceIndex
1523 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
1524 final int containerIndex
1525 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
1526 final int itemTypeIndex
1527 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
1528 final int screenIndex
1529 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
1530 final int cellXIndex
1531 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
1532 final int cellYIndex
1533 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
1534 final int uriIndex
1535 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
1536 final int displayModeIndex
1537 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
1538
1539 int i = 0;
1540 int curX = 0;
1541 int curY = 0;
1542
1543 final LauncherAppState app = LauncherAppState.getInstance();
1544 final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
1545 final int width = (int) grid.numColumns;
1546 final int height = (int) grid.numRows;
1547 final int hotseatWidth = (int) grid.numHotseatIcons;
Adam Cohen556f6132014-01-15 15:18:08 -08001548 PackageManager pm = mContext.getPackageManager();
Dan Sandlerd5024042014-01-09 15:01:33 -05001549
1550 final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
1551
Adam Cohen72960972014-01-15 18:13:55 -08001552 final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
1553 final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
Dan Sandlerd5024042014-01-09 15:01:33 -05001554
1555 while (c.moveToNext()) {
1556 final int itemType = c.getInt(itemTypeIndex);
1557 if (itemType != Favorites.ITEM_TYPE_APPLICATION
1558 && itemType != Favorites.ITEM_TYPE_SHORTCUT
1559 && itemType != Favorites.ITEM_TYPE_FOLDER) {
1560 continue;
1561 }
1562
1563 final int cellX = c.getInt(cellXIndex);
1564 final int cellY = c.getInt(cellYIndex);
1565 final int screen = c.getInt(screenIndex);
1566 int container = c.getInt(containerIndex);
1567 final String intentStr = c.getString(intentIndex);
1568 Launcher.addDumpLog(TAG, "migrating \""
1569 + c.getString(titleIndex) + "\": " + intentStr, true);
1570
1571 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
Adam Cohen556f6132014-01-15 15:18:08 -08001572
1573 final Intent intent;
1574 final ComponentName cn;
1575 try {
1576 intent = Intent.parseUri(intentStr, 0);
1577 } catch (URISyntaxException e) {
1578 // bogus intent?
1579 Launcher.addDumpLog(TAG,
1580 "skipping invalid intent uri", true);
1581 continue;
1582 }
1583
1584 cn = intent.getComponent();
Dan Sandlerd5024042014-01-09 15:01:33 -05001585 if (TextUtils.isEmpty(intentStr)) {
1586 // no intent? no icon
1587 Launcher.addDumpLog(TAG, "skipping empty intent", true);
1588 continue;
Adam Cohen72960972014-01-15 18:13:55 -08001589 } else if (cn != null &&
1590 !LauncherModel.isValidPackageComponent(pm, cn)) {
Adam Cohen556f6132014-01-15 15:18:08 -08001591 // component no longer exists.
Adam Cohen72960972014-01-15 18:13:55 -08001592 Launcher.addDumpLog(TAG, "skipping item whose component " +
Adam Cohen556f6132014-01-15 15:18:08 -08001593 "no longer exists.", true);
1594 continue;
Adam Cohen72960972014-01-15 18:13:55 -08001595 } else if (container ==
1596 LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1597 // Dedupe icons directly on the workspace
1598
Adam Cohen556f6132014-01-15 15:18:08 -08001599 // Canonicalize
1600 // the Play Store sets the package parameter, but Launcher
1601 // does not, so we clear that out to keep them the same
1602 intent.setPackage(null);
1603 final String key = intent.toUri(0);
1604 if (seenIntents.contains(key)) {
1605 Launcher.addDumpLog(TAG, "skipping duplicate", true);
Dan Sandlerd5024042014-01-09 15:01:33 -05001606 continue;
Adam Cohen556f6132014-01-15 15:18:08 -08001607 } else {
1608 seenIntents.add(key);
Dan Sandlerd5024042014-01-09 15:01:33 -05001609 }
1610 }
1611 }
1612
1613 ContentValues values = new ContentValues(c.getColumnCount());
1614 values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
1615 values.put(LauncherSettings.Favorites.INTENT, intentStr);
1616 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
1617 values.put(LauncherSettings.Favorites.ICON_TYPE,
1618 c.getInt(iconTypeIndex));
1619 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
1620 values.put(LauncherSettings.Favorites.ICON_PACKAGE,
1621 c.getString(iconPackageIndex));
1622 values.put(LauncherSettings.Favorites.ICON_RESOURCE,
1623 c.getString(iconResourceIndex));
1624 values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
1625 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
1626 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
1627 values.put(LauncherSettings.Favorites.DISPLAY_MODE,
1628 c.getInt(displayModeIndex));
1629
1630 if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
Adam Cohen72960972014-01-15 18:13:55 -08001631 && (screen >= hotseatWidth ||
1632 screen == grid.hotseatAllAppsRank)) {
Dan Sandlerd5024042014-01-09 15:01:33 -05001633 // no room for you in the hotseat? it's off to the desktop with you
1634 container = Favorites.CONTAINER_DESKTOP;
1635 }
1636
1637 if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1638 // In a folder or in the hotseat, preserve position
1639 values.put(LauncherSettings.Favorites.SCREEN, screen);
1640 values.put(LauncherSettings.Favorites.CELLX, cellX);
1641 values.put(LauncherSettings.Favorites.CELLY, cellY);
1642 } else {
Adam Cohen72960972014-01-15 18:13:55 -08001643 // For items contained directly on one of the workspace screen,
1644 // we'll determine their location (screen, x, y) in a second pass.
Dan Sandlerd5024042014-01-09 15:01:33 -05001645 }
1646
1647 values.put(LauncherSettings.Favorites.CONTAINER, container);
1648
Adam Cohen72960972014-01-15 18:13:55 -08001649 if (itemType != Favorites.ITEM_TYPE_FOLDER) {
1650 shortcuts.add(values);
1651 } else {
1652 folders.add(values);
1653 }
Dan Sandlerd5024042014-01-09 15:01:33 -05001654 }
1655
Adam Cohen72960972014-01-15 18:13:55 -08001656 final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
1657 // Folders first
1658 allItems.addAll(folders);
1659 // Then shortcuts
1660 allItems.addAll(shortcuts);
1661
1662 // Layout all the folders
1663 for (ContentValues values: allItems) {
1664 if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
1665 LauncherSettings.Favorites.CONTAINER_DESKTOP) {
1666 // Hotseat items and folder items have already had their
1667 // location information set. Nothing to be done here.
1668 continue;
1669 }
1670 values.put(LauncherSettings.Favorites.SCREEN, curScreen);
1671 values.put(LauncherSettings.Favorites.CELLX, curX);
1672 values.put(LauncherSettings.Favorites.CELLY, curY);
1673 curX = (curX + 1) % width;
1674 if (curX == 0) {
1675 curY = (curY + 1);
1676 }
1677 // Leave the last row of icons blank on every screen
1678 if (curY == height - 1) {
1679 curScreen = (int) generateNewScreenId();
1680 curY = 0;
1681 }
1682 }
1683
1684 if (allItems.size() > 0) {
Dan Sandlerd5024042014-01-09 15:01:33 -05001685 db.beginTransaction();
1686 try {
Adam Cohen72960972014-01-15 18:13:55 -08001687 for (ContentValues row: allItems) {
1688 if (row == null) continue;
1689 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
Dan Sandlerd5024042014-01-09 15:01:33 -05001690 < 0) {
1691 return;
1692 } else {
1693 count++;
1694 }
1695 }
1696 db.setTransactionSuccessful();
1697 } finally {
1698 db.endTransaction();
1699 }
1700 }
1701
1702 db.beginTransaction();
1703 try {
1704 for (i=0; i<=curScreen; i++) {
1705 final ContentValues values = new ContentValues();
1706 values.put(LauncherSettings.WorkspaceScreens._ID, i);
1707 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
1708 if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
1709 < 0) {
1710 return;
1711 }
1712 }
1713 db.setTransactionSuccessful();
1714 } finally {
1715 db.endTransaction();
1716 }
1717 }
1718 } finally {
1719 c.close();
1720 }
1721 }
1722
1723 Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
1724 + (curScreen+1) + " screens", true);
1725
1726 // ensure that new screens are created to hold these icons
1727 setFlagJustLoadedOldDb();
1728
1729 // Update max IDs; very important since we just grabbed IDs from another database
1730 mMaxItemId = initializeMaxItemId(db);
1731 mMaxScreenId = initializeMaxScreenId(db);
1732 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
1733 }
Mike Cleronb87bd162009-10-30 16:36:56 -07001734 }
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001735
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001736 /**
1737 * Build a query string that will match any row where the column matches
1738 * anything in the values list.
1739 */
1740 static String buildOrWhereString(String column, int[] values) {
1741 StringBuilder selectWhere = new StringBuilder();
1742 for (int i = values.length - 1; i >= 0; i--) {
1743 selectWhere.append(column).append("=").append(values[i]);
1744 if (i > 0) {
1745 selectWhere.append(" OR ");
1746 }
1747 }
1748 return selectWhere.toString();
1749 }
1750
1751 static class SqlArguments {
1752 public final String table;
1753 public final String where;
1754 public final String[] args;
1755
1756 SqlArguments(Uri url, String where, String[] args) {
1757 if (url.getPathSegments().size() == 1) {
1758 this.table = url.getPathSegments().get(0);
1759 this.where = where;
1760 this.args = args;
1761 } else if (url.getPathSegments().size() != 2) {
1762 throw new IllegalArgumentException("Invalid URI: " + url);
1763 } else if (!TextUtils.isEmpty(where)) {
1764 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
1765 } else {
1766 this.table = url.getPathSegments().get(0);
Daniel Lehmannc3a80402012-04-23 21:35:11 -07001767 this.where = "_id=" + ContentUris.parseId(url);
The Android Open Source Project31dd5032009-03-03 19:32:27 -08001768 this.args = null;
1769 }
1770 }
1771
1772 SqlArguments(Uri url) {
1773 if (url.getPathSegments().size() == 1) {
1774 table = url.getPathSegments().get(0);
1775 where = null;
1776 args = null;
1777 } else {
1778 throw new IllegalArgumentException("Invalid URI: " + url);
1779 }
1780 }
1781 }
Adam Cohen72960972014-01-15 18:13:55 -08001782}