Refactoring layout parsing code

Change-Id: Iee5b2280cb1e841bcfe91fdcf523de6fa7f7f3b8
This commit is contained in:
Sunny Goyal 2014-10-01 15:33:41 -07:00
parent 91d79e820a
commit 3a5a9d18da
3 changed files with 457 additions and 751 deletions

View File

@ -36,7 +36,6 @@ import android.util.Pair;
import android.util.Patterns;
import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherProvider.WorkspaceLoader;
import com.android.launcher3.LauncherSettings.Favorites;
import org.xmlpull.v1.XmlPullParser;
@ -47,13 +46,11 @@ import java.util.ArrayList;
import java.util.HashMap;
/**
* This class contains contains duplication of functionality as found in
* LauncherProvider#DatabaseHelper. It has been isolated and differentiated in order
* to cleanly and separately represent AutoInstall default layout format and policy.
* Layout parsing code for auto installs layout
*/
public class AutoInstallsLayout implements WorkspaceLoader {
public class AutoInstallsLayout {
private static final String TAG = "AutoInstalls";
private static final boolean LOGD = true;
private static final boolean LOGD = false;
/** Marker action used to discover a package which defines launcher customization */
static final String ACTION_LAUNCHER_CUSTOMIZATION =
@ -76,7 +73,8 @@ public class AutoInstallsLayout implements WorkspaceLoader {
Log.e(TAG, "Layout definition not found in package: " + pkg);
return null;
}
return new AutoInstallsLayout(context, appWidgetHost, callback, pkg, res, layoutId);
return new AutoInstallsLayout(context, appWidgetHost, callback, res, layoutId,
TAG_WORKSPACE);
}
// Object Tags
@ -116,45 +114,55 @@ public class AutoInstallsLayout implements WorkspaceLoader {
private final AppWidgetHost mAppWidgetHost;
private final LayoutParserCallback mCallback;
private final PackageManager mPackageManager;
protected final PackageManager mPackageManager;
protected final Resources mSourceRes;
protected final int mLayoutId;
private final int mHotseatAllAppsRank;
private final long[] mTemp = new long[2];
private final ContentValues mValues;
private final String mRootTag;
private final Resources mRes;
private final int mLayoutId;
private SQLiteDatabase mDb;
protected SQLiteDatabase mDb;
public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, String packageName, Resources res, int layoutId) {
LayoutParserCallback callback, Resources res,
int layoutId, String rootTag) {
mContext = context;
mAppWidgetHost = appWidgetHost;
mCallback = callback;
mPackageManager = context.getPackageManager();
mValues = new ContentValues();
mRootTag = rootTag;
mRes = res;
mSourceRes = res;
mLayoutId = layoutId;
mHotseatAllAppsRank = LauncherAppState.getInstance()
.getDynamicGrid().getDeviceProfile().hotseatAllAppsRank;
}
@Override
/**
* Loads the layout in the db and returns the number of entries added on the desktop.
*/
public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
mDb = db;
try {
return parseLayout(mRes, mLayoutId, screenIds);
return parseLayout(mLayoutId, screenIds);
} catch (Exception e) {
Log.w(TAG, "Got exception parsing layout.", e);
return -1;
}
}
private int parseLayout(Resources res, int layoutId, ArrayList<Long> screenIds)
/**
* Parses the layout and returns the number of elements added on the homescreen.
*/
protected int parseLayout(int layoutId, ArrayList<Long> screenIds)
throws XmlPullParserException, IOException {
final int hotseatAllAppsRank = LauncherAppState.getInstance()
.getDynamicGrid().getDeviceProfile().hotseatAllAppsRank;
XmlResourceParser parser = res.getXml(layoutId);
beginDocument(parser, TAG_WORKSPACE);
XmlResourceParser parser = mSourceRes.getXml(layoutId);
beginDocument(parser, mRootTag);
final int depth = parser.getDepth();
int type;
HashMap<String, TagParser> tagParserMap = getLayoutElementsMap();
@ -165,47 +173,62 @@ public class AutoInstallsLayout implements WorkspaceLoader {
if (type != XmlPullParser.START_TAG) {
continue;
}
mValues.clear();
final int container;
final long screenId;
if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
container = Favorites.CONTAINER_HOTSEAT;
// Hack: hotseat items are stored using screen ids
long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
screenId = (rank < hotseatAllAppsRank) ? rank : (rank + 1);
} else {
container = Favorites.CONTAINER_DESKTOP;
screenId = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
mValues.put(Favorites.CELLX, getAttributeValue(parser, ATTR_X));
mValues.put(Favorites.CELLY, getAttributeValue(parser, ATTR_Y));
}
mValues.put(Favorites.CONTAINER, container);
mValues.put(Favorites.SCREEN, screenId);
TagParser tagParser = tagParserMap.get(parser.getName());
if (tagParser == null) {
if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
continue;
}
long newElementId = tagParser.parseAndAdd(parser, res);
if (newElementId >= 0) {
// Keep track of the set of screens which need to be added to the db.
if (!screenIds.contains(screenId) &&
container == Favorites.CONTAINER_DESKTOP) {
screenIds.add(screenId);
}
count++;
}
count += parseAndAddNode(parser, tagParserMap, screenIds);
}
return count;
}
/**
* Parses container and screenId attribute from the current tag, and puts it in the out.
* @param out array of size 2.
*/
protected void parseContainerAndScreen(XmlResourceParser parser, long[] out) {
if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
out[0] = Favorites.CONTAINER_HOTSEAT;
// Hack: hotseat items are stored using screen ids
long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
out[1] = (rank < mHotseatAllAppsRank) ? rank : (rank + 1);
} else {
out[0] = Favorites.CONTAINER_DESKTOP;
out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
}
}
/**
* Parses the current node and returns the number of elements added.
*/
protected int parseAndAddNode(
XmlResourceParser parser,
HashMap<String, TagParser> tagParserMap,
ArrayList<Long> screenIds)
throws XmlPullParserException, IOException {
mValues.clear();
parseContainerAndScreen(parser, mTemp);
final long container = mTemp[0];
final long screenId = mTemp[1];
mValues.put(Favorites.CONTAINER, container);
mValues.put(Favorites.SCREEN, screenId);
mValues.put(Favorites.CELLX, getAttributeValue(parser, ATTR_X));
mValues.put(Favorites.CELLY, getAttributeValue(parser, ATTR_Y));
TagParser tagParser = tagParserMap.get(parser.getName());
if (tagParser == null) {
if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
return 0;
}
long newElementId = tagParser.parseAndAdd(parser);
if (newElementId >= 0) {
// Keep track of the set of screens which need to be added to the db.
if (!screenIds.contains(screenId) &&
container == Favorites.CONTAINER_DESKTOP) {
screenIds.add(screenId);
}
return 1;
}
return 0;
}
protected long addShortcut(String title, Intent intent, int type) {
long id = mCallback.generateNewItemId();
mValues.put(Favorites.INTENT, intent.toUri(0));
@ -225,7 +248,7 @@ public class AutoInstallsLayout implements WorkspaceLoader {
HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
parsers.put(TAG_APP_ICON, new AppShortcutParser());
parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
return parsers;
}
@ -235,23 +258,26 @@ public class AutoInstallsLayout implements WorkspaceLoader {
parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
parsers.put(TAG_FOLDER, new FolderParser());
parsers.put(TAG_APPWIDGET, new AppWidgetParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
return parsers;
}
private interface TagParser {
protected interface TagParser {
/**
* Parses the tag and adds to the db
* @return the id of the row added or -1;
*/
long parseAndAdd(XmlResourceParser parser, Resources res)
long parseAndAdd(XmlResourceParser parser)
throws XmlPullParserException, IOException;
}
private class AppShortcutParser implements TagParser {
/**
* App shortcuts: required attributes packageName and className
*/
protected class AppShortcutParser implements TagParser {
@Override
public long parseAndAdd(XmlResourceParser parser, Resources res) {
public long parseAndAdd(XmlResourceParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
@ -277,20 +303,30 @@ public class AutoInstallsLayout implements WorkspaceLoader {
return addShortcut(info.loadLabel(mPackageManager).toString(),
intent, Favorites.ITEM_TYPE_APPLICATION);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
if (LOGD) Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
}
return -1;
} else {
if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component or uri");
return -1;
return invalidPackageOrClass(parser);
}
}
/**
* Helper method to allow extending the parser capabilities
*/
protected long invalidPackageOrClass(XmlResourceParser parser) {
if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
return -1;
}
}
private class AutoInstallParser implements TagParser {
/**
* AutoInstall: required attributes packageName and className
*/
protected class AutoInstallParser implements TagParser {
@Override
public long parseAndAdd(XmlResourceParser parser, Resources res) {
public long parseAndAdd(XmlResourceParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
@ -309,11 +345,19 @@ public class AutoInstallsLayout implements WorkspaceLoader {
}
}
private class ShortcutParser implements TagParser {
/**
* Parses a web shortcut. Required attributes url, icon, title
*/
protected class ShortcutParser implements TagParser {
private final Resources mIconRes;
public ShortcutParser(Resources iconRes) {
mIconRes = iconRes;
}
@Override
public long parseAndAdd(XmlResourceParser parser, Resources res) {
final String url = getAttributeValue(parser, ATTR_URL);
public long parseAndAdd(XmlResourceParser parser) {
final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0);
@ -322,29 +366,46 @@ public class AutoInstallsLayout implements WorkspaceLoader {
return -1;
}
if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
final Intent intent = parseIntent(parser);
if (intent == null) {
return -1;
}
Drawable icon = res.getDrawable(iconId);
Drawable icon = mIconRes.getDrawable(iconId);
if (icon == null) {
if (LOGD) Log.d(TAG, "Ignoring shortcut, can't load icon");
return -1;
}
ItemInfo.writeBitmap(mValues, Utilities.createIconBitmap(icon, mContext));
final Intent intent = new Intent(Intent.ACTION_VIEW, null)
.setData(Uri.parse(url))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
mValues.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addShortcut(res.getString(titleResId), intent, Favorites.ITEM_TYPE_SHORTCUT);
return addShortcut(mSourceRes.getString(titleResId),
intent, Favorites.ITEM_TYPE_SHORTCUT);
}
protected Intent parseIntent(XmlResourceParser parser) {
final String url = getAttributeValue(parser, ATTR_URL);
if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
return null;
}
return new Intent(Intent.ACTION_VIEW, null).setData(Uri.parse(url));
}
}
private class AppWidgetParser implements TagParser {
/**
* AppWidget parser: Required attributes packageName, className, spanX and spanY.
* Options child nodes: <extra key=... value=... />
*/
protected class AppWidgetParser implements TagParser {
@Override
public long parseAndAdd(XmlResourceParser parser, Resources res)
public long parseAndAdd(XmlResourceParser parser)
throws XmlPullParserException, IOException {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
@ -429,16 +490,24 @@ public class AutoInstallsLayout implements WorkspaceLoader {
}
}
private class FolderParser implements TagParser {
private final HashMap<String, TagParser> mFolderElements = getFolderElementsMap();
protected class FolderParser implements TagParser {
private final HashMap<String, TagParser> mFolderElements;
public FolderParser() {
this(getFolderElementsMap());
}
public FolderParser(HashMap<String, TagParser> elements) {
mFolderElements = elements;
}
@Override
public long parseAndAdd(XmlResourceParser parser, Resources res)
public long parseAndAdd(XmlResourceParser parser)
throws XmlPullParserException, IOException {
final String title;
final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
if (titleResId != 0) {
title = res.getString(titleResId);
title = mSourceRes.getString(titleResId);
} else {
title = mContext.getResources().getString(R.string.folder_name);
}
@ -469,7 +538,7 @@ public class AutoInstallsLayout implements WorkspaceLoader {
TagParser tagParser = mFolderElements.get(parser.getName());
if (tagParser != null) {
final long id = tagParser.parseAndAdd(parser, res);
final long id = tagParser.parseAndAdd(parser);
if (id >= 0) {
folderItems.add(id);
}
@ -508,7 +577,7 @@ public class AutoInstallsLayout implements WorkspaceLoader {
}
}
private static final void beginDocument(XmlPullParser parser, String firstElementName)
protected static final void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@ -528,7 +597,7 @@ public class AutoInstallsLayout implements WorkspaceLoader {
* Return attribute value, attempting launcher-specific namespace first
* before falling back to anonymous attribute.
*/
private static String getAttributeValue(XmlResourceParser parser, String attribute) {
protected static String getAttributeValue(XmlResourceParser parser, String attribute) {
String value = parser.getAttributeValue(
"http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
if (value == null) {
@ -541,7 +610,7 @@ public class AutoInstallsLayout implements WorkspaceLoader {
* Return attribute resource value, attempting launcher-specific namespace
* first before falling back to anonymous attribute.
*/
private static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
protected static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
int defaultValue) {
int value = parser.getAttributeResourceValue(
"http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,

View File

@ -0,0 +1,290 @@
package com.android.launcher3;
import android.appwidget.AppWidgetHost;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Implements the layout parser with rules for internal layouts and partner layouts.
*/
public class DefaultLayoutParser extends AutoInstallsLayout {
private static final String TAG = "DefaultLayoutParser";
private static final String TAG_RESOLVE = "resolve";
private static final String TAG_FAVORITES = "favorites";
private static final String TAG_FAVORITE = "favorite";
private static final String TAG_APPWIDGET = "appwidget";
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_FOLDER = "folder";
private static final String TAG_PARTNER_FOLDER = "partner-folder";
private static final String TAG_INCLUDE = "include";
private static final String ATTR_URI = "uri";
private static final String ATTR_WORKSPACE = "workspace";
private static final String ATTR_CONTAINER = "container";
private static final String ATTR_SCREEN = "screen";
private static final String ATTR_FOLDER_ITEMS = "folderItems";
public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, Resources sourceRes, int layoutId) {
super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
Log.e(TAG, "Default layout parser initialized");
}
@Override
protected HashMap<String, TagParser> getFolderElementsMap() {
return getFolderElementsMap(mSourceRes);
}
private HashMap<String, TagParser> getFolderElementsMap(Resources res) {
HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
return parsers;
}
@Override
protected HashMap<String, TagParser> getLayoutElementsMap() {
HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_APPWIDGET, new AppWidgetParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
parsers.put(TAG_RESOLVE, new ResolveParser());
parsers.put(TAG_FOLDER, new MyFolderParser());
parsers.put(TAG_PARTNER_FOLDER, new PartnerFolderParser());
return parsers;
}
@Override
protected void parseContainerAndScreen(XmlResourceParser parser, long[] out) {
out[0] = LauncherSettings.Favorites.CONTAINER_DESKTOP;
String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
if (strContainer != null) {
out[0] = Long.valueOf(strContainer);
}
out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
}
@Override
protected int parseAndAddNode(
XmlResourceParser parser,
HashMap<String, TagParser> tagParserMap,
ArrayList<Long> screenIds)
throws XmlPullParserException, IOException {
if (TAG_INCLUDE.equals(parser.getName())) {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
if (resId != 0) {
// recursively load some more favorites, why not?
return parseLayout(resId, screenIds);
} else {
return 0;
}
} else {
return super.parseAndAddNode(parser, tagParserMap, screenIds);
}
}
/**
* AppShortcutParser which also supports adding URI based intents
*/
private class AppShortcutWithUriParser extends AppShortcutParser {
@Override
protected long invalidPackageOrClass(XmlResourceParser parser) {
final String uri = getAttributeValue(parser, ATTR_URI);
if (TextUtils.isEmpty(uri)) {
Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
return -1;
}
final Intent metaIntent;
try {
metaIntent = Intent.parseUri(uri, 0);
} catch (URISyntaxException e) {
Log.e(TAG, "Unable to add meta-favorite: " + uri, e);
return -1;
}
ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
PackageManager.MATCH_DEFAULT_ONLY);
final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
// Verify that the result is an app and not just the resolver dialog asking which
// app to use.
if (wouldLaunchResolverActivity(resolved, appList)) {
// If only one of the results is a system app then choose that as the default.
final ResolveInfo systemApp = getSingleSystemActivity(appList);
if (systemApp == null) {
// There is no logical choice for this meta-favorite, so rather than making
// a bad choice just add nothing.
Log.w(TAG, "No preference or single system activity found for "
+ metaIntent.toString());
return -1;
}
resolved = systemApp;
}
final ActivityInfo info = resolved.activityInfo;
final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
if (intent == null) {
return -1;
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addShortcut(info.loadLabel(mPackageManager).toString(), intent,
Favorites.ITEM_TYPE_APPLICATION);
}
private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
ResolveInfo systemResolve = null;
final int N = appList.size();
for (int i = 0; i < N; ++i) {
try {
ApplicationInfo info = mPackageManager.getApplicationInfo(
appList.get(i).activityInfo.packageName, 0);
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (systemResolve != null) {
return null;
} else {
systemResolve = appList.get(i);
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to get info about resolve results", e);
return null;
}
}
return systemResolve;
}
private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
List<ResolveInfo> appList) {
// If the list contains the above resolved activity, then it can't be
// ResolverActivity itself.
for (int i = 0; i < appList.size(); ++i) {
ResolveInfo tmp = appList.get(i);
if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
&& tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
return false;
}
}
return true;
}
}
/**
* Shortcut parser which allows any uri and not just web urls.
*/
private class UriShortcutParser extends ShortcutParser {
public UriShortcutParser(Resources iconRes) {
super(iconRes);
}
@Override
protected Intent parseIntent(XmlResourceParser parser) {
String uri = null;
try {
uri = getAttributeValue(parser, ATTR_URI);
return Intent.parseUri(uri, 0);
} catch (URISyntaxException e) {
Log.w(TAG, "Shortcut has malformed uri: " + uri);
return null; // Oh well
}
}
}
/**
* Contains a list of <favorite> nodes, and accepts the first successfully parsed node.
*/
private class ResolveParser implements TagParser {
private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
@Override
public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
IOException {
final int groupDepth = parser.getDepth();
int type;
long addedId = -1;
while ((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > groupDepth) {
if (type != XmlPullParser.START_TAG || addedId > -1) {
continue;
}
final String fallback_item_name = parser.getName();
if (TAG_FAVORITE.equals(fallback_item_name)) {
addedId = mChildParser.parseAndAdd(parser);
} else {
Log.e(TAG, "Fallback groups can contain only favorites, found "
+ fallback_item_name);
}
}
return addedId;
}
}
/**
* A parser which adds a folder whose contents come from partner apk.
*/
private class PartnerFolderParser implements TagParser {
@Override
public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
IOException {
// Folder contents come from an external XML resource
final Partner partner = Partner.get(mPackageManager);
if (partner != null) {
final Resources partnerRes = partner.getResources();
final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
"xml", partner.getPackageName());
if (resId != 0) {
final XmlResourceParser partnerParser = partnerRes.getXml(resId);
beginDocument(partnerParser, TAG_FOLDER);
FolderParser folderParser = new FolderParser(getFolderElementsMap(partnerRes));
return folderParser.parseAndAdd(partnerParser);
}
}
return -1;
}
}
/**
* An extension of FolderParser which allows adding items from a different xml.
*/
private class MyFolderParser extends FolderParser {
@Override
public long parseAndAdd(XmlResourceParser parser) throws XmlPullParserException,
IOException {
final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
if (resId != 0) {
parser = mSourceRes.getXml(resId);
beginDocument(parser, TAG_FOLDER);
}
return super.parseAndAdd(parser);
}
}
}

View File

@ -31,12 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
@ -46,7 +41,6 @@ import android.database.sqlite.SQLiteStatement;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@ -58,11 +52,7 @@ import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.ProviderConfig;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
@ -89,9 +79,6 @@ public class LauncherProvider extends ContentProvider {
static final String EMPTY_DATABASE_CREATED =
"EMPTY_DATABASE_CREATED";
private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
"com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd";
private LauncherProviderChangeListener mListener;
@ -160,12 +147,6 @@ public class LauncherProvider extends ContentProvider {
return db.insert(table, nullColumnHack, values);
}
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);
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
SqlArguments args = new SqlArguments(uri);
@ -325,7 +306,7 @@ public class LauncherProvider extends ContentProvider {
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Log.d(TAG, "loading default workspace");
WorkspaceLoader loader = AutoInstallsLayout.get(getContext(),
AutoInstallsLayout loader = AutoInstallsLayout.get(getContext(),
mOpenHelper.mAppWidgetHost, mOpenHelper);
if (loader == null) {
@ -335,14 +316,15 @@ public class LauncherProvider extends ContentProvider {
int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
"xml", partner.getPackageName());
if (workspaceResId != 0) {
loader = new SimpleWorkspaceLoader(mOpenHelper, partnerRes, workspaceResId);
loader = new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
mOpenHelper, partnerRes, workspaceResId);
}
}
}
if (loader == null) {
loader = new SimpleWorkspaceLoader(mOpenHelper, getContext().getResources(),
getDefaultWorkspaceResourceId());
loader = new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
mOpenHelper, getContext().getResources(), getDefaultWorkspaceResourceId());
}
// Populate favorites table with initial favorites
@ -386,40 +368,7 @@ public class LauncherProvider extends ContentProvider {
}
private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
private static final String TAG_RESOLVE = "resolve";
private static final String TAG_FAVORITES = "favorites";
private static final String TAG_FAVORITE = "favorite";
private static final String TAG_APPWIDGET = "appwidget";
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_FOLDER = "folder";
private static final String TAG_PARTNER_FOLDER = "partner-folder";
private static final String TAG_EXTRA = "extra";
private static final String TAG_INCLUDE = "include";
private static final String TAG_FOLDER_ITEMS = "folderitems";
// Style attrs -- "Favorite"
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_CONTAINER = "container";
private static final String ATTR_SCREEN = "screen";
private static final String ATTR_X = "x";
private static final String ATTR_Y = "y";
private static final String ATTR_SPAN_X = "spanX";
private static final String ATTR_SPAN_Y = "spanY";
private static final String ATTR_ICON = "icon";
private static final String ATTR_TITLE = "title";
private static final String ATTR_URI = "uri";
// Style attrs -- "Include"
private static final String ATTR_WORKSPACE = "workspace";
private static final String ATTR_FOLDER_ITEMS = "folderItems";
// Style attrs -- "Extra"
private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE = "value";
private final Context mContext;
private final PackageManager mPackageManager;
private final AppWidgetHost mAppWidgetHost;
private long mMaxItemId = -1;
private long mMaxScreenId = -1;
@ -429,7 +378,6 @@ public class LauncherProvider extends ContentProvider {
DatabaseHelper(Context context) {
super(context, LauncherFiles.LAUNCHER_DB, null, DATABASE_VERSION);
mContext = context;
mPackageManager = context.getPackageManager();
mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
// In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
@ -785,8 +733,8 @@ public class LauncherProvider extends ContentProvider {
}
// Add default hotseat icons
loadFavorites(db, new SimpleWorkspaceLoader(this, mContext.getResources(),
R.xml.update_workspace));
loadFavorites(db, new DefaultLayoutParser(mContext, mAppWidgetHost, this,
mContext.getResources(), R.xml.update_workspace));
version = 9;
}
@ -1353,31 +1301,7 @@ public class LauncherProvider extends ContentProvider {
return rank;
}
private static final void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.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);
}
}
private static Intent buildMainIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
return intent;
}
private int loadFavorites(SQLiteDatabase db, WorkspaceLoader loader) {
private int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
ArrayList<Long> screenIds = new ArrayList<Long>();
// TODO: Use multiple loaders with fall-back and transaction.
int count = loader.loadLayout(db, screenIds);
@ -1404,392 +1328,6 @@ public class LauncherProvider extends ContentProvider {
return count;
}
/**
* Loads the default set of favorite packages from an xml file.
*
* @param db The database to write the values into
* @param filterContainerId The specific container id of items to load
* @param the set of screenIds which are used by the favorites
*/
private int loadFavoritesRecursive(SQLiteDatabase db, Resources res, int workspaceResourceId,
ArrayList<Long> screenIds) {
ContentValues values = new ContentValues();
if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
int count = 0;
try {
XmlResourceParser parser = res.getXml(workspaceResourceId);
beginDocument(parser, TAG_FAVORITES);
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
boolean added = false;
final String name = parser.getName();
if (TAG_INCLUDE.equals(name)) {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
"", resId));
if (resId != 0 && resId != workspaceResourceId) {
// recursively load some more favorites, why not?
count += loadFavoritesRecursive(db, res, resId, screenIds);
added = false;
} else {
Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
}
if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
continue;
}
// Assuming it's a <favorite> at this point
long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
if (strContainer != null) {
container = Long.valueOf(strContainer);
}
String screen = getAttributeValue(parser, ATTR_SCREEN);
String x = getAttributeValue(parser, ATTR_X);
String y = getAttributeValue(parser, ATTR_Y);
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 (LOGD) {
final String title = getAttributeValue(parser, ATTR_TITLE);
final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String something = title != null ? title : pkg;
Log.v(TAG, String.format(
("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
"", name,
(something == null ? "" : (" \"" + something + "\"")),
container, screen, x, y));
}
if (TAG_FAVORITE.equals(name)) {
long id = addAppShortcut(db, values, parser);
added = id >= 0;
} else if (TAG_APPWIDGET.equals(name)) {
added = addAppWidget(parser, type, db, values);
} else if (TAG_SHORTCUT.equals(name)) {
long id = addUriShortcut(db, values, res, parser);
added = id >= 0;
} else if (TAG_RESOLVE.equals(name)) {
// This looks through the contained favorites (or meta-favorites) and
// attempts to add them as shortcuts in the fallback group's location
// until one is added successfully.
added = false;
final int groupDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > groupDepth) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String fallback_item_name = parser.getName();
if (!added) {
if (TAG_FAVORITE.equals(fallback_item_name)) {
final long id = addAppShortcut(db, values, parser);
added = id >= 0;
} else {
Log.e(TAG, "Fallback groups can contain only favorites, found "
+ fallback_item_name);
}
}
}
} else if (TAG_FOLDER.equals(name)) {
// Folder contents are nested in this XML file
added = loadFolder(db, values, res, parser);
} else if (TAG_PARTNER_FOLDER.equals(name)) {
// Folder contents come from an external XML resource
final Partner partner = Partner.get(mPackageManager);
if (partner != null) {
final Resources partnerRes = partner.getResources();
final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
"xml", partner.getPackageName());
if (resId != 0) {
final XmlResourceParser partnerParser = partnerRes.getXml(resId);
beginDocument(partnerParser, TAG_FOLDER);
added = loadFolder(db, values, partnerRes, partnerParser);
}
}
}
if (added) {
long screenId = Long.parseLong(screen);
// Keep track of the set of screens which need to be added to the db.
if (!screenIds.contains(screenId) &&
container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
screenIds.add(screenId);
}
count++;
}
}
} catch (XmlPullParserException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
} catch (IOException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
} catch (RuntimeException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
}
return count;
}
/**
* Parse folder items starting at {@link XmlPullParser} location. Allow recursive
* includes of items.
*/
private void addToFolder(SQLiteDatabase db, Resources res, XmlResourceParser parser,
ArrayList<Long> folderItems, long folderId) throws IOException, XmlPullParserException {
int type;
int folderDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > folderDepth) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String tag = parser.getName();
final ContentValues childValues = new ContentValues();
childValues.put(LauncherSettings.Favorites.CONTAINER, folderId);
if (LOGD) {
final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String uri = getAttributeValue(parser, ATTR_URI);
Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
tag, uri != null ? uri : pkg));
}
if (TAG_FAVORITE.equals(tag) && folderId >= 0) {
final long id = addAppShortcut(db, childValues, parser);
if (id >= 0) {
folderItems.add(id);
}
} else if (TAG_SHORTCUT.equals(tag) && folderId >= 0) {
final long id = addUriShortcut(db, childValues, res, parser);
if (id >= 0) {
folderItems.add(id);
}
} else if (TAG_INCLUDE.equals(tag) && folderId >= 0) {
final int resId = getAttributeResourceValue(parser, ATTR_FOLDER_ITEMS, 0);
XmlResourceParser itemsParser = res.getXml(resId);
beginDocument(itemsParser, TAG_FOLDER_ITEMS);
addToFolder(db, res, itemsParser, folderItems, folderId);
} else {
throw new RuntimeException("Folders can contain only shortcuts");
}
}
}
/**
* Parse folder starting at current {@link XmlPullParser} location.
*/
private boolean loadFolder(SQLiteDatabase db, ContentValues values, Resources res,
XmlResourceParser parser) throws IOException, XmlPullParserException {
final String title;
final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
if (titleResId != 0) {
title = res.getString(titleResId);
} else {
title = mContext.getResources().getString(R.string.folder_name);
}
values.put(LauncherSettings.Favorites.TITLE, title);
long folderId = addFolder(db, values);
boolean added = folderId >= 0;
ArrayList<Long> folderItems = new ArrayList<Long>();
addToFolder(db, res, parser, folderItems, folderId);
// 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) {
// Delete the folder
deleteId(db, folderId);
// If we have a single item, promote it to where the folder
// would have been.
if (folderItems.size() == 1) {
final ContentValues childValues = new ContentValues();
copyInteger(values, childValues, LauncherSettings.Favorites.CONTAINER);
copyInteger(values, childValues, LauncherSettings.Favorites.SCREEN);
copyInteger(values, childValues, LauncherSettings.Favorites.CELLX);
copyInteger(values, childValues, LauncherSettings.Favorites.CELLY);
final long id = folderItems.get(0);
db.update(TABLE_FAVORITES, childValues,
LauncherSettings.Favorites._ID + "=" + id, null);
} else {
added = false;
}
}
return added;
}
// A meta shortcut attempts to resolve an intent specified as a URI in the XML, if a
// logical choice for what shortcut should be used for that intent exists, then it is
// added. Otherwise add nothing.
private long addAppShortcutByUri(SQLiteDatabase db, ContentValues values,
String intentUri) {
Intent metaIntent;
try {
metaIntent = Intent.parseUri(intentUri, 0);
} catch (URISyntaxException e) {
Log.e(TAG, "Unable to add meta-favorite: " + intentUri, e);
return -1;
}
ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
PackageManager.MATCH_DEFAULT_ONLY);
final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
// Verify that the result is an app and not just the resolver dialog asking which
// app to use.
if (wouldLaunchResolverActivity(resolved, appList)) {
// If only one of the results is a system app then choose that as the default.
final ResolveInfo systemApp = getSingleSystemActivity(appList);
if (systemApp == null) {
// There is no logical choice for this meta-favorite, so rather than making
// a bad choice just add nothing.
Log.w(TAG, "No preference or single system activity found for "
+ metaIntent.toString());
return -1;
}
resolved = systemApp;
}
final ActivityInfo info = resolved.activityInfo;
final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
if (intent == null) {
return -1;
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addAppShortcut(db, values, info.loadLabel(mPackageManager).toString(), intent);
}
private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
ResolveInfo systemResolve = null;
final int N = appList.size();
for (int i = 0; i < N; ++i) {
try {
ApplicationInfo info = mPackageManager.getApplicationInfo(
appList.get(i).activityInfo.packageName, 0);
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (systemResolve != null) {
return null;
} else {
systemResolve = appList.get(i);
}
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to get info about resolve results", e);
return null;
}
}
return systemResolve;
}
private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
List<ResolveInfo> appList) {
// If the list contains the above resolved activity, then it can't be
// ResolverActivity itself.
for (int i = 0; i < appList.size(); ++i) {
ResolveInfo tmp = appList.get(i);
if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
&& tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
return false;
}
}
return true;
}
private long addAppShortcut(SQLiteDatabase db, ContentValues values,
XmlResourceParser parser) {
final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
final String uri = getAttributeValue(parser, ATTR_URI);
if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
ActivityInfo info;
try {
ComponentName cn;
try {
cn = new ComponentName(packageName, className);
info = mPackageManager.getActivityInfo(cn, 0);
} catch (PackageManager.NameNotFoundException nnfe) {
String[] packages = mPackageManager.currentToCanonicalPackageNames(
new String[] { packageName });
cn = new ComponentName(packages[0], className);
info = mPackageManager.getActivityInfo(cn, 0);
}
final Intent intent = buildMainIntent();
intent.setComponent(cn);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
return addAppShortcut(db, values, info.loadLabel(mPackageManager).toString(),
intent);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to add favorite: " + packageName +
"/" + className, e);
}
return -1;
} else if (!TextUtils.isEmpty(uri)) {
// If no component specified try to find a shortcut to add from the URI.
return addAppShortcutByUri(db, values, uri);
} else {
Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
return -1;
}
}
private long addAppShortcut(SQLiteDatabase db, ContentValues values, String title,
Intent intent) {
long id = generateNewItemId();
values.put(Favorites.INTENT, intent.toUri(0));
values.put(Favorites.TITLE, title);
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
values.put(Favorites.SPANX, 1);
values.put(Favorites.SPANY, 1);
values.put(Favorites._ID, id);
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
return -1;
} else {
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 = generateNewItemId();
values.put(Favorites._ID, id);
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
return -1;
} else {
return id;
}
}
private ComponentName getSearchWidgetProvider() {
SearchManager searchManager =
(SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
@ -1816,140 +1354,6 @@ public class LauncherProvider extends ContentProvider {
return null;
}
private boolean addAppWidget(XmlResourceParser parser, int type,
SQLiteDatabase db, ContentValues values)
throws XmlPullParserException, IOException {
String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
String className = getAttributeValue(parser, ATTR_CLASS_NAME);
if (packageName == null || className == null) {
return false;
}
boolean hasPackage = true;
ComponentName cn = new ComponentName(packageName, className);
try {
mPackageManager.getReceiverInfo(cn, 0);
} catch (Exception e) {
String[] packages = mPackageManager.currentToCanonicalPackageNames(
new String[] { packageName });
cn = new ComponentName(packages[0], className);
try {
mPackageManager.getReceiverInfo(cn, 0);
} catch (Exception e1) {
System.out.println("Can't find widget provider: " + className);
hasPackage = false;
}
}
if (hasPackage) {
String spanX = getAttributeValue(parser, ATTR_SPAN_X);
String spanY = getAttributeValue(parser, ATTR_SPAN_Y);
values.put(Favorites.SPANX, spanX);
values.put(Favorites.SPANY, spanY);
// 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;
}
if (TAG_EXTRA.equals(parser.getName())) {
String key = getAttributeValue(parser, ATTR_KEY);
String value = getAttributeValue(parser, ATTR_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");
}
}
return addAppWidget(db, values, cn, extras);
}
return false;
}
private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
Bundle extras) {
boolean allocatedAppWidgets = false;
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
try {
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
values.put(Favorites.APPWIDGET_ID, appWidgetId);
values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
values.put(Favorites._ID, generateNewItemId());
dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
allocatedAppWidgets = true;
// TODO: need to check return value
appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
// 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);
}
} catch (RuntimeException ex) {
Log.e(TAG, "Problem allocating appWidgetId", ex);
}
return allocatedAppWidgets;
}
private long addUriShortcut(SQLiteDatabase db, ContentValues values, Resources res,
XmlResourceParser parser) {
final int iconResId = getAttributeResourceValue(parser, ATTR_ICON, 0);
final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
Intent intent;
String uri = null;
try {
uri = getAttributeValue(parser, ATTR_URI);
intent = Intent.parseUri(uri, 0);
} catch (URISyntaxException e) {
Log.w(TAG, "Shortcut has malformed uri: " + uri);
return -1; // Oh well
}
if (iconResId == 0 || titleResId == 0) {
Log.w(TAG, "Shortcut is missing title or icon resource ID");
return -1;
}
long id = generateNewItemId();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
values.put(Favorites.INTENT, intent.toUri(0));
values.put(Favorites.TITLE, res.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, res.getResourcePackageName(iconResId));
values.put(Favorites.ICON_RESOURCE, res.getResourceName(iconResId));
values.put(Favorites._ID, id);
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
return -1;
}
return id;
}
private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
final ContentResolver resolver = mContext.getContentResolver();
Cursor c = null;
@ -2249,38 +1653,6 @@ public class LauncherProvider extends ContentProvider {
return selectWhere.toString();
}
/**
* Return attribute value, attempting launcher-specific namespace first
* before falling back to anonymous attribute.
*/
private static String getAttributeValue(XmlResourceParser parser, String attribute) {
String value = parser.getAttributeValue(
"http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
if (value == null) {
value = parser.getAttributeValue(null, attribute);
}
return value;
}
/**
* Return attribute resource value, attempting launcher-specific namespace
* first before falling back to anonymous attribute.
*/
private static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
int defaultValue) {
int value = parser.getAttributeResourceValue(
"http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
defaultValue);
if (value == defaultValue) {
value = parser.getAttributeResourceValue(null, attribute, defaultValue);
}
return value;
}
private static void copyInteger(ContentValues from, ContentValues to, String key) {
to.put(key, from.getAsInteger(key));
}
static class SqlArguments {
public final String table;
public final String where;
@ -2312,29 +1684,4 @@ public class LauncherProvider extends ContentProvider {
}
}
}
static interface WorkspaceLoader {
/**
* @param screenIds A mutable list of screen its
* @return the number of workspace items added.
*/
int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds);
}
private static class SimpleWorkspaceLoader implements WorkspaceLoader {
private final Resources mRes;
private final int mWorkspaceId;
private final DatabaseHelper mHelper;
SimpleWorkspaceLoader(DatabaseHelper helper, Resources res, int workspaceId) {
mHelper = helper;
mRes = res;
mWorkspaceId = workspaceId;
}
@Override
public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
return mHelper.loadFavoritesRecursive(db, mRes, mWorkspaceId, screenIds);
}
}
}