2009-03-03 19:32:27 -08:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2013-06-05 22:57:57 -04:00
|
|
|
package com.android.launcher3;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2009-03-11 12:11:58 -07:00
|
|
|
import android.appwidget.AppWidgetHost;
|
2009-10-30 16:36:56 -07:00
|
|
|
import android.appwidget.AppWidgetManager;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.content.ComponentName;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.content.ContentProvider;
|
2014-02-11 15:15:29 +00:00
|
|
|
import android.content.ContentProviderOperation;
|
|
|
|
|
import android.content.ContentProviderResult;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.content.ContentResolver;
|
|
|
|
|
import android.content.ContentUris;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.content.ContentValues;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.content.Context;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.content.Intent;
|
2014-02-11 15:15:29 +00:00
|
|
|
import android.content.OperationApplicationException;
|
2012-04-25 15:48:32 -07:00
|
|
|
import android.content.SharedPreferences;
|
2009-10-30 16:36:56 -07:00
|
|
|
import android.content.res.Resources;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.database.Cursor;
|
|
|
|
|
import android.database.SQLException;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.database.sqlite.SQLiteDatabase;
|
|
|
|
|
import android.database.sqlite.SQLiteOpenHelper;
|
|
|
|
|
import android.database.sqlite.SQLiteQueryBuilder;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.net.Uri;
|
2015-01-21 11:50:57 -08:00
|
|
|
import android.os.StrictMode;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.Log;
|
2014-03-06 23:48:04 -05:00
|
|
|
import android.util.SparseArray;
|
2010-02-24 20:01:46 -08:00
|
|
|
|
2014-08-06 09:55:36 -07:00
|
|
|
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
|
|
|
|
|
import com.android.launcher3.LauncherSettings.Favorites;
|
2014-04-30 03:02:21 +01:00
|
|
|
import com.android.launcher3.compat.UserHandleCompat;
|
|
|
|
|
import com.android.launcher3.compat.UserManagerCompat;
|
2013-10-09 10:36:55 -04:00
|
|
|
import com.android.launcher3.config.ProviderConfig;
|
2012-04-18 14:23:14 -07:00
|
|
|
|
2014-01-09 15:01:33 -05:00
|
|
|
import java.io.File;
|
2011-07-27 22:23:47 -07:00
|
|
|
import java.net.URISyntaxException;
|
|
|
|
|
import java.util.ArrayList;
|
2014-05-15 14:04:01 -07:00
|
|
|
import java.util.Collections;
|
2014-01-09 15:01:33 -05:00
|
|
|
import java.util.HashSet;
|
2010-03-04 13:03:17 -08:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
public class LauncherProvider extends ContentProvider {
|
2009-11-11 08:16:49 -08:00
|
|
|
private static final String TAG = "Launcher.LauncherProvider";
|
|
|
|
|
private static final boolean LOGD = false;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2014-11-21 22:42:53 -08:00
|
|
|
private static final int MIN_DATABASE_VERSION = 12;
|
2015-02-25 10:46:34 -08:00
|
|
|
private static final int DATABASE_VERSION = 22;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2013-06-06 23:08:25 -07:00
|
|
|
static final String OLD_AUTHORITY = "com.android.launcher2.settings";
|
2013-10-09 10:36:55 -04:00
|
|
|
static final String AUTHORITY = ProviderConfig.AUTHORITY;
|
2011-07-13 17:25:49 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
static final String TABLE_FAVORITES = "favorites";
|
2013-06-18 13:13:40 -07:00
|
|
|
static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
|
2009-03-03 19:32:27 -08:00
|
|
|
static final String PARAMETER_NOTIFY = "notify";
|
2014-11-21 22:42:53 -08:00
|
|
|
static final String UPGRADED_FROM_OLD_DATABASE = "UPGRADED_FROM_OLD_DATABASE";
|
|
|
|
|
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2014-07-23 14:49:38 -07:00
|
|
|
private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd";
|
|
|
|
|
|
2014-03-13 12:14:12 -07:00
|
|
|
private LauncherProviderChangeListener mListener;
|
|
|
|
|
|
AI 143776: am: CL 143622 Correctly startListening() for widget updates when first boot completes.
During the first boot upgrade, LauncherProvider will deleteHost() to clear out any old appWidgetId bindings. During the first boot, Launcher calls AppWidgetHost.startListening() to watch for widget updates. It also calls loadUserItems(), which loads data from LauncherProvider, triggering the database creation and deleteHost() call. Because deleteHost() removes any existing callbacks, any future widget updates are dropped on the floor. (This can currently be solved by rebooting, because there isn't an upgrade on subsequent boots.)
This bug was particularly evident on vfpioneer-userdebug builds, as there aren't any configuration changes that cause Launcher to be destroyed and recreated. (When destroyed and recreated, we startListening() again, and LauncherProvider doesn't call deleteHost().)
To handle this special case, Launcher creates a ContentObserver pointing at a specific URI, which the LauncherProvider notifies when the AppWidgetHost is reset through deleteHost(), allowing Launcher to correctly startListening() again.
Original author: jsharkey
Merged from: //branches/cupcake/...
Automated import of CL 143776
2009-03-31 14:37:57 -07:00
|
|
|
/**
|
2009-06-09 12:57:21 -07:00
|
|
|
* {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
|
AI 143776: am: CL 143622 Correctly startListening() for widget updates when first boot completes.
During the first boot upgrade, LauncherProvider will deleteHost() to clear out any old appWidgetId bindings. During the first boot, Launcher calls AppWidgetHost.startListening() to watch for widget updates. It also calls loadUserItems(), which loads data from LauncherProvider, triggering the database creation and deleteHost() call. Because deleteHost() removes any existing callbacks, any future widget updates are dropped on the floor. (This can currently be solved by rebooting, because there isn't an upgrade on subsequent boots.)
This bug was particularly evident on vfpioneer-userdebug builds, as there aren't any configuration changes that cause Launcher to be destroyed and recreated. (When destroyed and recreated, we startListening() again, and LauncherProvider doesn't call deleteHost().)
To handle this special case, Launcher creates a ContentObserver pointing at a specific URI, which the LauncherProvider notifies when the AppWidgetHost is reset through deleteHost(), allowing Launcher to correctly startListening() again.
Original author: jsharkey
Merged from: //branches/cupcake/...
Automated import of CL 143776
2009-03-31 14:37:57 -07:00
|
|
|
* {@link AppWidgetHost#deleteHost()} is called during database creation.
|
|
|
|
|
* Use this to recall {@link AppWidgetHost#startListening()} if needed.
|
|
|
|
|
*/
|
|
|
|
|
static final Uri CONTENT_APPWIDGET_RESET_URI =
|
|
|
|
|
Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2011-04-28 14:59:33 -07:00
|
|
|
private DatabaseHelper mOpenHelper;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onCreate() {
|
2013-06-25 15:13:26 -04:00
|
|
|
final Context context = getContext();
|
2015-01-26 14:07:29 -08:00
|
|
|
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
|
2013-06-25 15:13:26 -04:00
|
|
|
mOpenHelper = new DatabaseHelper(context);
|
2015-01-21 11:50:57 -08:00
|
|
|
StrictMode.setThreadPolicy(oldPolicy);
|
2013-06-25 15:13:26 -04:00
|
|
|
LauncherAppState.setLauncherProvider(this);
|
2009-03-03 19:32:27 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-21 13:00:26 -08:00
|
|
|
public boolean wasNewDbCreated() {
|
|
|
|
|
return mOpenHelper.wasNewDbCreated();
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-13 12:14:12 -07:00
|
|
|
public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
|
|
|
|
|
mListener = listener;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public String getType(Uri uri) {
|
|
|
|
|
SqlArguments args = new SqlArguments(uri, null, null);
|
|
|
|
|
if (TextUtils.isEmpty(args.where)) {
|
|
|
|
|
return "vnd.android.cursor.dir/" + args.table;
|
|
|
|
|
} else {
|
|
|
|
|
return "vnd.android.cursor.item/" + args.table;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Cursor query(Uri uri, String[] projection, String selection,
|
|
|
|
|
String[] selectionArgs, String sortOrder) {
|
|
|
|
|
|
|
|
|
|
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
|
|
|
|
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
|
|
|
|
qb.setTables(args.table);
|
|
|
|
|
|
2009-06-09 12:57:21 -07:00
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
2009-03-03 19:32:27 -08:00
|
|
|
Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
|
|
|
|
|
result.setNotificationUri(getContext().getContentResolver(), uri);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-28 14:59:33 -07:00
|
|
|
private static long dbInsertAndCheck(DatabaseHelper helper,
|
|
|
|
|
SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
|
2014-01-09 15:01:33 -05:00
|
|
|
if (values == null) {
|
|
|
|
|
throw new RuntimeException("Error: attempting to insert null values");
|
|
|
|
|
}
|
2014-05-15 14:04:01 -07:00
|
|
|
if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
|
2011-04-28 14:59:33 -07:00
|
|
|
throw new RuntimeException("Error: attempting to add item without specifying an id");
|
|
|
|
|
}
|
2013-12-20 17:22:11 -05:00
|
|
|
helper.checkId(table, values);
|
2011-04-28 14:59:33 -07:00
|
|
|
return db.insert(table, nullColumnHack, values);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public Uri insert(Uri uri, ContentValues initialValues) {
|
|
|
|
|
SqlArguments args = new SqlArguments(uri);
|
|
|
|
|
|
2014-07-23 14:49:38 -07:00
|
|
|
// In very limited cases, we support system|signature permission apps to add to the db
|
|
|
|
|
String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD);
|
|
|
|
|
if (externalAdd != null && "true".equals(externalAdd)) {
|
|
|
|
|
if (!mOpenHelper.initializeExternalAdd(initialValues)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
2013-09-13 18:01:38 -04:00
|
|
|
addModifiedTime(initialValues);
|
2011-04-28 14:59:33 -07:00
|
|
|
final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
|
2015-03-02 14:24:21 -08:00
|
|
|
if (rowId < 0) return null;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
|
|
|
|
uri = ContentUris.withAppendedId(uri, rowId);
|
|
|
|
|
sendNotify(uri);
|
|
|
|
|
|
|
|
|
|
return uri;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-23 14:49:38 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public int bulkInsert(Uri uri, ContentValues[] values) {
|
|
|
|
|
SqlArguments args = new SqlArguments(uri);
|
|
|
|
|
|
|
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
int numValues = values.length;
|
|
|
|
|
for (int i = 0; i < numValues; i++) {
|
2013-09-13 18:01:38 -04:00
|
|
|
addModifiedTime(values[i]);
|
2011-04-28 14:59:33 -07:00
|
|
|
if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendNotify(uri);
|
|
|
|
|
return values.length;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 15:15:29 +00:00
|
|
|
@Override
|
|
|
|
|
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
|
|
|
|
|
throws OperationApplicationException {
|
|
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
ContentProviderResult[] result = super.applyBatch(operations);
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
return result;
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
|
|
|
|
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
|
|
|
|
|
|
|
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
|
|
|
|
int count = db.delete(args.table, args.where, args.args);
|
|
|
|
|
if (count > 0) sendNotify(uri);
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
|
|
|
|
SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
|
|
|
|
|
|
2013-09-13 18:01:38 -04:00
|
|
|
addModifiedTime(values);
|
2009-03-03 19:32:27 -08:00
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
|
|
|
|
int count = db.update(args.table, values, args.where, args.args);
|
|
|
|
|
if (count > 0) sendNotify(uri);
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void sendNotify(Uri uri) {
|
|
|
|
|
String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
|
|
|
|
|
if (notify == null || "true".equals(notify)) {
|
|
|
|
|
getContext().getContentResolver().notifyChange(uri, null);
|
|
|
|
|
}
|
2013-09-13 18:01:38 -04:00
|
|
|
|
|
|
|
|
// always notify the backup agent
|
2013-10-04 11:29:36 -04:00
|
|
|
LauncherBackupAgentHelper.dataChanged(getContext());
|
2014-03-13 12:14:12 -07:00
|
|
|
if (mListener != null) {
|
|
|
|
|
mListener.onLauncherProviderChange();
|
|
|
|
|
}
|
2013-09-13 18:01:38 -04:00
|
|
|
}
|
|
|
|
|
|
2015-02-25 10:46:34 -08:00
|
|
|
private static void addModifiedTime(ContentValues values) {
|
2013-09-13 18:01:38 -04:00
|
|
|
values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:13:40 -07:00
|
|
|
public long generateNewItemId() {
|
|
|
|
|
return mOpenHelper.generateNewItemId();
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-19 13:49:06 -07:00
|
|
|
public void updateMaxItemId(long id) {
|
|
|
|
|
mOpenHelper.updateMaxItemId(id);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:13:40 -07:00
|
|
|
public long generateNewScreenId() {
|
|
|
|
|
return mOpenHelper.generateNewScreenId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is only required one time while loading the workspace during the
|
|
|
|
|
// upgrade path, and should never be called from anywhere else.
|
|
|
|
|
public void updateMaxScreenId(long maxScreenId) {
|
|
|
|
|
mOpenHelper.updateMaxScreenId(maxScreenId);
|
2011-04-28 14:59:33 -07:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 22:09:29 -07:00
|
|
|
/**
|
|
|
|
|
* Clears all the data for a fresh start.
|
|
|
|
|
*/
|
|
|
|
|
synchronized public void createEmptyDB() {
|
|
|
|
|
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-16 09:24:19 -07:00
|
|
|
public void clearFlagEmptyDbCreated() {
|
|
|
|
|
String spKey = LauncherAppState.getSharedPreferencesKey();
|
|
|
|
|
getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE)
|
|
|
|
|
.edit()
|
|
|
|
|
.remove(EMPTY_DATABASE_CREATED)
|
|
|
|
|
.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-02 16:55:54 -07:00
|
|
|
/**
|
2014-08-06 09:55:36 -07:00
|
|
|
* Loads the default workspace based on the following priority scheme:
|
|
|
|
|
* 1) From a package provided by play store
|
|
|
|
|
* 2) From a partner configuration APK, already in the system image
|
|
|
|
|
* 3) The default configuration for the particular device
|
2012-10-02 16:55:54 -07:00
|
|
|
*/
|
2014-08-06 09:55:36 -07:00
|
|
|
synchronized public void loadDefaultFavoritesIfNecessary() {
|
2013-06-11 14:45:48 -04:00
|
|
|
String spKey = LauncherAppState.getSharedPreferencesKey();
|
2012-04-25 15:48:32 -07:00
|
|
|
SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
|
2013-06-06 23:08:25 -07:00
|
|
|
|
2013-07-19 13:49:06 -07:00
|
|
|
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
|
2013-12-20 17:22:11 -05:00
|
|
|
Log.d(TAG, "loading default workspace");
|
2012-10-08 13:21:35 +02:00
|
|
|
|
2014-10-01 15:33:41 -07:00
|
|
|
AutoInstallsLayout loader = AutoInstallsLayout.get(getContext(),
|
2014-08-06 09:55:36 -07:00
|
|
|
mOpenHelper.mAppWidgetHost, mOpenHelper);
|
|
|
|
|
|
|
|
|
|
if (loader == null) {
|
2014-05-30 15:34:09 -07:00
|
|
|
final Partner partner = Partner.get(getContext().getPackageManager());
|
|
|
|
|
if (partner != null && partner.hasDefaultLayout()) {
|
|
|
|
|
final Resources partnerRes = partner.getResources();
|
2014-08-29 15:05:48 -07:00
|
|
|
int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
|
2014-05-30 15:34:09 -07:00
|
|
|
"xml", partner.getPackageName());
|
2014-08-06 09:55:36 -07:00
|
|
|
if (workspaceResId != 0) {
|
2014-10-01 15:33:41 -07:00
|
|
|
loader = new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
|
|
|
|
|
mOpenHelper, partnerRes, workspaceResId);
|
2014-08-06 09:55:36 -07:00
|
|
|
}
|
2014-05-30 15:34:09 -07:00
|
|
|
}
|
|
|
|
|
}
|
2012-10-02 16:55:54 -07:00
|
|
|
|
2014-10-23 14:21:02 -07:00
|
|
|
final boolean usingExternallyProvidedLayout = loader != null;
|
2014-08-06 09:55:36 -07:00
|
|
|
if (loader == null) {
|
2014-10-23 14:21:02 -07:00
|
|
|
loader = getDefaultLayoutParser();
|
2012-10-08 13:21:35 +02:00
|
|
|
}
|
2014-08-06 09:55:36 -07:00
|
|
|
// Populate favorites table with initial favorites
|
2014-10-23 14:21:02 -07:00
|
|
|
if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
|
|
|
|
|
&& usingExternallyProvidedLayout) {
|
|
|
|
|
// Unable to load external layout. Cleanup and load the internal layout.
|
|
|
|
|
createEmptyDB();
|
|
|
|
|
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
|
|
|
|
|
getDefaultLayoutParser());
|
|
|
|
|
}
|
2014-10-16 09:24:19 -07:00
|
|
|
clearFlagEmptyDbCreated();
|
2012-04-25 15:48:32 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-23 14:21:02 -07:00
|
|
|
private DefaultLayoutParser getDefaultLayoutParser() {
|
|
|
|
|
int defaultLayout = LauncherAppState.getInstance()
|
|
|
|
|
.getDynamicGrid().getDeviceProfile().defaultLayoutId;
|
|
|
|
|
return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
|
|
|
|
|
mOpenHelper, getContext().getResources(), defaultLayout);
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-09 15:01:33 -05:00
|
|
|
public void migrateLauncher2Shortcuts() {
|
|
|
|
|
mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
|
2014-03-21 15:42:06 -04:00
|
|
|
Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
|
2014-01-09 15:01:33 -05:00
|
|
|
}
|
|
|
|
|
|
2015-01-05 13:41:43 -08:00
|
|
|
public void updateFolderItemsRank() {
|
|
|
|
|
mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-09 15:01:33 -05:00
|
|
|
public void deleteDatabase() {
|
|
|
|
|
// Are you sure? (y/n)
|
|
|
|
|
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
2014-01-21 14:14:41 -05:00
|
|
|
final File dbFile = new File(db.getPath());
|
2014-01-09 15:01:33 -05:00
|
|
|
mOpenHelper.close();
|
2014-01-21 14:14:41 -05:00
|
|
|
if (dbFile.exists()) {
|
|
|
|
|
SQLiteDatabase.deleteDatabase(dbFile);
|
|
|
|
|
}
|
2014-01-09 15:01:33 -05:00
|
|
|
mOpenHelper = new DatabaseHelper(getContext());
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-06 09:55:36 -07:00
|
|
|
private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
|
2009-03-03 19:32:27 -08:00
|
|
|
private final Context mContext;
|
2009-03-11 12:11:58 -07:00
|
|
|
private final AppWidgetHost mAppWidgetHost;
|
2013-06-18 13:13:40 -07:00
|
|
|
private long mMaxItemId = -1;
|
|
|
|
|
private long mMaxScreenId = -1;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2014-01-21 13:00:26 -08:00
|
|
|
private boolean mNewDbCreated = false;
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
DatabaseHelper(Context context) {
|
2014-10-06 16:06:46 +01:00
|
|
|
super(context, LauncherFiles.LAUNCHER_DB, null, DATABASE_VERSION);
|
2009-03-03 19:32:27 -08:00
|
|
|
mContext = context;
|
2009-03-11 12:11:58 -07:00
|
|
|
mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
|
2011-07-13 17:25:49 -07:00
|
|
|
|
|
|
|
|
// In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
|
|
|
|
|
// the DB here
|
2013-06-18 13:13:40 -07:00
|
|
|
if (mMaxItemId == -1) {
|
|
|
|
|
mMaxItemId = initializeMaxItemId(getWritableDatabase());
|
|
|
|
|
}
|
|
|
|
|
if (mMaxScreenId == -1) {
|
|
|
|
|
mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
|
2011-07-13 17:25:49 -07:00
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
|
2014-01-21 13:00:26 -08:00
|
|
|
public boolean wasNewDbCreated() {
|
|
|
|
|
return mNewDbCreated;
|
|
|
|
|
}
|
|
|
|
|
|
AI 143776: am: CL 143622 Correctly startListening() for widget updates when first boot completes.
During the first boot upgrade, LauncherProvider will deleteHost() to clear out any old appWidgetId bindings. During the first boot, Launcher calls AppWidgetHost.startListening() to watch for widget updates. It also calls loadUserItems(), which loads data from LauncherProvider, triggering the database creation and deleteHost() call. Because deleteHost() removes any existing callbacks, any future widget updates are dropped on the floor. (This can currently be solved by rebooting, because there isn't an upgrade on subsequent boots.)
This bug was particularly evident on vfpioneer-userdebug builds, as there aren't any configuration changes that cause Launcher to be destroyed and recreated. (When destroyed and recreated, we startListening() again, and LauncherProvider doesn't call deleteHost().)
To handle this special case, Launcher creates a ContentObserver pointing at a specific URI, which the LauncherProvider notifies when the AppWidgetHost is reset through deleteHost(), allowing Launcher to correctly startListening() again.
Original author: jsharkey
Merged from: //branches/cupcake/...
Automated import of CL 143776
2009-03-31 14:37:57 -07:00
|
|
|
/**
|
|
|
|
|
* Send notification that we've deleted the {@link AppWidgetHost},
|
|
|
|
|
* probably as part of the initial database creation. The receiver may
|
|
|
|
|
* want to re-call {@link AppWidgetHost#startListening()} to ensure
|
|
|
|
|
* callbacks are correctly set.
|
|
|
|
|
*/
|
|
|
|
|
private void sendAppWidgetResetNotify() {
|
|
|
|
|
final ContentResolver resolver = mContext.getContentResolver();
|
|
|
|
|
resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public void onCreate(SQLiteDatabase db) {
|
2009-11-11 08:16:49 -08:00
|
|
|
if (LOGD) Log.d(TAG, "creating new launcher database");
|
2011-04-28 14:59:33 -07:00
|
|
|
|
2013-06-18 13:13:40 -07:00
|
|
|
mMaxItemId = 1;
|
|
|
|
|
mMaxScreenId = 0;
|
2014-01-21 13:00:26 -08:00
|
|
|
mNewDbCreated = true;
|
2011-04-28 14:59:33 -07:00
|
|
|
|
2014-04-30 03:02:21 +01:00
|
|
|
UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
|
|
|
|
|
long userSerialNumber = userManager.getSerialNumberForUser(
|
|
|
|
|
UserHandleCompat.myUserHandle());
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
db.execSQL("CREATE TABLE favorites (" +
|
|
|
|
|
"_id INTEGER PRIMARY KEY," +
|
|
|
|
|
"title TEXT," +
|
|
|
|
|
"intent TEXT," +
|
|
|
|
|
"container INTEGER," +
|
|
|
|
|
"screen INTEGER," +
|
|
|
|
|
"cellX INTEGER," +
|
|
|
|
|
"cellY INTEGER," +
|
|
|
|
|
"spanX INTEGER," +
|
|
|
|
|
"spanY INTEGER," +
|
|
|
|
|
"itemType INTEGER," +
|
2009-03-13 13:04:24 -07:00
|
|
|
"appWidgetId INTEGER NOT NULL DEFAULT -1," +
|
2009-03-03 19:32:27 -08:00
|
|
|
"isShortcut INTEGER," +
|
|
|
|
|
"iconType INTEGER," +
|
|
|
|
|
"iconPackage TEXT," +
|
|
|
|
|
"iconResource TEXT," +
|
|
|
|
|
"icon BLOB," +
|
|
|
|
|
"uri TEXT," +
|
2013-09-16 14:02:29 -04:00
|
|
|
"displayMode INTEGER," +
|
2013-09-13 18:01:38 -04:00
|
|
|
"appWidgetProvider TEXT," +
|
2014-01-16 18:13:56 -05:00
|
|
|
"modified INTEGER NOT NULL DEFAULT 0," +
|
2014-04-30 03:02:21 +01:00
|
|
|
"restored INTEGER NOT NULL DEFAULT 0," +
|
2015-01-05 13:41:43 -08:00
|
|
|
"profileId INTEGER DEFAULT " + userSerialNumber + "," +
|
|
|
|
|
"rank INTEGER NOT NULL DEFAULT 0" +
|
2009-03-03 19:32:27 -08:00
|
|
|
");");
|
2013-06-18 13:13:40 -07:00
|
|
|
addWorkspacesTable(db);
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2009-03-11 12:11:58 -07:00
|
|
|
// Database was just created, so wipe any previous widgets
|
|
|
|
|
if (mAppWidgetHost != null) {
|
|
|
|
|
mAppWidgetHost.deleteHost();
|
AI 143776: am: CL 143622 Correctly startListening() for widget updates when first boot completes.
During the first boot upgrade, LauncherProvider will deleteHost() to clear out any old appWidgetId bindings. During the first boot, Launcher calls AppWidgetHost.startListening() to watch for widget updates. It also calls loadUserItems(), which loads data from LauncherProvider, triggering the database creation and deleteHost() call. Because deleteHost() removes any existing callbacks, any future widget updates are dropped on the floor. (This can currently be solved by rebooting, because there isn't an upgrade on subsequent boots.)
This bug was particularly evident on vfpioneer-userdebug builds, as there aren't any configuration changes that cause Launcher to be destroyed and recreated. (When destroyed and recreated, we startListening() again, and LauncherProvider doesn't call deleteHost().)
To handle this special case, Launcher creates a ContentObserver pointing at a specific URI, which the LauncherProvider notifies when the AppWidgetHost is reset through deleteHost(), allowing Launcher to correctly startListening() again.
Original author: jsharkey
Merged from: //branches/cupcake/...
Automated import of CL 143776
2009-03-31 14:37:57 -07:00
|
|
|
sendAppWidgetResetNotify();
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
2011-07-13 17:25:49 -07:00
|
|
|
|
2014-11-21 22:42:53 -08:00
|
|
|
// Fresh and clean launcher DB.
|
|
|
|
|
mMaxItemId = initializeMaxItemId(db);
|
|
|
|
|
setFlagEmptyDbCreated();
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:13:40 -07:00
|
|
|
private void addWorkspacesTable(SQLiteDatabase db) {
|
|
|
|
|
db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
|
2015-02-25 10:46:34 -08:00
|
|
|
LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," +
|
2013-09-13 18:01:38 -04:00
|
|
|
LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
|
|
|
|
|
LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
|
2013-06-18 13:13:40 -07:00
|
|
|
");");
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 16:59:08 -07:00
|
|
|
private void removeOrphanedItems(SQLiteDatabase db) {
|
2014-04-17 18:20:45 -07:00
|
|
|
// Delete items directly on the workspace who's screen id doesn't exist
|
|
|
|
|
// "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
|
|
|
|
|
// AND container = -100"
|
|
|
|
|
String removeOrphanedDesktopItems = "DELETE FROM " + TABLE_FAVORITES +
|
|
|
|
|
" WHERE " +
|
2014-04-02 16:59:08 -07:00
|
|
|
LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
|
2014-04-17 18:20:45 -07:00
|
|
|
LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS + ")" +
|
|
|
|
|
" AND " +
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER + " = " +
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
|
|
|
|
db.execSQL(removeOrphanedDesktopItems);
|
|
|
|
|
|
|
|
|
|
// Delete items contained in folders which no longer exist (after above statement)
|
|
|
|
|
// "DELETE FROM favorites WHERE container <> -100 AND container <> -101 AND container
|
|
|
|
|
// NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
|
|
|
|
|
String removeOrphanedFolderItems = "DELETE FROM " + TABLE_FAVORITES +
|
|
|
|
|
" WHERE " +
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER + " <> " +
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER_DESKTOP +
|
|
|
|
|
" AND "
|
|
|
|
|
+ LauncherSettings.Favorites.CONTAINER + " <> " +
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER_HOTSEAT +
|
|
|
|
|
" AND "
|
|
|
|
|
+ LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
|
|
|
|
|
LauncherSettings.Favorites._ID + " FROM " + TABLE_FAVORITES +
|
|
|
|
|
" WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
|
|
|
|
|
LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
|
|
|
|
|
db.execSQL(removeOrphanedFolderItems);
|
2014-04-02 16:59:08 -07:00
|
|
|
}
|
|
|
|
|
|
2013-07-19 13:49:06 -07:00
|
|
|
private void setFlagJustLoadedOldDb() {
|
|
|
|
|
String spKey = LauncherAppState.getSharedPreferencesKey();
|
|
|
|
|
SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
|
|
|
|
|
SharedPreferences.Editor editor = sp.edit();
|
|
|
|
|
editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
|
|
|
|
|
editor.putBoolean(EMPTY_DATABASE_CREATED, false);
|
|
|
|
|
editor.commit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setFlagEmptyDbCreated() {
|
2013-06-11 14:45:48 -04:00
|
|
|
String spKey = LauncherAppState.getSharedPreferencesKey();
|
2012-04-25 15:48:32 -07:00
|
|
|
SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
|
|
|
|
|
SharedPreferences.Editor editor = sp.edit();
|
2013-07-19 13:49:06 -07:00
|
|
|
editor.putBoolean(EMPTY_DATABASE_CREATED, true);
|
|
|
|
|
editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
|
2012-04-25 15:48:32 -07:00
|
|
|
editor.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
2013-07-19 13:49:06 -07:00
|
|
|
if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
int version = oldVersion;
|
2014-11-21 22:42:53 -08:00
|
|
|
if (version < MIN_DATABASE_VERSION) {
|
|
|
|
|
// The version cannot be lower that this, as Launcher3 never supported a lower
|
|
|
|
|
// version of the DB.
|
|
|
|
|
createEmptyDB(db);
|
|
|
|
|
version = DATABASE_VERSION;
|
2012-04-23 21:35:11 -07:00
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:13:40 -07:00
|
|
|
if (version < 13) {
|
|
|
|
|
// With the new shrink-wrapped and re-orderable workspaces, it makes sense
|
|
|
|
|
// to persist workspace screens and their relative order.
|
|
|
|
|
mMaxScreenId = 0;
|
|
|
|
|
|
|
|
|
|
addWorkspacesTable(db);
|
|
|
|
|
version = 13;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-16 14:02:29 -04:00
|
|
|
if (version < 14) {
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
// Insert new column for holding widget provider name
|
|
|
|
|
db.execSQL("ALTER TABLE favorites " +
|
|
|
|
|
"ADD COLUMN appWidgetProvider TEXT;");
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
version = 14;
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
|
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-13 18:01:38 -04:00
|
|
|
if (version < 15) {
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
// Insert new column for holding update timestamp
|
|
|
|
|
db.execSQL("ALTER TABLE favorites " +
|
|
|
|
|
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
|
|
|
|
|
db.execSQL("ALTER TABLE workspaceScreens " +
|
|
|
|
|
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
version = 15;
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
|
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-16 18:13:56 -05:00
|
|
|
|
|
|
|
|
if (version < 16) {
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
// Insert new column for holding restore status
|
|
|
|
|
db.execSQL("ALTER TABLE favorites " +
|
|
|
|
|
"ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;");
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
version = 16;
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
|
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-21 14:09:53 -08:00
|
|
|
if (version < 17) {
|
|
|
|
|
// We use the db version upgrade here to identify users who may not have seen
|
|
|
|
|
// clings yet (because they weren't available), but for whom the clings are now
|
|
|
|
|
// available (tablet users). Because one of the possible cling flows (migration)
|
|
|
|
|
// is very destructive (wipes out workspaces), we want to prevent this from showing
|
|
|
|
|
// until clear data. We do so by marking that the clings have been shown.
|
|
|
|
|
LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext);
|
|
|
|
|
version = 17;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-02 16:59:08 -07:00
|
|
|
if (version < 18) {
|
2014-04-17 18:20:45 -07:00
|
|
|
// No-op
|
|
|
|
|
version = 18;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (version < 19) {
|
2014-04-02 16:59:08 -07:00
|
|
|
// Due to a data loss bug, some users may have items associated with screen ids
|
|
|
|
|
// which no longer exist. Since this can cause other problems, and since the user
|
|
|
|
|
// will never see these items anyway, we use database upgrade as an opportunity to
|
|
|
|
|
// clean things up.
|
2014-04-17 18:20:45 -07:00
|
|
|
removeOrphanedItems(db);
|
|
|
|
|
version = 19;
|
2014-04-02 16:59:08 -07:00
|
|
|
}
|
|
|
|
|
|
2014-04-30 03:02:21 +01:00
|
|
|
if (version < 20) {
|
|
|
|
|
// Add userId column
|
|
|
|
|
if (addProfileColumn(db)) {
|
|
|
|
|
version = 20;
|
|
|
|
|
}
|
|
|
|
|
// else old version remains, which means we wipe old data
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-05 13:41:43 -08:00
|
|
|
if (version < 21) {
|
|
|
|
|
if (updateFolderItemsRank(db, true)) {
|
|
|
|
|
version = 21;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-25 10:46:34 -08:00
|
|
|
if (version == 21) {
|
|
|
|
|
// Recreate workspace table with screen id a primary key
|
|
|
|
|
if (recreateWorkspaceTable(db)) {
|
|
|
|
|
version = 22;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
if (version != DATABASE_VERSION) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Destroying all old data.");
|
2014-11-21 22:42:53 -08:00
|
|
|
createEmptyDB(db);
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
}
|
2009-12-02 20:10:07 -08:00
|
|
|
|
2014-05-21 19:01:57 -07:00
|
|
|
@Override
|
|
|
|
|
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
|
|
|
// This shouldn't happen -- throw our hands up in the air and start over.
|
|
|
|
|
Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
|
|
|
|
|
". Wiping databse.");
|
2014-09-26 22:09:29 -07:00
|
|
|
createEmptyDB(db);
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-21 19:01:57 -07:00
|
|
|
|
2014-09-26 22:09:29 -07:00
|
|
|
/**
|
|
|
|
|
* Clears all the data for a fresh start.
|
|
|
|
|
*/
|
|
|
|
|
public void createEmptyDB(SQLiteDatabase db) {
|
2014-05-21 19:01:57 -07:00
|
|
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
|
|
|
|
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
|
|
|
|
|
onCreate(db);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-25 10:46:34 -08:00
|
|
|
/**
|
|
|
|
|
* Recreates workspace table and migrates data to the new table.
|
|
|
|
|
*/
|
|
|
|
|
public boolean recreateWorkspaceTable(SQLiteDatabase db) {
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
Cursor c = db.query(TABLE_WORKSPACE_SCREENS,
|
|
|
|
|
new String[] {LauncherSettings.WorkspaceScreens._ID},
|
|
|
|
|
null, null, null, null,
|
|
|
|
|
LauncherSettings.WorkspaceScreens.SCREEN_RANK);
|
|
|
|
|
ArrayList<Long> sortedIDs = new ArrayList<Long>();
|
|
|
|
|
long maxId = 0;
|
|
|
|
|
try {
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
Long id = c.getLong(0);
|
|
|
|
|
if (!sortedIDs.contains(id)) {
|
|
|
|
|
sortedIDs.add(id);
|
|
|
|
|
maxId = Math.max(maxId, id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
|
|
|
|
|
addWorkspacesTable(db);
|
|
|
|
|
|
|
|
|
|
// Add all screen ids back
|
|
|
|
|
int total = sortedIDs.size();
|
|
|
|
|
for (int i = 0; i < total; i++) {
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i));
|
|
|
|
|
values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
|
|
|
addModifiedTime(values);
|
|
|
|
|
db.insertOrThrow(TABLE_WORKSPACE_SCREENS, null, values);
|
|
|
|
|
}
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
mMaxScreenId = maxId;
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
|
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-05 13:41:43 -08:00
|
|
|
private boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
if (addRankColumn) {
|
|
|
|
|
// Insert new column for holding rank
|
|
|
|
|
db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get a map for folder ID to folder width
|
|
|
|
|
Cursor c = db.rawQuery("SELECT container, MAX(cellX) FROM favorites"
|
|
|
|
|
+ " WHERE container IN (SELECT _id FROM favorites WHERE itemType = ?)"
|
|
|
|
|
+ " GROUP BY container;",
|
|
|
|
|
new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)});
|
|
|
|
|
|
|
|
|
|
while (c.moveToNext()) {
|
2015-02-10 19:52:36 -08:00
|
|
|
db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE "
|
|
|
|
|
+ "container=? AND cellX IS NOT NULL AND cellY IS NOT NULL;",
|
2015-01-05 13:41:43 -08:00
|
|
|
new Object[] {c.getLong(1) + 1, c.getLong(0)});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.close();
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
|
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-30 03:02:21 +01:00
|
|
|
private boolean addProfileColumn(SQLiteDatabase db) {
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
|
|
|
|
|
// Default to the serial number of this user, for older
|
|
|
|
|
// shortcuts.
|
|
|
|
|
long userSerialNumber = userManager.getSerialNumberForUser(
|
|
|
|
|
UserHandleCompat.myUserHandle());
|
|
|
|
|
// Insert new column for holding user serial number
|
|
|
|
|
db.execSQL("ALTER TABLE favorites " +
|
|
|
|
|
"ADD COLUMN profileId INTEGER DEFAULT "
|
|
|
|
|
+ userSerialNumber + ";");
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
|
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-28 14:59:33 -07:00
|
|
|
// Generates a new ID to use for an object in your database. This method should be only
|
|
|
|
|
// called from the main UI thread. As an exception, we do call it when we call the
|
|
|
|
|
// constructor from the worker thread; however, this doesn't extend until after the
|
|
|
|
|
// constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
|
|
|
|
|
// after that point
|
2014-08-06 09:55:36 -07:00
|
|
|
@Override
|
2013-06-18 13:13:40 -07:00
|
|
|
public long generateNewItemId() {
|
|
|
|
|
if (mMaxItemId < 0) {
|
|
|
|
|
throw new RuntimeException("Error: max item id was not initialized");
|
2011-04-28 14:59:33 -07:00
|
|
|
}
|
2013-06-18 13:13:40 -07:00
|
|
|
mMaxItemId += 1;
|
|
|
|
|
return mMaxItemId;
|
2011-04-28 14:59:33 -07:00
|
|
|
}
|
|
|
|
|
|
2014-08-06 09:55:36 -07:00
|
|
|
@Override
|
|
|
|
|
public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
|
|
|
|
|
return dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-19 13:49:06 -07:00
|
|
|
public void updateMaxItemId(long id) {
|
|
|
|
|
mMaxItemId = id + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-20 17:22:11 -05:00
|
|
|
public void checkId(String table, ContentValues values) {
|
|
|
|
|
long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
|
|
|
|
|
if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
|
|
|
|
|
mMaxScreenId = Math.max(id, mMaxScreenId);
|
|
|
|
|
} else {
|
|
|
|
|
mMaxItemId = Math.max(id, mMaxItemId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:13:40 -07:00
|
|
|
private long initializeMaxItemId(SQLiteDatabase db) {
|
2011-04-28 14:59:33 -07:00
|
|
|
Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
|
|
|
|
|
|
|
|
|
|
// get the result
|
|
|
|
|
final int maxIdIndex = 0;
|
|
|
|
|
long id = -1;
|
|
|
|
|
if (c != null && c.moveToNext()) {
|
|
|
|
|
id = c.getLong(maxIdIndex);
|
|
|
|
|
}
|
2011-10-13 04:55:35 -07:00
|
|
|
if (c != null) {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
2011-04-28 14:59:33 -07:00
|
|
|
|
|
|
|
|
if (id == -1) {
|
2013-06-18 13:13:40 -07:00
|
|
|
throw new RuntimeException("Error: could not query max item id");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generates a new ID to use for an workspace screen in your database. This method
|
|
|
|
|
// should be only called from the main UI thread. As an exception, we do call it when we
|
|
|
|
|
// call the constructor from the worker thread; however, this doesn't extend until after the
|
|
|
|
|
// constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
|
|
|
|
|
// after that point
|
|
|
|
|
public long generateNewScreenId() {
|
|
|
|
|
if (mMaxScreenId < 0) {
|
|
|
|
|
throw new RuntimeException("Error: max screen id was not initialized");
|
|
|
|
|
}
|
|
|
|
|
mMaxScreenId += 1;
|
2013-11-15 13:05:06 -08:00
|
|
|
// Log to disk
|
|
|
|
|
Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
|
2013-06-18 13:13:40 -07:00
|
|
|
return mMaxScreenId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void updateMaxScreenId(long maxScreenId) {
|
2013-11-15 13:05:06 -08:00
|
|
|
// Log to disk
|
|
|
|
|
Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
|
2013-06-18 13:13:40 -07:00
|
|
|
mMaxScreenId = maxScreenId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long initializeMaxScreenId(SQLiteDatabase db) {
|
|
|
|
|
Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
|
|
|
|
|
|
|
|
|
|
// get the result
|
|
|
|
|
final int maxIdIndex = 0;
|
|
|
|
|
long id = -1;
|
|
|
|
|
if (c != null && c.moveToNext()) {
|
|
|
|
|
id = c.getLong(maxIdIndex);
|
|
|
|
|
}
|
|
|
|
|
if (c != null) {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (id == -1) {
|
|
|
|
|
throw new RuntimeException("Error: could not query max screen id");
|
2011-04-28 14:59:33 -07:00
|
|
|
}
|
|
|
|
|
|
2013-11-15 13:05:06 -08:00
|
|
|
// Log to disk
|
|
|
|
|
Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
|
2011-04-28 14:59:33 -07:00
|
|
|
return id;
|
2010-02-08 13:44:00 -08:00
|
|
|
}
|
|
|
|
|
|
2014-07-23 14:49:38 -07:00
|
|
|
private boolean initializeExternalAdd(ContentValues values) {
|
|
|
|
|
// 1. Ensure that externally added items have a valid item id
|
|
|
|
|
long id = generateNewItemId();
|
|
|
|
|
values.put(LauncherSettings.Favorites._ID, id);
|
|
|
|
|
|
|
|
|
|
// 2. In the case of an app widget, and if no app widget id is specified, we
|
|
|
|
|
// attempt allocate and bind the widget.
|
|
|
|
|
Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
|
|
|
|
|
if (itemType != null &&
|
|
|
|
|
itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
|
|
|
|
|
!values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
|
|
|
|
|
|
|
|
|
|
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
|
|
|
|
|
ComponentName cn = ComponentName.unflattenFromString(
|
|
|
|
|
values.getAsString(Favorites.APPWIDGET_PROVIDER));
|
|
|
|
|
|
|
|
|
|
if (cn != null) {
|
|
|
|
|
try {
|
|
|
|
|
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
|
2014-07-23 18:21:20 -07:00
|
|
|
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
|
2014-10-14 08:55:28 -07:00
|
|
|
if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
|
2014-10-14 15:04:34 -07:00
|
|
|
return false;
|
2014-07-23 14:49:38 -07:00
|
|
|
}
|
|
|
|
|
} catch (RuntimeException e) {
|
|
|
|
|
Log.e(TAG, "Failed to initialize external widget", e);
|
2014-10-14 08:55:28 -07:00
|
|
|
return false;
|
2014-07-23 14:49:38 -07:00
|
|
|
}
|
2014-10-14 08:55:28 -07:00
|
|
|
} else {
|
|
|
|
|
return false;
|
2014-07-23 14:49:38 -07:00
|
|
|
}
|
|
|
|
|
}
|
2014-07-31 00:09:45 -07:00
|
|
|
|
|
|
|
|
// Add screen id if not present
|
|
|
|
|
long screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
|
|
|
|
|
if (!addScreenIdIfNecessary(screenId)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-07-23 14:49:38 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-31 00:09:45 -07:00
|
|
|
// Returns true of screen id exists, or if successfully added
|
|
|
|
|
private boolean addScreenIdIfNecessary(long screenId) {
|
|
|
|
|
if (!hasScreenId(screenId)) {
|
|
|
|
|
int rank = getMaxScreenRank() + 1;
|
|
|
|
|
|
|
|
|
|
ContentValues v = new ContentValues();
|
|
|
|
|
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
|
|
|
|
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
|
|
|
|
|
if (dbInsertAndCheck(this, getWritableDatabase(),
|
|
|
|
|
TABLE_WORKSPACE_SCREENS, null, v) < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean hasScreenId(long screenId) {
|
|
|
|
|
SQLiteDatabase db = getWritableDatabase();
|
|
|
|
|
Cursor c = db.rawQuery("SELECT * FROM " + TABLE_WORKSPACE_SCREENS + " WHERE "
|
|
|
|
|
+ LauncherSettings.WorkspaceScreens._ID + " = " + screenId, null);
|
|
|
|
|
if (c != null) {
|
|
|
|
|
int count = c.getCount();
|
|
|
|
|
c.close();
|
|
|
|
|
return count > 0;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int getMaxScreenRank() {
|
|
|
|
|
SQLiteDatabase db = getWritableDatabase();
|
|
|
|
|
Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens.SCREEN_RANK
|
|
|
|
|
+ ") FROM " + TABLE_WORKSPACE_SCREENS, null);
|
|
|
|
|
|
|
|
|
|
// get the result
|
|
|
|
|
final int maxRankIndex = 0;
|
|
|
|
|
int rank = -1;
|
|
|
|
|
if (c != null && c.moveToNext()) {
|
|
|
|
|
rank = c.getInt(maxRankIndex);
|
|
|
|
|
}
|
|
|
|
|
if (c != null) {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rank;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-01 15:33:41 -07:00
|
|
|
private int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
|
2014-05-15 14:04:01 -07:00
|
|
|
ArrayList<Long> screenIds = new ArrayList<Long>();
|
2014-08-06 09:55:36 -07:00
|
|
|
// TODO: Use multiple loaders with fall-back and transaction.
|
|
|
|
|
int count = loader.loadLayout(db, screenIds);
|
2014-05-15 14:04:01 -07:00
|
|
|
|
|
|
|
|
// Add the screens specified by the items above
|
|
|
|
|
Collections.sort(screenIds);
|
|
|
|
|
int rank = 0;
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
for (Long id : screenIds) {
|
|
|
|
|
values.clear();
|
|
|
|
|
values.put(LauncherSettings.WorkspaceScreens._ID, id);
|
|
|
|
|
values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
|
|
|
|
|
if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values) < 0) {
|
|
|
|
|
throw new RuntimeException("Failed initialize screen table"
|
|
|
|
|
+ "from default layout");
|
|
|
|
|
}
|
|
|
|
|
rank++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure that the max ids are initialized
|
|
|
|
|
mMaxItemId = initializeMaxItemId(db);
|
|
|
|
|
mMaxScreenId = initializeMaxScreenId(db);
|
2014-07-31 00:09:45 -07:00
|
|
|
|
2014-05-15 14:04:01 -07:00
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-06 09:55:36 -07:00
|
|
|
private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
|
2014-01-09 15:01:33 -05:00
|
|
|
final ContentResolver resolver = mContext.getContentResolver();
|
|
|
|
|
Cursor c = null;
|
|
|
|
|
int count = 0;
|
|
|
|
|
int curScreen = 0;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
c = resolver.query(uri, null, null, null, "title ASC");
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// Ignore
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We already have a favorites database in the old provider
|
|
|
|
|
if (c != null) {
|
|
|
|
|
try {
|
|
|
|
|
if (c.getCount() > 0) {
|
|
|
|
|
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
|
|
|
|
|
final int intentIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
|
|
|
|
|
final int titleIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
|
|
|
|
|
final int iconTypeIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
|
|
|
|
|
final int iconIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
|
|
|
|
|
final int iconPackageIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
|
|
|
|
|
final int iconResourceIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
|
|
|
|
|
final int containerIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
|
|
|
|
|
final int itemTypeIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
|
|
|
|
|
final int screenIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
|
|
|
|
|
final int cellXIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
|
|
|
|
|
final int cellYIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
|
|
|
|
|
final int uriIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
|
|
|
|
|
final int displayModeIndex
|
|
|
|
|
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
|
2014-04-30 03:02:21 +01:00
|
|
|
final int profileIndex
|
|
|
|
|
= c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID);
|
2014-01-09 15:01:33 -05:00
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
int curX = 0;
|
|
|
|
|
int curY = 0;
|
|
|
|
|
|
|
|
|
|
final LauncherAppState app = LauncherAppState.getInstance();
|
|
|
|
|
final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
|
|
|
|
|
final int width = (int) grid.numColumns;
|
|
|
|
|
final int height = (int) grid.numRows;
|
|
|
|
|
final int hotseatWidth = (int) grid.numHotseatIcons;
|
|
|
|
|
|
|
|
|
|
final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
|
|
|
|
|
|
2014-01-15 18:13:55 -08:00
|
|
|
final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
|
|
|
|
|
final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
|
2014-03-06 23:48:04 -05:00
|
|
|
final SparseArray<ContentValues> hotseat = new SparseArray<ContentValues>();
|
2014-01-09 15:01:33 -05:00
|
|
|
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
final int itemType = c.getInt(itemTypeIndex);
|
|
|
|
|
if (itemType != Favorites.ITEM_TYPE_APPLICATION
|
|
|
|
|
&& itemType != Favorites.ITEM_TYPE_SHORTCUT
|
|
|
|
|
&& itemType != Favorites.ITEM_TYPE_FOLDER) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final int cellX = c.getInt(cellXIndex);
|
|
|
|
|
final int cellY = c.getInt(cellYIndex);
|
|
|
|
|
final int screen = c.getInt(screenIndex);
|
|
|
|
|
int container = c.getInt(containerIndex);
|
|
|
|
|
final String intentStr = c.getString(intentIndex);
|
2014-04-30 03:02:21 +01:00
|
|
|
|
|
|
|
|
UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
|
|
|
|
|
UserHandleCompat userHandle;
|
|
|
|
|
final long userSerialNumber;
|
|
|
|
|
if (profileIndex != -1 && !c.isNull(profileIndex)) {
|
|
|
|
|
userSerialNumber = c.getInt(profileIndex);
|
|
|
|
|
userHandle = userManager.getUserForSerialNumber(userSerialNumber);
|
|
|
|
|
} else {
|
|
|
|
|
// Default to the serial number of this user, for older
|
|
|
|
|
// shortcuts.
|
|
|
|
|
userHandle = UserHandleCompat.myUserHandle();
|
|
|
|
|
userSerialNumber = userManager.getSerialNumberForUser(userHandle);
|
|
|
|
|
}
|
2014-11-14 11:59:57 -08:00
|
|
|
|
|
|
|
|
if (userHandle == null) {
|
|
|
|
|
Launcher.addDumpLog(TAG, "skipping deleted user", true);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-09 15:01:33 -05:00
|
|
|
Launcher.addDumpLog(TAG, "migrating \""
|
2014-03-06 23:48:04 -05:00
|
|
|
+ c.getString(titleIndex) + "\" ("
|
|
|
|
|
+ cellX + "," + cellY + "@"
|
|
|
|
|
+ LauncherSettings.Favorites.containerToString(container)
|
|
|
|
|
+ "/" + screen
|
|
|
|
|
+ "): " + intentStr, true);
|
2014-01-09 15:01:33 -05:00
|
|
|
|
|
|
|
|
if (itemType != Favorites.ITEM_TYPE_FOLDER) {
|
2014-01-15 15:18:08 -08:00
|
|
|
|
|
|
|
|
final Intent intent;
|
|
|
|
|
final ComponentName cn;
|
|
|
|
|
try {
|
|
|
|
|
intent = Intent.parseUri(intentStr, 0);
|
|
|
|
|
} catch (URISyntaxException e) {
|
|
|
|
|
// bogus intent?
|
|
|
|
|
Launcher.addDumpLog(TAG,
|
|
|
|
|
"skipping invalid intent uri", true);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cn = intent.getComponent();
|
2014-01-09 15:01:33 -05:00
|
|
|
if (TextUtils.isEmpty(intentStr)) {
|
|
|
|
|
// no intent? no icon
|
|
|
|
|
Launcher.addDumpLog(TAG, "skipping empty intent", true);
|
|
|
|
|
continue;
|
2014-01-15 18:13:55 -08:00
|
|
|
} else if (cn != null &&
|
2014-04-30 03:02:21 +01:00
|
|
|
!LauncherModel.isValidPackageActivity(mContext, cn,
|
|
|
|
|
userHandle)) {
|
2014-01-15 15:18:08 -08:00
|
|
|
// component no longer exists.
|
2014-01-15 18:13:55 -08:00
|
|
|
Launcher.addDumpLog(TAG, "skipping item whose component " +
|
2014-01-15 15:18:08 -08:00
|
|
|
"no longer exists.", true);
|
|
|
|
|
continue;
|
2014-01-15 18:13:55 -08:00
|
|
|
} else if (container ==
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
|
|
|
|
// Dedupe icons directly on the workspace
|
|
|
|
|
|
2014-01-15 15:18:08 -08:00
|
|
|
// Canonicalize
|
|
|
|
|
// the Play Store sets the package parameter, but Launcher
|
2014-10-24 12:20:20 -07:00
|
|
|
// does not, so we clear that out to keep them the same.
|
|
|
|
|
// Also ignore intent flags for the purposes of deduping.
|
2014-01-15 15:18:08 -08:00
|
|
|
intent.setPackage(null);
|
2014-10-24 12:20:20 -07:00
|
|
|
int flags = intent.getFlags();
|
|
|
|
|
intent.setFlags(0);
|
2014-01-15 15:18:08 -08:00
|
|
|
final String key = intent.toUri(0);
|
2014-10-24 12:20:20 -07:00
|
|
|
intent.setFlags(flags);
|
2014-01-15 15:18:08 -08:00
|
|
|
if (seenIntents.contains(key)) {
|
|
|
|
|
Launcher.addDumpLog(TAG, "skipping duplicate", true);
|
2014-01-09 15:01:33 -05:00
|
|
|
continue;
|
2014-01-15 15:18:08 -08:00
|
|
|
} else {
|
|
|
|
|
seenIntents.add(key);
|
2014-01-09 15:01:33 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ContentValues values = new ContentValues(c.getColumnCount());
|
|
|
|
|
values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.INTENT, intentStr);
|
|
|
|
|
values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.ICON_TYPE,
|
|
|
|
|
c.getInt(iconTypeIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.ICON_PACKAGE,
|
|
|
|
|
c.getString(iconPackageIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.ICON_RESOURCE,
|
|
|
|
|
c.getString(iconResourceIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
|
|
|
|
|
values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
|
|
|
|
|
values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.DISPLAY_MODE,
|
|
|
|
|
c.getInt(displayModeIndex));
|
2014-04-30 03:02:21 +01:00
|
|
|
values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
|
2014-01-09 15:01:33 -05:00
|
|
|
|
2014-03-06 23:48:04 -05:00
|
|
|
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
|
|
|
|
|
hotseat.put(screen, values);
|
2014-01-09 15:01:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
|
|
|
|
// In a folder or in the hotseat, preserve position
|
|
|
|
|
values.put(LauncherSettings.Favorites.SCREEN, screen);
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLX, cellX);
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLY, cellY);
|
|
|
|
|
} else {
|
2014-01-15 18:13:55 -08:00
|
|
|
// For items contained directly on one of the workspace screen,
|
|
|
|
|
// we'll determine their location (screen, x, y) in a second pass.
|
2014-01-09 15:01:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
values.put(LauncherSettings.Favorites.CONTAINER, container);
|
|
|
|
|
|
2014-01-15 18:13:55 -08:00
|
|
|
if (itemType != Favorites.ITEM_TYPE_FOLDER) {
|
|
|
|
|
shortcuts.add(values);
|
|
|
|
|
} else {
|
|
|
|
|
folders.add(values);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-06 23:48:04 -05:00
|
|
|
// Now that we have all the hotseat icons, let's go through them left-right
|
|
|
|
|
// and assign valid locations for them in the new hotseat
|
|
|
|
|
final int N = hotseat.size();
|
|
|
|
|
for (int idx=0; idx<N; idx++) {
|
|
|
|
|
int hotseatX = hotseat.keyAt(idx);
|
|
|
|
|
ContentValues values = hotseat.valueAt(idx);
|
|
|
|
|
|
|
|
|
|
if (hotseatX == grid.hotseatAllAppsRank) {
|
|
|
|
|
// let's drop this in the next available hole in the hotseat
|
|
|
|
|
while (++hotseatX < hotseatWidth) {
|
|
|
|
|
if (hotseat.get(hotseatX) == null) {
|
|
|
|
|
// found a spot! move it here
|
|
|
|
|
values.put(LauncherSettings.Favorites.SCREEN,
|
|
|
|
|
hotseatX);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (hotseatX >= hotseatWidth) {
|
|
|
|
|
// no room for you in the hotseat? it's off to the desktop with you
|
|
|
|
|
values.put(LauncherSettings.Favorites.CONTAINER,
|
|
|
|
|
Favorites.CONTAINER_DESKTOP);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-15 18:13:55 -08:00
|
|
|
final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
|
|
|
|
|
// Folders first
|
|
|
|
|
allItems.addAll(folders);
|
|
|
|
|
// Then shortcuts
|
|
|
|
|
allItems.addAll(shortcuts);
|
|
|
|
|
|
|
|
|
|
// Layout all the folders
|
|
|
|
|
for (ContentValues values: allItems) {
|
|
|
|
|
if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
|
|
|
|
|
LauncherSettings.Favorites.CONTAINER_DESKTOP) {
|
|
|
|
|
// Hotseat items and folder items have already had their
|
|
|
|
|
// location information set. Nothing to be done here.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
values.put(LauncherSettings.Favorites.SCREEN, curScreen);
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLX, curX);
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLY, curY);
|
|
|
|
|
curX = (curX + 1) % width;
|
|
|
|
|
if (curX == 0) {
|
|
|
|
|
curY = (curY + 1);
|
|
|
|
|
}
|
|
|
|
|
// Leave the last row of icons blank on every screen
|
|
|
|
|
if (curY == height - 1) {
|
|
|
|
|
curScreen = (int) generateNewScreenId();
|
|
|
|
|
curY = 0;
|
|
|
|
|
}
|
2014-01-09 15:01:33 -05:00
|
|
|
}
|
|
|
|
|
|
2014-01-15 18:13:55 -08:00
|
|
|
if (allItems.size() > 0) {
|
2014-01-09 15:01:33 -05:00
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
2014-01-15 18:13:55 -08:00
|
|
|
for (ContentValues row: allItems) {
|
|
|
|
|
if (row == null) continue;
|
|
|
|
|
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
|
2014-01-09 15:01:33 -05:00
|
|
|
< 0) {
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
for (i=0; i<=curScreen; i++) {
|
|
|
|
|
final ContentValues values = new ContentValues();
|
|
|
|
|
values.put(LauncherSettings.WorkspaceScreens._ID, i);
|
|
|
|
|
values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
|
|
|
|
|
if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
|
|
|
|
|
< 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
2015-01-05 13:41:43 -08:00
|
|
|
|
|
|
|
|
updateFolderItemsRank(db, false);
|
2014-01-09 15:01:33 -05:00
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
|
|
|
|
|
+ (curScreen+1) + " screens", true);
|
|
|
|
|
|
|
|
|
|
// ensure that new screens are created to hold these icons
|
|
|
|
|
setFlagJustLoadedOldDb();
|
|
|
|
|
|
|
|
|
|
// Update max IDs; very important since we just grabbed IDs from another database
|
|
|
|
|
mMaxItemId = initializeMaxItemId(db);
|
|
|
|
|
mMaxScreenId = initializeMaxScreenId(db);
|
|
|
|
|
if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
|
|
|
|
|
}
|
2009-10-30 16:36:56 -07:00
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
static class SqlArguments {
|
|
|
|
|
public final String table;
|
|
|
|
|
public final String where;
|
|
|
|
|
public final String[] args;
|
|
|
|
|
|
|
|
|
|
SqlArguments(Uri url, String where, String[] args) {
|
|
|
|
|
if (url.getPathSegments().size() == 1) {
|
|
|
|
|
this.table = url.getPathSegments().get(0);
|
|
|
|
|
this.where = where;
|
|
|
|
|
this.args = args;
|
|
|
|
|
} else if (url.getPathSegments().size() != 2) {
|
|
|
|
|
throw new IllegalArgumentException("Invalid URI: " + url);
|
|
|
|
|
} else if (!TextUtils.isEmpty(where)) {
|
|
|
|
|
throw new UnsupportedOperationException("WHERE clause not supported: " + url);
|
|
|
|
|
} else {
|
|
|
|
|
this.table = url.getPathSegments().get(0);
|
2012-04-23 21:35:11 -07:00
|
|
|
this.where = "_id=" + ContentUris.parseId(url);
|
2009-03-03 19:32:27 -08:00
|
|
|
this.args = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SqlArguments(Uri url) {
|
|
|
|
|
if (url.getPathSegments().size() == 1) {
|
|
|
|
|
table = url.getPathSegments().get(0);
|
|
|
|
|
where = null;
|
|
|
|
|
args = null;
|
|
|
|
|
} else {
|
|
|
|
|
throw new IllegalArgumentException("Invalid URI: " + url);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-01-15 18:13:55 -08:00
|
|
|
}
|