Refactoring layout parsing code
Change-Id: Iee5b2280cb1e841bcfe91fdcf523de6fa7f7f3b8
This commit is contained in:
parent
91d79e820a
commit
3a5a9d18da
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue