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.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-07-30 13:37:37 -07:00
|
|
|
package com.android.launcher2;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2010-01-14 13:26:43 +00:00
|
|
|
import android.app.SearchManager;
|
2009-03-11 12:11:58 -07:00
|
|
|
import android.appwidget.AppWidgetHost;
|
2009-10-30 16:36:56 -07:00
|
|
|
import android.appwidget.AppWidgetManager;
|
2010-01-14 13:26:43 +00:00
|
|
|
import android.appwidget.AppWidgetProviderInfo;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.content.ComponentName;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.content.ContentProvider;
|
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;
|
2012-04-25 15:48:32 -07:00
|
|
|
import android.content.SharedPreferences;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.content.pm.ActivityInfo;
|
|
|
|
|
import android.content.pm.PackageManager;
|
2009-10-30 16:36:56 -07:00
|
|
|
import android.content.res.Resources;
|
2009-03-18 17:39:48 -07:00
|
|
|
import android.content.res.TypedArray;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.content.res.XmlResourceParser;
|
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;
|
|
|
|
|
import android.database.sqlite.SQLiteStatement;
|
2010-02-08 13:44:00 -08:00
|
|
|
import android.graphics.Bitmap;
|
|
|
|
|
import android.graphics.BitmapFactory;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.net.Uri;
|
2012-05-01 10:19:14 -07:00
|
|
|
import android.os.Bundle;
|
2009-03-03 19:32:27 -08:00
|
|
|
import android.provider.Settings;
|
2011-07-27 22:23:47 -07:00
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.util.Xml;
|
2010-02-24 20:01:46 -08:00
|
|
|
|
2012-04-18 14:23:14 -07:00
|
|
|
import com.android.launcher.R;
|
|
|
|
|
import com.android.launcher2.LauncherSettings.Favorites;
|
|
|
|
|
|
|
|
|
|
import org.xmlpull.v1.XmlPullParser;
|
|
|
|
|
import org.xmlpull.v1.XmlPullParserException;
|
|
|
|
|
|
2011-07-27 22:23:47 -07:00
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
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
|
|
|
|
|
|
|
|
private static final String DATABASE_NAME = "launcher.db";
|
2011-07-13 17:25:49 -07:00
|
|
|
|
2012-05-04 16:22:30 -07:00
|
|
|
private static final int DATABASE_VERSION = 11;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2009-07-30 13:37:37 -07:00
|
|
|
static final String AUTHORITY = "com.android.launcher2.settings";
|
2011-07-13 17:25:49 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
static final String TABLE_FAVORITES = "favorites";
|
|
|
|
|
static final String PARAMETER_NOTIFY = "notify";
|
2012-04-25 15:48:32 -07:00
|
|
|
static final String DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED =
|
|
|
|
|
"DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED";
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2012-05-01 10:19:14 -07:00
|
|
|
private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
|
|
|
|
|
"com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
|
|
|
|
|
|
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() {
|
|
|
|
|
mOpenHelper = new DatabaseHelper(getContext());
|
2011-04-28 14:59:33 -07:00
|
|
|
((LauncherApplication) getContext()).setLauncherProvider(this);
|
2009-03-03 19:32:27 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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) {
|
|
|
|
|
if (!values.containsKey(LauncherSettings.Favorites._ID)) {
|
|
|
|
|
throw new RuntimeException("Error: attempting to add item without specifying an id");
|
|
|
|
|
}
|
|
|
|
|
return db.insert(table, nullColumnHack, values);
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-27 22:23:47 -07:00
|
|
|
private static void deleteId(SQLiteDatabase db, long id) {
|
|
|
|
|
Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
|
|
|
|
|
SqlArguments args = new SqlArguments(uri, null, null);
|
|
|
|
|
db.delete(args.table, args.where, args.args);
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
@Override
|
|
|
|
|
public Uri insert(Uri uri, ContentValues initialValues) {
|
|
|
|
|
SqlArguments args = new SqlArguments(uri);
|
|
|
|
|
|
|
|
|
|
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
|
2011-04-28 14:59:33 -07:00
|
|
|
final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
|
2009-03-03 19:32:27 -08:00
|
|
|
if (rowId <= 0) return null;
|
|
|
|
|
|
|
|
|
|
uri = ContentUris.withAppendedId(uri, rowId);
|
|
|
|
|
sendNotify(uri);
|
|
|
|
|
|
|
|
|
|
return uri;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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++) {
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-28 14:59:33 -07:00
|
|
|
public long generateNewId() {
|
|
|
|
|
return mOpenHelper.generateNewId();
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 15:48:32 -07:00
|
|
|
public void loadDefaultFavoritesIfNecessary() {
|
|
|
|
|
String spKey = LauncherApplication.getSharedPreferencesKey();
|
|
|
|
|
SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
|
|
|
|
|
if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
|
|
|
|
|
// Populate favorites table with initial favorites
|
|
|
|
|
SharedPreferences.Editor editor = sp.edit();
|
|
|
|
|
editor.remove(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED);
|
|
|
|
|
mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), R.xml.default_workspace);
|
|
|
|
|
editor.commit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
private static class DatabaseHelper extends SQLiteOpenHelper {
|
|
|
|
|
private static final String TAG_FAVORITES = "favorites";
|
|
|
|
|
private static final String TAG_FAVORITE = "favorite";
|
2009-03-18 17:39:48 -07:00
|
|
|
private static final String TAG_CLOCK = "clock";
|
|
|
|
|
private static final String TAG_SEARCH = "search";
|
2009-10-30 16:36:56 -07:00
|
|
|
private static final String TAG_APPWIDGET = "appwidget";
|
|
|
|
|
private static final String TAG_SHORTCUT = "shortcut";
|
2011-07-27 22:23:47 -07:00
|
|
|
private static final String TAG_FOLDER = "folder";
|
2012-05-01 10:19:14 -07:00
|
|
|
private static final String TAG_EXTRA = "extra";
|
2011-07-13 17:25:49 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
private final Context mContext;
|
2009-03-11 12:11:58 -07:00
|
|
|
private final AppWidgetHost mAppWidgetHost;
|
2011-04-28 14:59:33 -07:00
|
|
|
private long mMaxId = -1;
|
2009-03-03 19:32:27 -08:00
|
|
|
|
|
|
|
|
DatabaseHelper(Context context) {
|
|
|
|
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
|
|
|
|
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
|
|
|
|
|
if (mMaxId == -1) {
|
|
|
|
|
mMaxId = initializeMaxId(getWritableDatabase());
|
|
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
mMaxId = 1;
|
|
|
|
|
|
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," +
|
|
|
|
|
"displayMode INTEGER" +
|
|
|
|
|
");");
|
|
|
|
|
|
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
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
if (!convertDatabase(db)) {
|
2012-04-25 15:48:32 -07:00
|
|
|
// Set a shared pref so that we know we need to load the default workspace later
|
|
|
|
|
setFlagToLoadDefaultWorkspaceLater();
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 15:48:32 -07:00
|
|
|
private void setFlagToLoadDefaultWorkspaceLater() {
|
|
|
|
|
String spKey = LauncherApplication.getSharedPreferencesKey();
|
|
|
|
|
SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
|
|
|
|
|
SharedPreferences.Editor editor = sp.edit();
|
|
|
|
|
editor.putBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, true);
|
|
|
|
|
editor.commit();
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
private boolean convertDatabase(SQLiteDatabase db) {
|
2009-11-11 08:16:49 -08:00
|
|
|
if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
|
2009-03-03 19:32:27 -08:00
|
|
|
boolean converted = false;
|
|
|
|
|
|
|
|
|
|
final Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
|
|
|
|
|
"/old_favorites?notify=true");
|
|
|
|
|
final ContentResolver resolver = mContext.getContentResolver();
|
|
|
|
|
Cursor cursor = null;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
cursor = resolver.query(uri, null, null, null, null);
|
|
|
|
|
} catch (Exception e) {
|
2011-04-28 14:59:33 -07:00
|
|
|
// Ignore
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We already have a favorites database in the old provider
|
|
|
|
|
if (cursor != null && cursor.getCount() > 0) {
|
|
|
|
|
try {
|
|
|
|
|
converted = copyFromCursor(db, cursor) > 0;
|
|
|
|
|
} finally {
|
|
|
|
|
cursor.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (converted) {
|
|
|
|
|
resolver.delete(uri, null, null);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
if (converted) {
|
2009-03-11 12:11:58 -07:00
|
|
|
// Convert widgets from this import into widgets
|
2009-11-11 08:16:49 -08:00
|
|
|
if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
|
2009-03-03 19:32:27 -08:00
|
|
|
convertWidgets(db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return converted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int copyFromCursor(SQLiteDatabase db, Cursor c) {
|
2009-06-09 12:57:21 -07:00
|
|
|
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
|
2009-03-03 19:32:27 -08:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
ContentValues[] rows = new ContentValues[c.getCount()];
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
ContentValues values = new ContentValues(c.getColumnCount());
|
2009-06-09 12:57:21 -07:00
|
|
|
values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
|
2009-03-03 19:32:27 -08:00
|
|
|
values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
|
|
|
|
|
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.CONTAINER, c.getInt(containerIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
|
2009-03-11 12:11:58 -07:00
|
|
|
values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
|
2009-03-03 19:32:27 -08:00
|
|
|
values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
|
|
|
|
|
values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
|
|
|
|
|
rows[i++] = values;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
int total = 0;
|
|
|
|
|
try {
|
|
|
|
|
int numValues = rows.length;
|
|
|
|
|
for (i = 0; i < numValues; i++) {
|
2011-04-28 14:59:33 -07:00
|
|
|
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
|
2009-03-03 19:32:27 -08:00
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
total++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
2009-11-11 08:16:49 -08:00
|
|
|
if (LOGD) Log.d(TAG, "onUpgrade triggered");
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
int version = oldVersion;
|
2009-03-13 13:04:24 -07:00
|
|
|
if (version < 3) {
|
|
|
|
|
// upgrade 1,2 -> 3 added appWidgetId column
|
2009-03-03 19:32:27 -08:00
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
2009-03-11 12:11:58 -07:00
|
|
|
// Insert new column for holding appWidgetIds
|
2009-03-03 19:32:27 -08:00
|
|
|
db.execSQL("ALTER TABLE favorites " +
|
2009-03-13 13:04:24 -07:00
|
|
|
"ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
|
2009-03-03 19:32:27 -08:00
|
|
|
db.setTransactionSuccessful();
|
2009-03-13 13:04:24 -07:00
|
|
|
version = 3;
|
2009-03-03 19:32:27 -08:00
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
2009-03-03 19:32:27 -08:00
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
// Convert existing widgets only if table upgrade was successful
|
2009-03-13 13:04:24 -07:00
|
|
|
if (version == 3) {
|
2009-03-03 19:32:27 -08:00
|
|
|
convertWidgets(db);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-09 12:57:21 -07:00
|
|
|
|
|
|
|
|
if (version < 4) {
|
2009-12-02 20:10:07 -08:00
|
|
|
version = 4;
|
2009-06-09 12:57:21 -07:00
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2010-03-23 15:10:56 -07:00
|
|
|
// Where's version 5?
|
|
|
|
|
// - Donut and sholes on 2.0 shipped with version 4 of launcher1.
|
|
|
|
|
// - Passion shipped on 2.1 with version 6 of launcher2
|
|
|
|
|
// - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
|
|
|
|
|
// but version 5 on there was the updateContactsShortcuts change
|
|
|
|
|
// which was version 6 in launcher 2 (first shipped on passion 2.1r1).
|
|
|
|
|
// The updateContactsShortcuts change is idempotent, so running it twice
|
|
|
|
|
// is okay so we'll do that when upgrading the devices that shipped with it.
|
|
|
|
|
if (version < 6) {
|
2009-11-05 17:17:42 -08:00
|
|
|
// We went from 3 to 5 screens. Move everything 1 to the right
|
|
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
db.execSQL("UPDATE favorites SET screen=(screen + 1);");
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
// Old version remains, which means we wipe old data
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.e(TAG, ex.getMessage(), ex);
|
2009-11-05 17:17:42 -08:00
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2010-03-23 15:10:56 -07:00
|
|
|
// We added the fast track.
|
2009-12-02 20:10:07 -08:00
|
|
|
if (updateContactsShortcuts(db)) {
|
|
|
|
|
version = 6;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-09 15:38:25 +00:00
|
|
|
|
|
|
|
|
if (version < 7) {
|
|
|
|
|
// Version 7 gets rid of the special search widget.
|
|
|
|
|
convertWidgets(db);
|
|
|
|
|
version = 7;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-08 13:44:00 -08:00
|
|
|
if (version < 8) {
|
|
|
|
|
// Version 8 (froyo) has the icons all normalized. This should
|
|
|
|
|
// already be the case in practice, but we now rely on it and don't
|
|
|
|
|
// resample the images each time.
|
|
|
|
|
normalizeIcons(db);
|
|
|
|
|
version = 8;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-13 17:25:49 -07:00
|
|
|
if (version < 9) {
|
|
|
|
|
// The max id is not yet set at this point (onUpgrade is triggered in the ctor
|
|
|
|
|
// before it gets a change to get set, so we need to read it here when we use it)
|
|
|
|
|
if (mMaxId == -1) {
|
|
|
|
|
mMaxId = initializeMaxId(db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add default hotseat icons
|
2011-11-16 18:43:26 -08:00
|
|
|
loadFavorites(db, R.xml.update_workspace);
|
2011-07-13 17:25:49 -07:00
|
|
|
version = 9;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 16:22:30 -07:00
|
|
|
// We bumped the version twice during JB, once to update the launch flags, and once to
|
|
|
|
|
// update the override for the default launch animation.
|
|
|
|
|
if (version < 11) {
|
2012-04-23 21:35:11 -07:00
|
|
|
// Contact shortcuts need a different set of flags to be launched now
|
|
|
|
|
// The updateContactsShortcuts change is idempotent, so we can keep using it like
|
|
|
|
|
// back in the Donut days
|
|
|
|
|
updateContactsShortcuts(db);
|
2012-05-04 16:22:30 -07:00
|
|
|
version = 11;
|
2012-04-23 21:35:11 -07:00
|
|
|
}
|
|
|
|
|
|
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.");
|
2009-03-03 19:32:27 -08:00
|
|
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
|
|
|
|
|
onCreate(db);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-02 20:10:07 -08:00
|
|
|
|
|
|
|
|
private boolean updateContactsShortcuts(SQLiteDatabase db) {
|
|
|
|
|
final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
|
|
|
|
|
new int[] { Favorites.ITEM_TYPE_SHORTCUT });
|
|
|
|
|
|
2012-04-23 21:35:11 -07:00
|
|
|
Cursor c = null;
|
|
|
|
|
final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
|
2009-12-02 20:10:07 -08:00
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
// Select and iterate through each matching widget
|
2012-04-23 21:35:11 -07:00
|
|
|
c = db.query(TABLE_FAVORITES,
|
|
|
|
|
new String[] { Favorites._ID, Favorites.INTENT },
|
2009-12-02 20:10:07 -08:00
|
|
|
selectWhere, null, null, null, null);
|
2012-04-23 21:35:11 -07:00
|
|
|
if (c == null) return false;
|
|
|
|
|
|
2009-12-02 20:10:07 -08:00
|
|
|
if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-12-02 20:10:07 -08:00
|
|
|
final int idIndex = c.getColumnIndex(Favorites._ID);
|
|
|
|
|
final int intentIndex = c.getColumnIndex(Favorites.INTENT);
|
2012-04-23 21:35:11 -07:00
|
|
|
|
|
|
|
|
while (c.moveToNext()) {
|
2009-12-02 20:10:07 -08:00
|
|
|
long favoriteId = c.getLong(idIndex);
|
|
|
|
|
final String intentUri = c.getString(intentIndex);
|
|
|
|
|
if (intentUri != null) {
|
|
|
|
|
try {
|
2012-04-23 21:35:11 -07:00
|
|
|
final Intent intent = Intent.parseUri(intentUri, 0);
|
2009-12-02 20:10:07 -08:00
|
|
|
android.util.Log.d("Home", intent.toString());
|
|
|
|
|
final Uri uri = intent.getData();
|
2012-04-23 21:35:11 -07:00
|
|
|
if (uri != null) {
|
|
|
|
|
final String data = uri.toString();
|
|
|
|
|
if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
|
|
|
|
|
actionQuickContact.equals(intent.getAction())) &&
|
|
|
|
|
(data.startsWith("content://contacts/people/") ||
|
|
|
|
|
data.startsWith("content://com.android.contacts/" +
|
|
|
|
|
"contacts/lookup/"))) {
|
|
|
|
|
|
|
|
|
|
final Intent newIntent = new Intent(actionQuickContact);
|
|
|
|
|
// When starting from the launcher, start in a new, cleared task
|
|
|
|
|
// CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
|
|
|
|
|
// clear the whole thing preemptively here since
|
|
|
|
|
// QuickContactActivity will finish itself when launching other
|
|
|
|
|
// detail activities.
|
|
|
|
|
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
|
|
|
|
|
Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
2012-05-04 16:22:30 -07:00
|
|
|
newIntent.putExtra(
|
|
|
|
|
Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
|
2012-04-23 21:35:11 -07:00
|
|
|
newIntent.setData(uri);
|
|
|
|
|
|
|
|
|
|
final ContentValues values = new ContentValues();
|
|
|
|
|
values.put(LauncherSettings.Favorites.INTENT,
|
|
|
|
|
newIntent.toUri(0));
|
|
|
|
|
|
|
|
|
|
String updateWhere = Favorites._ID + "=" + favoriteId;
|
|
|
|
|
db.update(TABLE_FAVORITES, values, updateWhere, null);
|
|
|
|
|
}
|
2009-12-02 20:10:07 -08:00
|
|
|
}
|
|
|
|
|
} catch (RuntimeException ex) {
|
|
|
|
|
Log.e(TAG, "Problem upgrading shortcut", ex);
|
|
|
|
|
} catch (URISyntaxException e) {
|
2012-04-23 21:35:11 -07:00
|
|
|
Log.e(TAG, "Problem upgrading shortcut", e);
|
2009-12-02 20:10:07 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-12-02 20:10:07 -08:00
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
Log.w(TAG, "Problem while upgrading contacts", ex);
|
|
|
|
|
return false;
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
if (c != null) {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-08 13:44:00 -08:00
|
|
|
private void normalizeIcons(SQLiteDatabase db) {
|
|
|
|
|
Log.d(TAG, "normalizing icons");
|
|
|
|
|
|
2010-02-18 10:34:24 -05:00
|
|
|
db.beginTransaction();
|
2010-02-08 13:44:00 -08:00
|
|
|
Cursor c = null;
|
2010-03-23 17:34:37 -04:00
|
|
|
SQLiteStatement update = null;
|
2010-02-08 13:44:00 -08:00
|
|
|
try {
|
|
|
|
|
boolean logged = false;
|
2010-03-23 17:34:37 -04:00
|
|
|
update = db.compileStatement("UPDATE favorites "
|
2010-02-13 00:08:17 -06:00
|
|
|
+ "SET icon=? WHERE _id=?");
|
2010-02-08 13:44:00 -08:00
|
|
|
|
|
|
|
|
c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
|
|
|
|
|
Favorites.ICON_TYPE_BITMAP, null);
|
|
|
|
|
|
|
|
|
|
final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
|
|
|
|
|
final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
|
|
|
|
|
|
|
|
|
|
while (c.moveToNext()) {
|
|
|
|
|
long id = c.getLong(idIndex);
|
|
|
|
|
byte[] data = c.getBlob(iconIndex);
|
|
|
|
|
try {
|
|
|
|
|
Bitmap bitmap = Utilities.resampleIconBitmap(
|
|
|
|
|
BitmapFactory.decodeByteArray(data, 0, data.length),
|
|
|
|
|
mContext);
|
|
|
|
|
if (bitmap != null) {
|
|
|
|
|
update.bindLong(1, id);
|
|
|
|
|
data = ItemInfo.flattenBitmap(bitmap);
|
|
|
|
|
if (data != null) {
|
|
|
|
|
update.bindBlob(2, data);
|
|
|
|
|
update.execute();
|
|
|
|
|
}
|
|
|
|
|
bitmap.recycle();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
if (!logged) {
|
|
|
|
|
Log.e(TAG, "Failed normalizing icon " + id, e);
|
|
|
|
|
} else {
|
|
|
|
|
Log.e(TAG, "Also failed normalizing icon " + id);
|
|
|
|
|
}
|
|
|
|
|
logged = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-02-19 11:15:40 +00:00
|
|
|
db.setTransactionSuccessful();
|
2010-02-08 13:44:00 -08:00
|
|
|
} catch (SQLException ex) {
|
|
|
|
|
Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
|
|
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
2010-03-23 17:34:37 -04:00
|
|
|
if (update != null) {
|
|
|
|
|
update.close();
|
|
|
|
|
}
|
2010-02-08 13:44:00 -08:00
|
|
|
if (c != null) {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
|
|
public long generateNewId() {
|
|
|
|
|
if (mMaxId < 0) {
|
|
|
|
|
throw new RuntimeException("Error: max id was not initialized");
|
|
|
|
|
}
|
|
|
|
|
mMaxId += 1;
|
|
|
|
|
return mMaxId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long initializeMaxId(SQLiteDatabase db) {
|
|
|
|
|
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) {
|
|
|
|
|
throw new RuntimeException("Error: could not query max id");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return id;
|
2010-02-08 13:44:00 -08:00
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
/**
|
2009-03-11 12:11:58 -07:00
|
|
|
* Upgrade existing clock and photo frame widgets into their new widget
|
2009-12-16 13:19:47 +00:00
|
|
|
* equivalents.
|
2009-03-03 19:32:27 -08:00
|
|
|
*/
|
|
|
|
|
private void convertWidgets(SQLiteDatabase db) {
|
2009-12-15 13:33:11 +00:00
|
|
|
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
|
2009-03-03 19:32:27 -08:00
|
|
|
final int[] bindSources = new int[] {
|
|
|
|
|
Favorites.ITEM_TYPE_WIDGET_CLOCK,
|
|
|
|
|
Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
|
2009-12-09 15:38:25 +00:00
|
|
|
Favorites.ITEM_TYPE_WIDGET_SEARCH,
|
2009-03-03 19:32:27 -08:00
|
|
|
};
|
2009-12-09 15:38:25 +00:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
Cursor c = null;
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
db.beginTransaction();
|
|
|
|
|
try {
|
|
|
|
|
// Select and iterate through each matching widget
|
2009-12-09 15:38:25 +00:00
|
|
|
c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
|
2009-03-03 19:32:27 -08:00
|
|
|
selectWhere, null, null, null, null);
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-11-11 08:16:49 -08:00
|
|
|
if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
final ContentValues values = new ContentValues();
|
|
|
|
|
while (c != null && c.moveToNext()) {
|
|
|
|
|
long favoriteId = c.getLong(0);
|
2009-12-09 15:38:25 +00:00
|
|
|
int favoriteType = c.getInt(1);
|
|
|
|
|
|
2009-03-11 12:11:58 -07:00
|
|
|
// Allocate and update database with new appWidgetId
|
2009-03-03 19:32:27 -08:00
|
|
|
try {
|
2009-03-11 12:11:58 -07:00
|
|
|
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-11-11 08:16:49 -08:00
|
|
|
if (LOGD) {
|
|
|
|
|
Log.d(TAG, "allocated appWidgetId=" + appWidgetId
|
|
|
|
|
+ " for favoriteId=" + favoriteId);
|
|
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
values.clear();
|
2009-12-09 15:38:25 +00:00
|
|
|
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
|
|
|
|
|
values.put(Favorites.APPWIDGET_ID, appWidgetId);
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
// Original widgets might not have valid spans when upgrading
|
2009-12-09 15:38:25 +00:00
|
|
|
if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
|
|
|
|
|
values.put(LauncherSettings.Favorites.SPANX, 4);
|
|
|
|
|
values.put(LauncherSettings.Favorites.SPANY, 1);
|
|
|
|
|
} else {
|
|
|
|
|
values.put(LauncherSettings.Favorites.SPANX, 2);
|
|
|
|
|
values.put(LauncherSettings.Favorites.SPANY, 2);
|
|
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
|
|
|
|
|
String updateWhere = Favorites._ID + "=" + favoriteId;
|
|
|
|
|
db.update(TABLE_FAVORITES, values, updateWhere, null);
|
2009-12-15 13:33:11 +00:00
|
|
|
|
|
|
|
|
if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
|
2012-04-18 14:23:14 -07:00
|
|
|
// TODO: check return value
|
|
|
|
|
appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
|
2009-12-15 13:33:11 +00:00
|
|
|
new ComponentName("com.android.alarmclock",
|
|
|
|
|
"com.android.alarmclock.AnalogAppWidgetProvider"));
|
|
|
|
|
} else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
|
2012-04-18 14:23:14 -07:00
|
|
|
// TODO: check return value
|
|
|
|
|
appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
|
2009-12-15 13:33:11 +00:00
|
|
|
new ComponentName("com.android.camera",
|
|
|
|
|
"com.android.camera.PhotoAppWidgetProvider"));
|
|
|
|
|
} else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
|
2012-04-18 14:23:14 -07:00
|
|
|
// TODO: check return value
|
|
|
|
|
appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
|
2010-01-14 13:26:43 +00:00
|
|
|
getSearchWidgetProvider());
|
2009-12-15 13:33:11 +00:00
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
} catch (RuntimeException ex) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.e(TAG, "Problem allocating appWidgetId", ex);
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
db.setTransactionSuccessful();
|
|
|
|
|
} catch (SQLException ex) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
|
2009-03-03 19:32:27 -08:00
|
|
|
} finally {
|
|
|
|
|
db.endTransaction();
|
|
|
|
|
if (c != null) {
|
|
|
|
|
c.close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 14:23:14 -07:00
|
|
|
private static final void beginDocument(XmlPullParser parser, String firstElementName)
|
|
|
|
|
throws XmlPullParserException, IOException {
|
|
|
|
|
int type;
|
|
|
|
|
while ((type = parser.next()) != parser.START_TAG
|
|
|
|
|
&& type != parser.END_DOCUMENT) {
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type != parser.START_TAG) {
|
|
|
|
|
throw new XmlPullParserException("No start tag found");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!parser.getName().equals(firstElementName)) {
|
|
|
|
|
throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
|
|
|
|
|
", expected " + firstElementName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
/**
|
|
|
|
|
* Loads the default set of favorite packages from an xml file.
|
|
|
|
|
*
|
|
|
|
|
* @param db The database to write the values into
|
2011-07-13 17:25:49 -07:00
|
|
|
* @param filterContainerId The specific container id of items to load
|
2009-03-03 19:32:27 -08:00
|
|
|
*/
|
2011-11-16 18:43:26 -08:00
|
|
|
private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
|
2009-03-03 19:32:27 -08:00
|
|
|
Intent intent = new Intent(Intent.ACTION_MAIN, null);
|
|
|
|
|
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
|
|
|
|
|
PackageManager packageManager = mContext.getPackageManager();
|
2012-02-29 13:33:22 -08:00
|
|
|
int allAppsButtonRank =
|
|
|
|
|
mContext.getResources().getInteger(R.integer.hotseat_all_apps_index);
|
2009-03-03 19:32:27 -08:00
|
|
|
int i = 0;
|
|
|
|
|
try {
|
2011-11-16 18:43:26 -08:00
|
|
|
XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
|
2009-03-18 17:39:48 -07:00
|
|
|
AttributeSet attrs = Xml.asAttributeSet(parser);
|
2012-04-18 14:23:14 -07:00
|
|
|
beginDocument(parser, TAG_FAVORITES);
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2009-03-18 17:39:48 -07:00
|
|
|
final int depth = parser.getDepth();
|
2009-03-03 19:32:27 -08:00
|
|
|
|
2009-03-18 17:39:48 -07:00
|
|
|
int type;
|
|
|
|
|
while (((type = parser.next()) != XmlPullParser.END_TAG ||
|
|
|
|
|
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
|
|
|
|
|
|
|
|
|
|
if (type != XmlPullParser.START_TAG) {
|
|
|
|
|
continue;
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
|
2009-03-18 17:39:48 -07:00
|
|
|
boolean added = false;
|
|
|
|
|
final String name = parser.getName();
|
|
|
|
|
|
|
|
|
|
TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
|
|
|
|
|
|
2011-07-13 17:25:49 -07:00
|
|
|
long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
|
|
|
|
if (a.hasValue(R.styleable.Favorite_container)) {
|
|
|
|
|
container = Long.valueOf(a.getString(R.styleable.Favorite_container));
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-16 18:43:26 -08:00
|
|
|
String screen = a.getString(R.styleable.Favorite_screen);
|
|
|
|
|
String x = a.getString(R.styleable.Favorite_x);
|
|
|
|
|
String y = a.getString(R.styleable.Favorite_y);
|
2011-07-27 22:23:47 -07:00
|
|
|
|
2011-11-16 18:43:26 -08:00
|
|
|
// If we are adding to the hotseat, the screen is used as the position in the
|
|
|
|
|
// hotseat. This screen can't be at position 0 because AllApps is in the
|
|
|
|
|
// zeroth position.
|
2012-02-29 13:33:22 -08:00
|
|
|
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
|
|
|
|
&& Integer.valueOf(screen) == allAppsButtonRank) {
|
2011-11-16 18:43:26 -08:00
|
|
|
throw new RuntimeException("Invalid screen position for hotseat item");
|
|
|
|
|
}
|
2011-07-27 22:23:47 -07:00
|
|
|
|
2011-11-16 18:43:26 -08:00
|
|
|
values.clear();
|
|
|
|
|
values.put(LauncherSettings.Favorites.CONTAINER, container);
|
|
|
|
|
values.put(LauncherSettings.Favorites.SCREEN, screen);
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLX, x);
|
|
|
|
|
values.put(LauncherSettings.Favorites.CELLY, y);
|
|
|
|
|
|
|
|
|
|
if (TAG_FAVORITE.equals(name)) {
|
|
|
|
|
long id = addAppShortcut(db, values, a, packageManager, intent);
|
|
|
|
|
added = id >= 0;
|
|
|
|
|
} else if (TAG_SEARCH.equals(name)) {
|
|
|
|
|
added = addSearchWidget(db, values);
|
|
|
|
|
} else if (TAG_CLOCK.equals(name)) {
|
|
|
|
|
added = addClockWidget(db, values);
|
|
|
|
|
} else if (TAG_APPWIDGET.equals(name)) {
|
2012-05-01 10:19:14 -07:00
|
|
|
added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
|
2011-11-16 18:43:26 -08:00
|
|
|
} else if (TAG_SHORTCUT.equals(name)) {
|
|
|
|
|
long id = addUriShortcut(db, values, a);
|
|
|
|
|
added = id >= 0;
|
|
|
|
|
} else if (TAG_FOLDER.equals(name)) {
|
|
|
|
|
String title;
|
|
|
|
|
int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
|
|
|
|
|
if (titleResId != -1) {
|
|
|
|
|
title = mContext.getResources().getString(titleResId);
|
|
|
|
|
} else {
|
|
|
|
|
title = mContext.getResources().getString(R.string.folder_name);
|
|
|
|
|
}
|
|
|
|
|
values.put(LauncherSettings.Favorites.TITLE, title);
|
|
|
|
|
long folderId = addFolder(db, values);
|
|
|
|
|
added = folderId >= 0;
|
2011-07-27 22:23:47 -07:00
|
|
|
|
2011-11-16 18:43:26 -08:00
|
|
|
ArrayList<Long> folderItems = new ArrayList<Long>();
|
|
|
|
|
|
|
|
|
|
int folderDepth = parser.getDepth();
|
|
|
|
|
while ((type = parser.next()) != XmlPullParser.END_TAG ||
|
|
|
|
|
parser.getDepth() > folderDepth) {
|
|
|
|
|
if (type != XmlPullParser.START_TAG) {
|
|
|
|
|
continue;
|
2011-07-27 22:23:47 -07:00
|
|
|
}
|
2011-11-16 18:43:26 -08:00
|
|
|
final String folder_item_name = parser.getName();
|
|
|
|
|
|
|
|
|
|
TypedArray ar = mContext.obtainStyledAttributes(attrs,
|
|
|
|
|
R.styleable.Favorite);
|
|
|
|
|
values.clear();
|
|
|
|
|
values.put(LauncherSettings.Favorites.CONTAINER, folderId);
|
|
|
|
|
|
|
|
|
|
if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
|
|
|
|
|
long id =
|
|
|
|
|
addAppShortcut(db, values, ar, packageManager, intent);
|
|
|
|
|
if (id >= 0) {
|
|
|
|
|
folderItems.add(id);
|
|
|
|
|
}
|
|
|
|
|
} else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
|
|
|
|
|
long id = addUriShortcut(db, values, ar);
|
|
|
|
|
if (id >= 0) {
|
|
|
|
|
folderItems.add(id);
|
2011-07-27 22:23:47 -07:00
|
|
|
}
|
2011-11-16 18:43:26 -08:00
|
|
|
} else {
|
|
|
|
|
throw new RuntimeException("Folders can " +
|
|
|
|
|
"contain only shortcuts");
|
|
|
|
|
}
|
|
|
|
|
ar.recycle();
|
|
|
|
|
}
|
|
|
|
|
// We can only have folders with >= 2 items, so we need to remove the
|
|
|
|
|
// folder and clean up if less than 2 items were included, or some
|
|
|
|
|
// failed to add, and less than 2 were actually added
|
|
|
|
|
if (folderItems.size() < 2 && folderId >= 0) {
|
|
|
|
|
// We just delete the folder and any items that made it
|
|
|
|
|
deleteId(db, folderId);
|
|
|
|
|
if (folderItems.size() > 0) {
|
|
|
|
|
deleteId(db, folderItems.get(0));
|
2011-07-27 22:23:47 -07:00
|
|
|
}
|
2011-11-16 18:43:26 -08:00
|
|
|
added = false;
|
2011-07-13 17:25:49 -07:00
|
|
|
}
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
2009-03-18 17:39:48 -07:00
|
|
|
if (added) i++;
|
|
|
|
|
a.recycle();
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
|
|
|
|
} catch (XmlPullParserException e) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Got exception parsing favorites.", e);
|
2009-03-03 19:32:27 -08:00
|
|
|
} catch (IOException e) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Got exception parsing favorites.", e);
|
2011-07-13 17:25:49 -07:00
|
|
|
} catch (RuntimeException e) {
|
|
|
|
|
Log.w(TAG, "Got exception parsing favorites.", e);
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
2009-03-18 17:39:48 -07:00
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-27 22:23:47 -07:00
|
|
|
private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
|
2009-03-18 17:39:48 -07:00
|
|
|
PackageManager packageManager, Intent intent) {
|
2011-07-27 22:23:47 -07:00
|
|
|
long id = -1;
|
2009-03-18 17:39:48 -07:00
|
|
|
ActivityInfo info;
|
|
|
|
|
String packageName = a.getString(R.styleable.Favorite_packageName);
|
|
|
|
|
String className = a.getString(R.styleable.Favorite_className);
|
|
|
|
|
try {
|
2010-03-23 10:58:18 -07:00
|
|
|
ComponentName cn;
|
|
|
|
|
try {
|
|
|
|
|
cn = new ComponentName(packageName, className);
|
|
|
|
|
info = packageManager.getActivityInfo(cn, 0);
|
|
|
|
|
} catch (PackageManager.NameNotFoundException nnfe) {
|
|
|
|
|
String[] packages = packageManager.currentToCanonicalPackageNames(
|
|
|
|
|
new String[] { packageName });
|
|
|
|
|
cn = new ComponentName(packages[0], className);
|
|
|
|
|
info = packageManager.getActivityInfo(cn, 0);
|
|
|
|
|
}
|
2011-07-27 22:23:47 -07:00
|
|
|
id = generateNewId();
|
2009-03-18 17:39:48 -07:00
|
|
|
intent.setComponent(cn);
|
2010-03-23 10:58:18 -07:00
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
|
|
|
|
|
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
|
2009-06-23 17:34:54 -07:00
|
|
|
values.put(Favorites.INTENT, intent.toUri(0));
|
2009-03-18 17:39:48 -07:00
|
|
|
values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
|
|
|
|
|
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
|
|
|
|
|
values.put(Favorites.SPANX, 1);
|
|
|
|
|
values.put(Favorites.SPANY, 1);
|
2011-04-28 14:59:33 -07:00
|
|
|
values.put(Favorites._ID, generateNewId());
|
2011-07-27 22:23:47 -07:00
|
|
|
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-03-18 17:39:48 -07:00
|
|
|
} catch (PackageManager.NameNotFoundException e) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Unable to add favorite: " + packageName +
|
2009-03-18 17:39:48 -07:00
|
|
|
"/" + className, e);
|
|
|
|
|
}
|
2011-07-27 22:23:47 -07:00
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long addFolder(SQLiteDatabase db, ContentValues values) {
|
|
|
|
|
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
|
|
|
|
|
values.put(Favorites.SPANX, 1);
|
|
|
|
|
values.put(Favorites.SPANY, 1);
|
|
|
|
|
long id = generateNewId();
|
|
|
|
|
values.put(Favorites._ID, id);
|
|
|
|
|
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
return id;
|
|
|
|
|
}
|
2009-03-18 17:39:48 -07:00
|
|
|
}
|
|
|
|
|
|
2010-01-14 13:26:43 +00:00
|
|
|
private ComponentName getSearchWidgetProvider() {
|
|
|
|
|
SearchManager searchManager =
|
|
|
|
|
(SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
|
|
|
|
|
ComponentName searchComponent = searchManager.getGlobalSearchActivity();
|
|
|
|
|
if (searchComponent == null) return null;
|
|
|
|
|
return getProviderInPackage(searchComponent.getPackageName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets an appwidget provider from the given package. If the package contains more than
|
|
|
|
|
* one appwidget provider, an arbitrary one is returned.
|
|
|
|
|
*/
|
|
|
|
|
private ComponentName getProviderInPackage(String packageName) {
|
|
|
|
|
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
|
|
|
|
|
List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
|
|
|
|
|
if (providers == null) return null;
|
|
|
|
|
final int providerCount = providers.size();
|
|
|
|
|
for (int i = 0; i < providerCount; i++) {
|
|
|
|
|
ComponentName provider = providers.get(i).provider;
|
|
|
|
|
if (provider != null && provider.getPackageName().equals(packageName)) {
|
|
|
|
|
return provider;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-18 17:39:48 -07:00
|
|
|
private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
|
2010-01-14 13:26:43 +00:00
|
|
|
ComponentName cn = getSearchWidgetProvider();
|
2012-05-01 10:19:14 -07:00
|
|
|
return addAppWidget(db, values, cn, 4, 1, null);
|
2009-03-18 17:39:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
|
2009-12-15 13:33:11 +00:00
|
|
|
ComponentName cn = new ComponentName("com.android.alarmclock",
|
|
|
|
|
"com.android.alarmclock.AnalogAppWidgetProvider");
|
2012-05-01 10:19:14 -07:00
|
|
|
return addAppWidget(db, values, cn, 2, 2, null);
|
2009-03-03 19:32:27 -08:00
|
|
|
}
|
2011-07-27 22:23:47 -07:00
|
|
|
|
2012-05-01 10:19:14 -07:00
|
|
|
private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
|
|
|
|
|
SQLiteDatabase db, ContentValues values, TypedArray a,
|
|
|
|
|
PackageManager packageManager) throws XmlPullParserException, IOException {
|
2010-03-23 10:58:18 -07:00
|
|
|
|
2009-10-30 16:36:56 -07:00
|
|
|
String packageName = a.getString(R.styleable.Favorite_packageName);
|
|
|
|
|
String className = a.getString(R.styleable.Favorite_className);
|
|
|
|
|
|
|
|
|
|
if (packageName == null || className == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2010-03-23 10:58:18 -07:00
|
|
|
|
|
|
|
|
boolean hasPackage = true;
|
2009-10-30 16:36:56 -07:00
|
|
|
ComponentName cn = new ComponentName(packageName, className);
|
2010-03-23 10:58:18 -07:00
|
|
|
try {
|
|
|
|
|
packageManager.getReceiverInfo(cn, 0);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
String[] packages = packageManager.currentToCanonicalPackageNames(
|
|
|
|
|
new String[] { packageName });
|
|
|
|
|
cn = new ComponentName(packages[0], className);
|
|
|
|
|
try {
|
|
|
|
|
packageManager.getReceiverInfo(cn, 0);
|
|
|
|
|
} catch (Exception e1) {
|
|
|
|
|
hasPackage = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasPackage) {
|
|
|
|
|
int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
|
|
|
|
|
int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
|
2012-05-01 10:19:14 -07:00
|
|
|
|
|
|
|
|
// Read the extras
|
|
|
|
|
Bundle extras = new Bundle();
|
|
|
|
|
int widgetDepth = parser.getDepth();
|
|
|
|
|
while ((type = parser.next()) != XmlPullParser.END_TAG ||
|
|
|
|
|
parser.getDepth() > widgetDepth) {
|
|
|
|
|
if (type != XmlPullParser.START_TAG) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
|
|
|
|
|
if (TAG_EXTRA.equals(parser.getName())) {
|
|
|
|
|
String key = ar.getString(R.styleable.Extra_key);
|
|
|
|
|
String value = ar.getString(R.styleable.Extra_value);
|
|
|
|
|
if (key != null && value != null) {
|
|
|
|
|
extras.putString(key, value);
|
|
|
|
|
} else {
|
|
|
|
|
throw new RuntimeException("Widget extras must have a key and value");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
throw new RuntimeException("Widgets can contain only extras");
|
|
|
|
|
}
|
|
|
|
|
ar.recycle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addAppWidget(db, values, cn, spanX, spanY, extras);
|
2010-03-23 10:58:18 -07:00
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2010-03-23 10:58:18 -07:00
|
|
|
return false;
|
2009-12-09 15:38:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
|
2012-05-01 10:19:14 -07:00
|
|
|
int spanX, int spanY, Bundle extras) {
|
2009-10-30 16:36:56 -07:00
|
|
|
boolean allocatedAppWidgets = false;
|
|
|
|
|
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-10-30 16:36:56 -07:00
|
|
|
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
|
2009-12-09 15:38:25 +00:00
|
|
|
values.put(Favorites.SPANX, spanX);
|
|
|
|
|
values.put(Favorites.SPANY, spanY);
|
2009-10-30 16:36:56 -07:00
|
|
|
values.put(Favorites.APPWIDGET_ID, appWidgetId);
|
2011-04-28 14:59:33 -07:00
|
|
|
values.put(Favorites._ID, generateNewId());
|
|
|
|
|
dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
|
2009-10-30 16:36:56 -07:00
|
|
|
|
|
|
|
|
allocatedAppWidgets = true;
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2012-04-18 14:23:14 -07:00
|
|
|
// TODO: need to check return value
|
|
|
|
|
appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
|
2012-05-01 10:19:14 -07:00
|
|
|
|
|
|
|
|
// Send a broadcast to configure the widget
|
|
|
|
|
if (extras != null && !extras.isEmpty()) {
|
|
|
|
|
Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
|
|
|
|
|
intent.setComponent(cn);
|
|
|
|
|
intent.putExtras(extras);
|
|
|
|
|
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
|
|
|
|
|
mContext.sendBroadcast(intent);
|
|
|
|
|
}
|
2009-10-30 16:36:56 -07:00
|
|
|
} catch (RuntimeException ex) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.e(TAG, "Problem allocating appWidgetId", ex);
|
2009-10-30 16:36:56 -07:00
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-10-30 16:36:56 -07:00
|
|
|
return allocatedAppWidgets;
|
|
|
|
|
}
|
2011-07-27 22:23:47 -07:00
|
|
|
|
|
|
|
|
private long addUriShortcut(SQLiteDatabase db, ContentValues values,
|
2009-10-30 16:36:56 -07:00
|
|
|
TypedArray a) {
|
|
|
|
|
Resources r = mContext.getResources();
|
|
|
|
|
|
|
|
|
|
final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
|
|
|
|
|
final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
|
|
|
|
|
|
2009-12-02 20:10:07 -08:00
|
|
|
Intent intent;
|
2009-10-30 16:36:56 -07:00
|
|
|
String uri = null;
|
|
|
|
|
try {
|
|
|
|
|
uri = a.getString(R.styleable.Favorite_uri);
|
|
|
|
|
intent = Intent.parseUri(uri, 0);
|
|
|
|
|
} catch (URISyntaxException e) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Shortcut has malformed uri: " + uri);
|
2011-07-27 22:23:47 -07:00
|
|
|
return -1; // Oh well
|
2009-10-30 16:36:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iconResId == 0 || titleResId == 0) {
|
2009-11-11 08:16:49 -08:00
|
|
|
Log.w(TAG, "Shortcut is missing title or icon resource ID");
|
2011-07-27 22:23:47 -07:00
|
|
|
return -1;
|
2009-10-30 16:36:56 -07:00
|
|
|
}
|
|
|
|
|
|
2011-07-27 22:23:47 -07:00
|
|
|
long id = generateNewId();
|
2009-10-30 16:36:56 -07:00
|
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
|
values.put(Favorites.INTENT, intent.toUri(0));
|
|
|
|
|
values.put(Favorites.TITLE, r.getString(titleResId));
|
|
|
|
|
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
|
|
|
|
|
values.put(Favorites.SPANX, 1);
|
|
|
|
|
values.put(Favorites.SPANY, 1);
|
|
|
|
|
values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
|
|
|
|
|
values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
|
|
|
|
|
values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
|
2011-07-27 22:23:47 -07:00
|
|
|
values.put(Favorites._ID, id);
|
2009-10-30 16:36:56 -07:00
|
|
|
|
2011-07-27 22:23:47 -07:00
|
|
|
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return id;
|
2009-10-30 16:36:56 -07:00
|
|
|
}
|
|
|
|
|
}
|
2012-04-23 21:35:11 -07:00
|
|
|
|
2009-03-03 19:32:27 -08:00
|
|
|
/**
|
|
|
|
|
* Build a query string that will match any row where the column matches
|
|
|
|
|
* anything in the values list.
|
|
|
|
|
*/
|
|
|
|
|
static String buildOrWhereString(String column, int[] values) {
|
|
|
|
|
StringBuilder selectWhere = new StringBuilder();
|
|
|
|
|
for (int i = values.length - 1; i >= 0; i--) {
|
|
|
|
|
selectWhere.append(column).append("=").append(values[i]);
|
|
|
|
|
if (i > 0) {
|
|
|
|
|
selectWhere.append(" OR ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return selectWhere.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|