diff options
Diffstat (limited to 'src/com/android/launcher3')
19 files changed, 988 insertions, 1036 deletions
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java index 1bd290777..7f3b7fb35 100644 --- a/src/com/android/launcher3/AppsCustomizePagedView.java +++ b/src/com/android/launcher3/AppsCustomizePagedView.java @@ -728,7 +728,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) { // Exit spring loaded mode if we have not successfully dropped or have not handled the // drop in Workspace - mLauncher.exitSpringLoadedDragMode(); + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); mLauncher.unlockScreenOrientation(false); } else { mLauncher.unlockScreenOrientation(false); diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 08ed89b43..a5d22286d 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -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; - private final ContentValues mValues; + protected final PackageManager mPackageManager; + protected final Resources mSourceRes; + protected final int mLayoutId; + + private final int mHotseatAllAppsRank; - private final Resources mRes; - private final int mLayoutId; + private final long[] mTemp = new long[2]; + private final ContentValues mValues; + private final String mRootTag; - 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,45 +173,60 @@ public class AutoInstallsLayout implements WorkspaceLoader { if (type != XmlPullParser.START_TAG) { continue; } + count += parseAndAddNode(parser, tagParserMap, screenIds); + } + return count; + } - 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); + /** + * 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)); + } + } - 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++; + /** + * 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 count; + return 0; } protected long addShortcut(String title, Intent intent, int type) { @@ -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, diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java new file mode 100644 index 000000000..e3ea40ebb --- /dev/null +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -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); + } + } +} diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 05e8906cb..ea058ea71 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -21,8 +21,6 @@ import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; @@ -41,12 +39,8 @@ import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.LinearInterpolator; -import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; -import java.util.List; -import java.util.Set; - public class DeleteDropTarget extends ButtonDropTarget { private static int DELETE_ANIMATION_DURATION = 285; private static int FLING_DELETE_ANIMATION_DURATION = 350; @@ -266,7 +260,7 @@ public class DeleteDropTarget extends ButtonDropTarget { public void run() { completeDrop(d); mSearchDropTargetBar.onDragEnd(); - mLauncher.exitSpringLoadedDragMode(); + mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null); } }; dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f, diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index daf5556d4..b2366bb2b 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -75,7 +75,6 @@ public class DeviceProfile { private float hotseatIconSize; int defaultLayoutId; - int defaultNoAllAppsLayoutId; boolean isLandscape; boolean isTablet; @@ -136,7 +135,7 @@ public class DeviceProfile { private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>(); DeviceProfile(String n, float w, float h, float r, float c, - float is, float its, float hs, float his, int dlId, int dnalId) { + float is, float its, float hs, float his, int dlId) { // Ensure that we have an odd number of hotseat items (since we need to place all apps) if (!LauncherAppState.isDisableAllApps() && hs % 2 == 0) { throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces"); @@ -152,7 +151,6 @@ public class DeviceProfile { numHotseatIcons = hs; hotseatIconSize = his; defaultLayoutId = dlId; - defaultNoAllAppsLayoutId = dnalId; } DeviceProfile() { @@ -215,9 +213,6 @@ public class DeviceProfile { // Snap to the closest default layout id defaultLayoutId = closestProfile.defaultLayoutId; - // Snap to the closest default no all-apps layout id - defaultNoAllAppsLayoutId = closestProfile.defaultNoAllAppsLayoutId; - // Interpolate the icon size points.clear(); for (DeviceProfile p : profiles) { diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java index 94a07d706..aa08148d2 100644 --- a/src/com/android/launcher3/DynamicGrid.java +++ b/src/com/android/launcher3/DynamicGrid.java @@ -60,41 +60,30 @@ public class DynamicGrid { DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm); // Our phone profiles include the bar sizes in each orientation deviceProfiles.add(new DeviceProfile("Super Short Stubby", - 255, 300, 2, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 255, 300, 2, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Shorter Stubby", - 255, 400, 3, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 255, 400, 3, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Short Stubby", - 275, 420, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 275, 420, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Stubby", - 255, 450, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 255, 450, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus S", - 296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 4", - 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Nexus 5", - 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4)); deviceProfiles.add(new DeviceProfile("Large Phone", - 406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5, - R.xml.default_workspace_5x5_no_all_apps)); + 406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5)); // The tablet profile is odd in that the landscape orientation // also includes the nav bar on the side deviceProfiles.add(new DeviceProfile("Nexus 7", - 575, 904, 5, 6, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6, - R.xml.default_workspace_5x6_no_all_apps)); + 575, 904, 5, 6, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6)); // Larger tablet profiles always have system bars on the top & bottom deviceProfiles.add(new DeviceProfile("Nexus 10", - 727, 1207, 5, 6, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6, - R.xml.default_workspace_5x6_no_all_apps)); + 727, 1207, 5, 6, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6)); deviceProfiles.add(new DeviceProfile("20-inch Tablet", - 1527, 2527, 7, 7, 100, 20, 7, 72, R.xml.default_workspace_4x4, - R.xml.default_workspace_4x4_no_all_apps)); + 1527, 2527, 7, 7, 100, 20, 7, 72, R.xml.default_workspace_4x4)); mMinWidth = dpiFromPx(minWidthPx, dm); mMinHeight = dpiFromPx(minHeightPx, dm); mProfile = new DeviceProfile(context, deviceProfiles, diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java index 12b7a4076..7d4664abb 100644 --- a/src/com/android/launcher3/FocusIndicatorView.java +++ b/src/com/android/launcher3/FocusIndicatorView.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; @@ -28,6 +30,7 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen // It can be any number >0. The view is resized using scaleX and scaleY. static final int DEFAULT_LAYOUT_SIZE = 100; private static final float MIN_VISIBLE_ALPHA = 0.2f; + private static final long ANIM_DURATION = 150; private static final int[] sTempPos = new int[2]; private static final int[] sTempShift = new int[2]; @@ -35,6 +38,9 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen private final int[] mIndicatorPos = new int[2]; private final int[] mTargetViewPos = new int[2]; + private ObjectAnimator mCurrentAnimation; + private ViewAnimState mTargetState; + private View mLastFocusedView; private boolean mInitiated; @@ -82,34 +88,58 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen int indicatorWidth = getWidth(); int indicatorHeight = getHeight(); - float scaleX = v.getScaleX() * v.getWidth() / indicatorWidth; - float scaleY = v.getScaleY() * v.getHeight() / indicatorHeight; + endCurrentAnimation(); + ViewAnimState nextState = new ViewAnimState(); + nextState.scaleX = v.getScaleX() * v.getWidth() / indicatorWidth; + nextState.scaleY = v.getScaleY() * v.getHeight() / indicatorHeight; getLocationRelativeToParentPagedView(v, mTargetViewPos); - float x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - scaleX) * indicatorWidth / 2; - float y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - scaleY) * indicatorHeight / 2; + nextState.x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - nextState.scaleX) * indicatorWidth / 2; + nextState.y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - nextState.scaleY) * indicatorHeight / 2; if (getAlpha() > MIN_VISIBLE_ALPHA) { - animate() - .translationX(x) - .translationY(y) - .scaleX(scaleX) - .scaleY(scaleY) - .alpha(1); + mTargetState = nextState; + mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this, + PropertyValuesHolder.ofFloat(View.ALPHA, 1), + PropertyValuesHolder.ofFloat(View.TRANSLATION_X, mTargetState.x), + PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, mTargetState.y), + PropertyValuesHolder.ofFloat(View.SCALE_X, mTargetState.scaleX), + PropertyValuesHolder.ofFloat(View.SCALE_Y, mTargetState.scaleY)); } else { - setTranslationX(x); - setTranslationY(y); - setScaleX(scaleX); - setScaleY(scaleY); - animate().alpha(1); + applyState(nextState); + mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this, + PropertyValuesHolder.ofFloat(View.ALPHA, 1)); } mLastFocusedView = v; } else { if (mLastFocusedView == v) { mLastFocusedView = null; - animate().alpha(0); + endCurrentAnimation(); + mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this, + PropertyValuesHolder.ofFloat(View.ALPHA, 0)); } } + if (mCurrentAnimation != null) { + mCurrentAnimation.setDuration(ANIM_DURATION).start(); + } + } + + private void endCurrentAnimation() { + if (mCurrentAnimation != null) { + mCurrentAnimation.cancel(); + mCurrentAnimation = null; + } + if (mTargetState != null) { + applyState(mTargetState); + mTargetState = null; + } + } + + private void applyState(ViewAnimState state) { + setTranslationX(state.x); + setTranslationY(state.y); + setScaleX(state.scaleX); + setScaleY(state.scaleY); } @Override @@ -143,4 +173,8 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen shift[0] = shift[1] = 0; } } + + private static final class ViewAnimState { + float x, y, scaleX, scaleY; + } } diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 06f9f2941..5a0875b30 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -24,7 +24,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -117,8 +116,7 @@ public class IconCache { } public Drawable getFullResDefaultActivityIcon() { - return getFullResIcon(Resources.getSystem(), - android.R.mipmap.sym_def_app_icon); + return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon); } private Drawable getFullResIcon(Resources resources, int iconId) { @@ -151,12 +149,7 @@ public class IconCache { return mIconDpi; } - public Drawable getFullResIcon(ResolveInfo info) { - return getFullResIcon(info.activityInfo); - } - public Drawable getFullResIcon(ActivityInfo info) { - Resources resources; try { resources = mPackageManager.getResourcesForApplication( @@ -190,16 +183,14 @@ public class IconCache { /** * Remove any records for the supplied ComponentName. */ - public void remove(ComponentName componentName, UserHandleCompat user) { - synchronized (mCache) { - mCache.remove(new CacheKey(componentName, user)); - } + public synchronized void remove(ComponentName componentName, UserHandleCompat user) { + mCache.remove(new CacheKey(componentName, user)); } /** * Remove any records for the supplied package name. */ - public void remove(String packageName, UserHandleCompat user) { + public synchronized void remove(String packageName, UserHandleCompat user) { HashSet<CacheKey> forDeletion = new HashSet<CacheKey>(); for (CacheKey key: mCache.keySet()) { if (key.componentName.getPackageName().equals(packageName) @@ -215,24 +206,20 @@ public class IconCache { /** * Empty out the cache. */ - public void flush() { - synchronized (mCache) { - mCache.clear(); - } + public synchronized void flush() { + mCache.clear(); } /** * Empty out the cache that aren't of the correct grid size */ - public void flushInvalidIcons(DeviceProfile grid) { - synchronized (mCache) { - Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator(); - while (it.hasNext()) { - final CacheEntry e = it.next().getValue(); - if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx - || e.icon.getHeight() < grid.iconSizePx)) { - it.remove(); - } + public synchronized void flushInvalidIcons(DeviceProfile grid) { + Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator(); + while (it.hasNext()) { + final CacheEntry e = it.next().getValue(); + if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx + || e.icon.getHeight() < grid.iconSizePx)) { + it.remove(); } } } @@ -240,90 +227,78 @@ public class IconCache { /** * Fill in "application" with the icon and label for "info." */ - public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, + public synchronized void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info, HashMap<Object, CharSequence> labelCache) { - synchronized (mCache) { - CacheEntry entry = cacheLocked(application.componentName, info, labelCache, - info.getUser(), false); - - application.title = entry.title; - application.iconBitmap = entry.icon; - application.contentDescription = entry.contentDescription; - } - } + CacheEntry entry = cacheLocked(application.componentName, info, labelCache, + info.getUser(), false); - public Bitmap getIcon(Intent intent, UserHandleCompat user) { - return getIcon(intent, null, user, true); + application.title = entry.title; + application.iconBitmap = entry.icon; + application.contentDescription = entry.contentDescription; } - private Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) { - synchronized (mCache) { - ComponentName component = intent.getComponent(); - // null info means not installed, but if we have a component from the intent then - // we should still look in the cache for restored app icons. - if (component == null) { - return getDefaultIcon(user); - } - - LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon); - if (title != null) { - entry.title = title; - entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user); - } - return entry.icon; + public synchronized Bitmap getIcon(Intent intent, UserHandleCompat user) { + ComponentName component = intent.getComponent(); + // null info means not installed, but if we have a component from the intent then + // we should still look in the cache for restored app icons. + if (component == null) { + return getDefaultIcon(user); } + + LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user); + CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, true); + return entry.icon; } /** * Fill in "shortcutInfo" with the icon and label for "info." */ - public void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, UserHandleCompat user, - boolean usePkgIcon) { - synchronized (mCache) { - ComponentName component = intent.getComponent(); - // null info means not installed, but if we have a component from the intent then - // we should still look in the cache for restored app icons. - if (component == null) { - shortcutInfo.setIcon(getDefaultIcon(user)); - shortcutInfo.title = ""; - shortcutInfo.usingFallbackIcon = true; - } else { - LauncherActivityInfoCompat launcherActInfo = - mLauncherApps.resolveActivity(intent, user); - CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon); + public synchronized void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, + UserHandleCompat user, boolean usePkgIcon) { + ComponentName component = intent.getComponent(); + // null info means not installed, but if we have a component from the intent then + // we should still look in the cache for restored app icons. + if (component == null) { + shortcutInfo.setIcon(getDefaultIcon(user)); + shortcutInfo.title = ""; + shortcutInfo.usingFallbackIcon = true; + } else { + LauncherActivityInfoCompat launcherActInfo = + mLauncherApps.resolveActivity(intent, user); + CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon); - shortcutInfo.setIcon(entry.icon); - shortcutInfo.title = entry.title; - shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); - } + shortcutInfo.setIcon(entry.icon); + shortcutInfo.title = entry.title; + shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user); } } - public Bitmap getDefaultIcon(UserHandleCompat user) { + public synchronized Bitmap getDefaultIcon(UserHandleCompat user) { if (!mDefaultIcons.containsKey(user)) { mDefaultIcons.put(user, makeDefaultIcon(user)); } return mDefaultIcons.get(user); } - public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info, + public synchronized Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info, HashMap<Object, CharSequence> labelCache) { - synchronized (mCache) { - if (info == null || component == null) { - return null; - } - - CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false); - return entry.icon; + if (info == null || component == null) { + return null; } + + CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false); + return entry.icon; } public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) { return mDefaultIcons.get(user) == icon; } + /** + * Retrieves the entry from the cache. If the entry is not present, it creates a new entry. + * This method is not thread safe, it must be called from a synchronized method. + */ private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info, HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) { CacheKey cacheKey = new CacheKey(componentName, user); @@ -380,7 +355,7 @@ public class IconCache { * Adds a default package entry in the cache. This entry is not persisted and will be removed * when the cache is flushed. */ - public void cachePackageInstallInfo(String packageName, UserHandleCompat user, + public synchronized void cachePackageInstallInfo(String packageName, UserHandleCompat user, Bitmap icon, CharSequence title) { remove(packageName, user); @@ -395,9 +370,10 @@ public class IconCache { /** * Gets an entry for the package, which can be used as a fallback entry for various components. + * This method is not thread safe, it must be called from a synchronized method. */ private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) { - ComponentName cn = getPackageComponent(packageName); + ComponentName cn = new ComponentName(packageName, EMPTY_CLASS_NAME);; CacheKey cacheKey = new CacheKey(cn, user); CacheEntry entry = mCache.get(cacheKey); if (entry == null) { @@ -420,15 +396,13 @@ public class IconCache { return entry; } - public HashMap<ComponentName,Bitmap> getAllIcons() { - synchronized (mCache) { - HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>(); - for (CacheKey ck : mCache.keySet()) { - final CacheEntry e = mCache.get(ck); - set.put(ck.componentName, e.icon); - } - return set; + public synchronized HashMap<ComponentName,Bitmap> getAllIcons() { + HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>(); + for (CacheKey ck : mCache.keySet()) { + final CacheEntry e = mCache.get(ck); + set.put(ck.componentName, e.icon); } + return set; } /** @@ -534,23 +508,15 @@ public class IconCache { * Remove a pre-loaded icon from the persistent icon cache. * * @param componentName the component that should own the icon - * @returns true on success */ - public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) { + public void deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) { // We don't keep icons for other profiles in persistent cache. - if (!user.equals(UserHandleCompat.myUserHandle())) { - return false; - } - if (componentName == null) { - return false; - } - if (mCache.remove(componentName) != null) { - if (DEBUG) Log.d(TAG, "removed pre-loaded icon from the in-memory cache"); + if (!user.equals(UserHandleCompat.myUserHandle()) || componentName == null) { + return; } + remove(componentName, user); boolean success = mContext.deleteFile(getResourceFilename(componentName)); if (DEBUG && success) Log.d(TAG, "removed pre-loaded icon from persistent cache"); - - return success; } private static String getResourceFilename(ComponentName component) { @@ -558,8 +524,4 @@ public class IconCache { String filename = resourceName.replace(File.separatorChar, '_'); return RESOURCE_FILE_PREFIX + filename; } - - static ComponentName getPackageComponent(String packageName) { - return new ComponentName(packageName, EMPTY_CLASS_NAME); - } } diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 2edde4fae..e9fb499ad 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -17,7 +17,6 @@ package com.android.launcher3; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -45,17 +44,17 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static final String TAG = "InstallShortcutReceiver"; private static final boolean DBG = false; - public static final String ACTION_INSTALL_SHORTCUT = + private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; - public static final String DATA_INTENT_KEY = "intent.data"; - public static final String LAUNCH_INTENT_KEY = "intent.launch"; - public static final String NAME_KEY = "name"; - public static final String ICON_KEY = "icon"; - public static final String ICON_RESOURCE_NAME_KEY = "iconResource"; - public static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage"; + private static final String DATA_INTENT_KEY = "intent.data"; + private static final String LAUNCH_INTENT_KEY = "intent.launch"; + private static final String NAME_KEY = "name"; + private static final String ICON_KEY = "icon"; + private static final String ICON_RESOURCE_NAME_KEY = "iconResource"; + private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage"; // The set of shortcuts that are pending install - public static final String APPS_PENDING_INSTALL = "apps_to_install"; + private static final String APPS_PENDING_INSTALL = "apps_to_install"; public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; @@ -63,17 +62,13 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static final int INSTALL_SHORTCUT_SUCCESSFUL = 0; private static final int INSTALL_SHORTCUT_IS_DUPLICATE = -1; - // A mime-type representing shortcut data - public static final String SHORTCUT_MIMETYPE = - "com.android.launcher3/shortcut"; - private static Object sLock = new Object(); private static void addToStringSet(SharedPreferences sharedPrefs, SharedPreferences.Editor editor, String key, String value) { Set<String> strings = sharedPrefs.getStringSet(key, null); if (strings == null) { - strings = new HashSet<String>(0); + strings = new HashSet<String>(1); } else { strings = new HashSet<String>(strings); } @@ -133,6 +128,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver { Intent launchIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0); String pn = launchIntent.getPackage(); if (pn == null) { + if (launchIntent.getComponent() == null) { + continue; + } pn = launchIntent.getComponent().getPackageName(); } if (packageNames.contains(pn)) { @@ -355,7 +353,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); } LauncherAppState app = LauncherAppState.getInstance(); - ShortcutInfo info = app.getModel().infoFromShortcutIntent(context, data, null); + ShortcutInfo info = app.getModel().infoFromShortcutIntent(context, data); info.title = ensureValidName(context, launchIntent, info.title); return info; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3762c2f50..178f6cb56 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -404,6 +404,10 @@ public class Launcher extends Activity .build()); } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.preOnCreate(); + } + super.onCreate(savedInstanceState); LauncherAppState.setApplicationContext(getApplicationContext()); @@ -500,13 +504,38 @@ public class Launcher extends Activity showFirstRunActivity(); showFirstRunClings(); } + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onCreate(savedInstanceState); + } + } + + private LauncherCallbacks mLauncherCallbacks; + + public void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onPostCreate(savedInstanceState); + } + } + + public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { + mLauncherCallbacks = callbacks; + return true; } @Override - public void onLauncherProviderChange() { } + public void onLauncherProviderChange() { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onLauncherProviderChange(); + } + } - /** To be overriden by subclasses to hint to Launcher that we have custom content */ + /** To be overridden by subclasses to hint to Launcher that we have custom content */ protected boolean hasCustomContentToLeft() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.hasCustomContentToLeft(); + } return false; } @@ -516,6 +545,9 @@ public class Launcher extends Activity * {@link #hasCustomContentToLeft()} is {@code true}. */ protected void populateCustomContentContainer() { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.populateCustomContentContainer(); + } } /** @@ -617,7 +649,7 @@ public class Launcher extends Activity private static void readConfiguration(Context context, LocaleConfiguration configuration) { DataInputStream in = null; try { - in = new DataInputStream(context.openFileInput(LauncherFiles.LAUNCHER_PREFS)); + in = new DataInputStream(context.openFileInput(LauncherFiles.LAUNCHER_PREFERENCES)); configuration.locale = in.readUTF(); configuration.mcc = in.readInt(); configuration.mnc = in.readInt(); @@ -640,7 +672,7 @@ public class Launcher extends Activity DataOutputStream out = null; try { out = new DataOutputStream(context.openFileOutput( - LauncherFiles.LAUNCHER_PREFS, MODE_PRIVATE)); + LauncherFiles.LAUNCHER_PREFERENCES, MODE_PRIVATE)); out.writeUTF(configuration.locale); out.writeInt(configuration.mcc); out.writeInt(configuration.mnc); @@ -649,7 +681,7 @@ public class Launcher extends Activity // Ignore } catch (IOException e) { //noinspection ResultOfMethodCallIgnored - context.getFileStreamPath(LauncherFiles.LAUNCHER_PREFS).delete(); + context.getFileStreamPath(LauncherFiles.LAUNCHER_PREFERENCES).delete(); } finally { if (out != null) { try { @@ -954,12 +986,20 @@ public class Launcher extends Activity protected void onStop() { super.onStop(); FirstFrameAnimatorHelper.setIsVisible(false); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onStop(); + } } @Override protected void onStart() { super.onStart(); FirstFrameAnimatorHelper.setIsVisible(true); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onStart(); + } } @Override @@ -969,6 +1009,11 @@ public class Launcher extends Activity startTime = System.currentTimeMillis(); Log.v(TAG, "Launcher.onResume()"); } + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.preOnResume(); + } + super.onResume(); // Restore the previous launcher state @@ -1057,6 +1102,10 @@ public class Launcher extends Activity mWorkspace.onResume(); PackageInstallerCompat.getInstance(this).onResume(); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onResume(); + } } @Override @@ -1075,25 +1124,10 @@ public class Launcher extends Activity if (mWorkspace.getCustomContentCallbacks() != null) { mWorkspace.getCustomContentCallbacks().onHide(); } - } - - QSBScroller mQsbScroller = new QSBScroller() { - int scrollY = 0; - @Override - public void setScrollY(int scroll) { - scrollY = scroll; - - if (mWorkspace.isOnOrMovingToCustomContent()) { - mSearchDropTargetBar.setTranslationY(- scrollY); - getQsbBar().setTranslationY(-scrollY); - } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onPause(); } - }; - - public void resetQSBScroll() { - mSearchDropTargetBar.animate().translationY(0).start(); - getQsbBar().animate().translationY(0).start(); } public interface CustomContentCallbacks { @@ -1112,17 +1146,16 @@ public class Launcher extends Activity } protected boolean hasSettings() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.hasSettings(); + } return false; } - public interface QSBScroller { - public void setScrollY(int scrollY); - } - public QSBScroller addToCustomContentPage(View customContent, + public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks, String description) { mWorkspace.addToCustomContentPage(customContent, callbacks, description); - return mQsbScroller; } // The custom content needs to offset its content to account for the QSB @@ -1147,6 +1180,10 @@ public class Launcher extends Activity public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mHasFocus = hasFocus; + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onWindowFocusChanged(hasFocus); + } } private boolean acceptFilter() { @@ -1333,9 +1370,6 @@ public class Launcher extends Activity settingsButton.setOnTouchListener(getHapticFeedbackTouchListener()); } else { settingsButton.setVisibility(View.GONE); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) widgetButton.getLayoutParams(); - lp.gravity = Gravity.END | Gravity.TOP; - widgetButton.requestLayout(); } mOverviewPanel.setAlpha(0f); @@ -1435,7 +1469,7 @@ public class Launcher extends Activity boolean foundCellSpan = false; - ShortcutInfo info = mModel.infoFromShortcutIntent(this, data, null); + ShortcutInfo info = mModel.infoFromShortcutIntent(this, data); if (info == null) { return; } @@ -1885,8 +1919,11 @@ public class Launcher extends Activity Folder openFolder = mWorkspace.getOpenFolder(); // In all these cases, only animate if we're already on home mWorkspace.exitWidgetResizeMode(); + + boolean moveToDefaultScreen = mLauncherCallbacks != null ? + mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true; if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() && - openFolder == null && shouldMoveToDefaultScreenOnHomeIntent()) { + openFolder == null && moveToDefaultScreen) { mWorkspace.moveToDefaultScreen(true); } @@ -1913,27 +1950,18 @@ public class Launcher extends Activity mAppsCustomizeTabHost.reset(); } - onHomeIntent(); + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onHomeIntent(); + } } if (DEBUG_RESUME_TIME) { Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime)); } - } - - /** - * Override point for subclasses to prevent movement to the default screen when the home - * button is pressed. Used (for example) in GEL, to prevent movement during a search. - */ - protected boolean shouldMoveToDefaultScreenOnHomeIntent() { - return true; - } - /** - * Override point for subclasses to provide custom behaviour for when a home intent is fired. - */ - protected void onHomeIntent() { - // Do nothing + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onNewIntent(intent); + } } @Override @@ -1985,6 +2013,10 @@ public class Launcher extends Activity outState.putInt("apps_customize_currentIndex", currentIndex); } outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onSaveInstanceState(outState); + } } @Override @@ -2033,6 +2065,10 @@ public class Launcher extends Activity mDragController = null; LauncherAnimUtils.onDestroyActivity(); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onDestroy(); + } } public DragController getDragController() { @@ -2086,6 +2122,11 @@ public class Launcher extends Activity */ public boolean startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) { + if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) { + return mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData, + sourceBounds); + } + startGlobalSearch(initialQuery, selectInitialQuery, appSearchData, sourceBounds); return false; @@ -2112,7 +2153,7 @@ public class Launcher extends Activity } else { appSearchData = new Bundle(appSearchData); } - // Set source to package name of app that starts global search, if not set already. + // Set source to package name of app that starts global search if not set already. if (!appSearchData.containsKey("source")) { appSearchData.putString("source", getPackageName()); } @@ -2150,6 +2191,10 @@ public class Launcher extends Activity showWorkspace(true); } } + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.onPrepareOptionsMenu(menu); + } + return false; } @@ -2184,7 +2229,11 @@ public class Launcher extends Activity } } - protected void onWorkspaceLockedChanged() { } + protected void onWorkspaceLockedChanged() { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onWorkspaceLockedChanged(); + } + } private void resetAddInfo() { mPendingAddInfo.container = ItemInfo.NO_ID; @@ -2346,6 +2395,9 @@ public class Launcher extends Activity } protected ComponentName getWallpaperPickerComponent() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.getWallpaperPickerComponent(); + } return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName()); } @@ -2384,6 +2436,10 @@ public class Launcher extends Activity @Override public void onBackPressed() { + if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) { + return; + } + if (isAllAppsVisible()) { if (mAppsCustomizeContent.getContentType() == AppsCustomizePagedView.ContentType.Applications) { @@ -2466,6 +2522,9 @@ public class Launcher extends Activity public void onClickPagedViewIcon(View v) { startAppShortcutOrInfoActivity(v); + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickPagedViewIcon(v); + } } public boolean onTouch(View v, MotionEvent event) { @@ -2476,6 +2535,11 @@ public class Launcher extends Activity * Event handler for the app widget view which has not fully restored. */ public void onClickPendingWidget(final PendingAppWidgetHostView v) { + if (mIsSafeModeEnabled) { + Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); + return; + } + final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag(); if (v.isReadyForClickSetup()) { int widgetId = info.appWidgetId; @@ -2527,6 +2591,11 @@ public class Launcher extends Activity } public void startVoice() { + if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) { + mLauncherCallbacks.startVoice(); + return; + } + try { final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); @@ -2557,6 +2626,9 @@ public class Launcher extends Activity } else { showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false); } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickAllAppsButton(v); + } } private void showBrokenAppInstallDialog(final String packageName, @@ -2631,6 +2703,10 @@ public class Launcher extends Activity // Start activities startAppShortcutOrInfoActivity(v); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickAppShortcut(v); + } } private void startAppShortcutOrInfoActivity(View v) { @@ -2704,6 +2780,10 @@ public class Launcher extends Activity } } } + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickFolderIcon(v); + } } /** @@ -2712,7 +2792,14 @@ public class Launcher extends Activity */ protected void onClickAddWidgetButton(View view) { if (LOGD) Log.d(TAG, "onClickAddWidgetButton"); - showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true); + if (mIsSafeModeEnabled) { + Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); + } else { + showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true); + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickAddWidgetButton(view); + } + } } /** @@ -2724,6 +2811,10 @@ public class Launcher extends Activity final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER); pickWallpaper.setComponent(getWallpaperPickerComponent()); startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickWallpaperPicker(v); + } } /** @@ -2732,6 +2823,9 @@ public class Launcher extends Activity */ protected void onClickSettingsButton(View v) { if (LOGD) Log.d(TAG, "onClickSettingsButton"); + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickSettingsButton(v); + } } public void onTouchDownAllAppsButton(View v) { @@ -2759,13 +2853,27 @@ public class Launcher extends Activity return mHapticFeedbackTouchListener; } - public void onDragStarted(View view) {} + public void onDragStarted(View view) { + if (isOnCustomContent()) { + // Custom content screen doesn't participate in drag and drop. If on custom + // content screen, move to default. + moveWorkspaceToDefaultScreen(); + } + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onDragStarted(view); + } + } /** * Called when the user stops interacting with the launcher. * This implies that the user is now on the homescreen and is not doing housekeeping. */ - protected void onInteractionEnd() {} + protected void onInteractionEnd() { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onInteractionEnd(); + } + } /** * Called when the user starts interacting with the launcher. @@ -2776,7 +2884,11 @@ public class Launcher extends Activity * This is a good time to stop doing things that only make sense * when the user is on the homescreen and not doing housekeeping. */ - protected void onInteractionBegin() {} + protected void onInteractionBegin() { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onInteractionBegin(); + } + } void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) { String packageName = componentName.getPackageName(); @@ -3972,6 +4084,13 @@ public class Launcher extends Activity } public View getQsbBar() { + if (mLauncherCallbacks != null) { + View qsb = mLauncherCallbacks.getQsbBar(); + if (qsb != null) { + return qsb; + } + } + if (mQsb == null) { mQsb = mInflater.inflate(R.layout.qsb, mSearchDropTargetBar, false); mSearchDropTargetBar.addView(mQsb); @@ -3980,6 +4099,10 @@ public class Launcher extends Activity } protected boolean updateGlobalSearchIcon() { + if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) { + return true; + } + final View searchButtonContainer = findViewById(R.id.search_button_container); final ImageView searchButton = (ImageView) findViewById(R.id.search_button); final View voiceButtonContainer = findViewById(R.id.voice_button_container); @@ -4015,6 +4138,7 @@ public class Launcher extends Activity } protected void updateGlobalSearchIcon(Drawable.ConstantState d) { + if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) return; final View searchButtonContainer = findViewById(R.id.search_button_container); final View searchButton = (ImageView) findViewById(R.id.search_button); updateButtonWithDrawable(R.id.search_button, d); @@ -4022,6 +4146,9 @@ public class Launcher extends Activity } protected boolean updateVoiceSearchIcon(boolean searchVisible) { + if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) { + return true; + } final View voiceButtonContainer = findViewById(R.id.voice_button_container); final View voiceButton = findViewById(R.id.voice_button); @@ -4068,6 +4195,10 @@ public class Launcher extends Activity } protected void updateVoiceSearchIcon(Drawable.ConstantState d) { + if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) { + return; + } + final View voiceButtonContainer = findViewById(R.id.voice_button_container); final View voiceButton = findViewById(R.id.voice_button); updateButtonWithDrawable(R.id.voice_button, d); @@ -4075,6 +4206,9 @@ public class Launcher extends Activity } public void updateVoiceButtonProxyVisible(boolean forceDisableVoiceButtonProxy) { + if (mLauncherCallbacks != null) { + forceDisableVoiceButtonProxy |= mLauncherCallbacks.forceDisableVoiceButtonProxy(); + } final View voiceButtonProxy = findViewById(R.id.voice_button_proxy); if (voiceButtonProxy != null) { boolean visible = !forceDisableVoiceButtonProxy && @@ -4462,8 +4596,9 @@ public class Launcher extends Activity final Workspace workspace = mWorkspace; AppWidgetProviderInfo appWidgetInfo; - if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) && - ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) { + if (!mIsSafeModeEnabled + && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) + && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) { appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName); if (appWidgetInfo == null) { @@ -4513,7 +4648,7 @@ public class Launcher extends Activity LauncherModel.updateItemInDatabase(this, item); } - if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { + if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { final int appWidgetId = item.appWidgetId; appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId); if (DEBUG_WIDGETS) { @@ -4523,7 +4658,8 @@ public class Launcher extends Activity item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); } else { appWidgetInfo = null; - PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item); + PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, + mIsSafeModeEnabled); view.updateIcon(mIconCache); item.hostView = view; item.hostView.updateAppWidget(null); @@ -4617,6 +4753,10 @@ public class Launcher extends Activity mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null); } PackageInstallerCompat.getInstance(this).onFinishBind(); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.finishBindingItems(upgradePath); + } } private void sendLoadingCompleteBroadcastIfNecessary() { @@ -4698,6 +4838,9 @@ public class Launcher extends Activity LauncherModel.getSortedWidgetsAndShortcuts(this)); } } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.bindAllApplications(apps); + } } /** @@ -4916,16 +5059,10 @@ public class Launcher extends Activity } } - /** - * Called when the SearchBar hint should be changed. - * - * @param hint the hint to be displayed in the search bar. - */ - protected void onSearchBarHintChanged(String hint) { - - } - protected boolean isLauncherPreinstalled() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.isLauncherPreinstalled(); + } PackageManager pm = getPackageManager(); try { ApplicationInfo ai = pm.getApplicationInfo(getComponentName().getPackageName(), 0); @@ -4945,6 +5082,9 @@ public class Launcher extends Activity * when our wallpaper cropper was not yet used to set a wallpaper. */ protected boolean overrideWallpaperDimensions() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.overrideWallpaperDimensions(); + } return true; } @@ -4953,6 +5093,9 @@ public class Launcher extends Activity * before showing the standard launcher experience. */ protected boolean hasFirstRunActivity() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.hasFirstRunActivity(); + } return false; } @@ -4960,6 +5103,9 @@ public class Launcher extends Activity * To be overridden by subclasses to launch any first run activity */ protected Intent getFirstRunActivity() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.getFirstRunActivity(); + } return null; } @@ -4996,6 +5142,9 @@ public class Launcher extends Activity * screen that must be displayed and dismissed. */ protected boolean hasDismissableIntroScreen() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.hasDismissableIntroScreen(); + } return false; } @@ -5003,6 +5152,9 @@ public class Launcher extends Activity * Full screen intro screen to be shown and dismissed before the launcher can be used. */ protected View getIntroScreen() { + if (mLauncherCallbacks != null) { + return mLauncherCallbacks.getIntroScreen(); + } return null; } @@ -5114,6 +5266,9 @@ public class Launcher extends Activity @Override public void onPageSwitch(View newPage, int newPageIndex) { + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onPageSwitch(newPage, newPageIndex); + } } /** @@ -5145,6 +5300,9 @@ public class Launcher extends Activity writer.println(" " + sDumpLogs.get(i)); } } + if (mLauncherCallbacks != null) { + mLauncherCallbacks.dump(prefix, fd, writer, args); + } } public static void dumpDebugLogsToConsole() { diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index ab0b13598..03ab94bab 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -37,7 +37,6 @@ import java.util.ArrayList; public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { private static final String TAG = "LauncherAppState"; - private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; private static final boolean DEBUG = false; @@ -188,7 +187,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks { } public static String getSharedPreferencesKey() { - return SHARED_PREFERENCES_KEY; + return LauncherFiles.SHARED_PREFERENCES_KEY; } DeviceProfile initDynamicGrid(Context context, int minWidth, int minHeight, diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java new file mode 100644 index 000000000..aef2adc7f --- /dev/null +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -0,0 +1,89 @@ +package com.android.launcher3; + +import android.content.ComponentName; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Bundle; +import android.view.Menu; +import android.view.View; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks + * in order to add additional functionality. Some of these are very general, and give extending + * classes the ability to react to Activity life-cycle or specific user interactions. Others + * are more specific and relate to replacing parts of the application, for example, the search + * interface or the wallpaper picker. + */ +public interface LauncherCallbacks { + + /* + * Activity life-cycle methods. These methods are triggered after + * the code in the corresponding Launcher method is executed. + */ + public void preOnCreate(); + public void onCreate(Bundle savedInstanceState); + public void preOnResume(); + public void onResume(); + public void onStart(); + public void onStop(); + public void onPause(); + public void onDestroy(); + public void onSaveInstanceState(Bundle outState); + public void onPostCreate(Bundle savedInstanceState); + public void onNewIntent(Intent intent); + public void onActivityResult(int requestCode, int resultCode, Intent data); + public void onWindowFocusChanged(boolean hasFocus); + public boolean onPrepareOptionsMenu(Menu menu); + public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args); + public void onHomeIntent(); + public boolean handleBackPressed(); + + /* + * Extension points for providing custom behavior on certain user interactions. + */ + public void onLauncherProviderChange(); + public void finishBindingItems(final boolean upgradePath); + public void onClickAllAppsButton(View v); + public void bindAllApplications(ArrayList<AppInfo> apps); + public void onClickFolderIcon(View v); + public void onClickAppShortcut(View v); + public void onClickPagedViewIcon(View v); + public void onClickWallpaperPicker(View v); + public void onClickSettingsButton(View v); + public void onClickAddWidgetButton(View v); + public void onPageSwitch(View newPage, int newPageIndex); + public void onWorkspaceLockedChanged(); + public void onDragStarted(View view); + public void onInteractionBegin(); + public void onInteractionEnd(); + + /* + * Extension points for replacing the search experience + */ + public boolean forceDisableVoiceButtonProxy(); + public boolean providesSearch(); + public boolean startSearch(String initialQuery, boolean selectInitialQuery, + Bundle appSearchData, Rect sourceBounds); + public void startVoice(); + public boolean hasCustomContentToLeft(); + public void populateCustomContentContainer(); + public View getQsbBar(); + + /* + * Extensions points for adding / replacing some other aspects of the Launcher experience. + */ + public Intent getFirstRunActivity(); + public boolean hasFirstRunActivity(); + public boolean hasDismissableIntroScreen(); + public View getIntroScreen(); + public boolean shouldMoveToDefaultScreenOnHomeIntent(); + public boolean hasSettings(); + public ComponentName getWallpaperPickerComponent(); + public boolean overrideWallpaperDimensions(); + public boolean isLauncherPreinstalled(); + +} diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java index 89600c2df..fa053650f 100644 --- a/src/com/android/launcher3/LauncherFiles.java +++ b/src/com/android/launcher3/LauncherFiles.java @@ -12,16 +12,29 @@ import java.util.List; */ public class LauncherFiles { - public static final String SHARED_PREFS = "com.android.launcher3.prefs.xml"; + private static final String XML = ".xml"; + + public static final String DEFAULT_WALLPAPER_THUMBNAIL = "default_thumb2.jpg"; + public static final String DEFAULT_WALLPAPER_THUMBNAIL_OLD = "default_thumb.jpg"; public static final String LAUNCHER_DB = "launcher.db"; - public static final String LAUNCHER_PREFS = "launcher.preferences"; + public static final String LAUNCHER_PREFERENCES = "launcher.preferences"; + public static final String LAUNCHES_LOG = "launches.log"; + public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs"; + public static final String STATS_LOG = "stats.log"; + public static final String WALLPAPER_CROP_PREFERENCES_KEY = + WallpaperCropActivity.class.getName(); public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db"; public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db"; public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList( - SHARED_PREFS, + DEFAULT_WALLPAPER_THUMBNAIL, + DEFAULT_WALLPAPER_THUMBNAIL_OLD, LAUNCHER_DB, - LAUNCHER_PREFS, + LAUNCHER_PREFERENCES, + LAUNCHES_LOG, + SHARED_PREFERENCES_KEY + XML, + STATS_LOG, + WALLPAPER_CROP_PREFERENCES_KEY + XML, WALLPAPER_IMAGES_DB, WIDGET_PREVIEWS_DB)); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 30a033776..17670d25d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2241,7 +2241,7 @@ public class LauncherModel extends BroadcastReceiver // App restore has started. Update the flag appWidgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; - } else if (REMOVE_UNRESTORED_ICONS) { + } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) { Launcher.addDumpLog(TAG, "Unrestored widget removed: " + component, true); itemsToRemove.add(id); @@ -3534,17 +3534,6 @@ public class LauncherModel extends BroadcastReceiver } } - ShortcutInfo addShortcut(Context context, Intent data, long container, int screen, - int cellX, int cellY, boolean notify) { - final ShortcutInfo info = infoFromShortcutIntent(context, data, null); - if (info == null) { - return null; - } - addItemToDatabase(context, info, container, screen, cellX, cellY, notify); - - return info; - } - /** * Attempts to find an AppWidgetProviderInfo that matches the given component. */ @@ -3560,7 +3549,7 @@ public class LauncherModel extends BroadcastReceiver return null; } - ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) { + ShortcutInfo infoFromShortcutIntent(Context context, Intent data) { Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); @@ -3593,12 +3582,8 @@ public class LauncherModel extends BroadcastReceiver // users wouldn't get here without intent forwarding anyway. info.user = UserHandleCompat.myUserHandle(); if (icon == null) { - if (fallbackIcon != null) { - icon = fallbackIcon; - } else { - icon = mIconCache.getDefaultIcon(info.user); - info.usingFallbackIcon = true; - } + icon = mIconCache.getDefaultIcon(info.user); + info.usingFallbackIcon = true; } info.setIcon(icon); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 44ccb6cb3..9150dc0c1 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -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 @@ -360,11 +342,7 @@ public class LauncherProvider extends ContentProvider { private static int getDefaultWorkspaceResourceId() { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - if (LauncherAppState.isDisableAllApps()) { - return grid.defaultNoAllAppsLayoutId; - } else { - return grid.defaultLayoutId; - } + return grid.defaultLayoutId; } private static interface ContentValuesCallback { @@ -390,38 +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"; - - // 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"; - - // 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; @@ -431,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 @@ -787,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; } @@ -1288,14 +1234,16 @@ public class LauncherProvider extends ContentProvider { try { int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId); - if (appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) { - return true; + if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) { + return false; } } catch (RuntimeException e) { Log.e(TAG, "Failed to initialize external widget", e); + return false; } + } else { + return false; } - return false; } // Add screen id if not present @@ -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,379 +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 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>(); - - 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 { - throw new RuntimeException("Folders can contain only shortcuts"); - } - } - - // 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); @@ -1803,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; @@ -2236,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; @@ -2299,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); - } - } } diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index d23a33033..179c60a98 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -41,9 +41,9 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen private final LauncherAppWidgetInfo mInfo; private final int mStartState; private final Intent mIconLookupIntent; + private final boolean mDisabledForSafeMode; private Bitmap mIcon; - private PreloadIconDrawable mDrawable; private Drawable mCenterDrawable; private Drawable mTopCornerDrawable; @@ -53,11 +53,13 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen private final TextPaint mPaint; private Layout mSetupTextLayout; - public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info) { + public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, + boolean disabledForSafeMode) { super(context); mInfo = info; mStartState = info.restoreStatus; mIconLookupIntent = new Intent().setComponent(info.providerName); + mDisabledForSafeMode = disabledForSafeMode; mPaint = new TextPaint(); mPaint.setColor(0xFFFFFFFF); @@ -106,14 +108,21 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen return; } mIcon = icon; - if (mDrawable != null) { - mDrawable.setCallback(null); - mDrawable = null; + if (mCenterDrawable != null) { + mCenterDrawable.setCallback(null); + mCenterDrawable = null; } if (mIcon != null) { - // The view displays two modes, one with a setup icon and another with a preload icon - // in the center. - if (isReadyForClickSetup()) { + // The view displays three modes, + // 1) App icon in the center + // 2) Preload icon in the center + // 3) Setup icon in the center and app icon in the top right corner. + if (mDisabledForSafeMode) { + FastBitmapDrawable disabledIcon = Utilities.createIconDrawable(mIcon); + disabledIcon.setGhostModeEnabled(true); + mCenterDrawable = disabledIcon; + mTopCornerDrawable = null; + } else if (isReadyForClickSetup()) { mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting); mTopCornerDrawable = new FastBitmapDrawable(mIcon); } else { @@ -123,8 +132,9 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen } FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon); - mDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); - mDrawable.setCallback(this); + mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); + mCenterDrawable.setCallback(this); + mTopCornerDrawable = null; applyState(); } mDrawableSizeChanged = true; @@ -133,12 +143,12 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen @Override protected boolean verifyDrawable(Drawable who) { - return (who == mDrawable) || super.verifyDrawable(who); + return (who == mCenterDrawable) || super.verifyDrawable(who); } public void applyState() { - if (mDrawable != null) { - mDrawable.setLevel(Math.max(mInfo.installProgress, 0)); + if (mCenterDrawable != null) { + mCenterDrawable.setLevel(Math.max(mInfo.installProgress, 0)); } } @@ -158,23 +168,30 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen @Override protected void onDraw(Canvas canvas) { - if (mDrawable != null) { + if (mCenterDrawable == null) { + // Nothing to draw + return; + } + + if (mTopCornerDrawable == null) { if (mDrawableSizeChanged) { + int outset = (mCenterDrawable instanceof PreloadIconDrawable) ? + ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0; int maxSize = LauncherAppState.getInstance().getDynamicGrid() - .getDeviceProfile().iconSizePx + 2 * mDrawable.getOutset(); + .getDeviceProfile().iconSizePx + 2 * outset; int size = Math.min(maxSize, Math.min( getWidth() - getPaddingLeft() - getPaddingRight(), getHeight() - getPaddingTop() - getPaddingBottom())); mRect.set(0, 0, size, size); - mRect.inset(mDrawable.getOutset(), mDrawable.getOutset()); + mRect.inset(outset, outset); mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); - mDrawable.setBounds(mRect); + mCenterDrawable.setBounds(mRect); mDrawableSizeChanged = false; } - - mDrawable.draw(canvas); - } else if ((mCenterDrawable != null) && (mTopCornerDrawable != null)) { + mCenterDrawable.draw(canvas); + } else { + // Draw the top corner icon and "Setup" text is possible if (mDrawableSizeChanged) { DeviceProfile grid = getDeviceProfile(); int iconSize = grid.iconSizePx; diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java index f3977e456..a87986562 100644 --- a/src/com/android/launcher3/Stats.java +++ b/src/com/android/launcher3/Stats.java @@ -38,12 +38,10 @@ public class Stats { public static final String EXTRA_CELLX = "cellX"; public static final String EXTRA_CELLY = "cellY"; - private static final String LOG_FILE_NAME = "launches.log"; private static final int LOG_VERSION = 1; private static final int LOG_TAG_VERSION = 0x1; private static final int LOG_TAG_LAUNCH = 0x1000; - private static final String STATS_FILE_NAME = "stats.log"; private static final int STATS_VERSION = 1; private static final int INITIAL_STATS_SIZE = 100; @@ -69,7 +67,8 @@ public class Stats { if (LOCAL_LAUNCH_LOG) { try { - mLog = new DataOutputStream(mLauncher.openFileOutput(LOG_FILE_NAME, Context.MODE_APPEND)); + mLog = new DataOutputStream(mLauncher.openFileOutput( + LauncherFiles.LAUNCHES_LOG, Context.MODE_APPEND)); mLog.writeInt(LOG_TAG_VERSION); mLog.writeInt(LOG_VERSION); } catch (FileNotFoundException e) { @@ -160,7 +159,8 @@ public class Stats { private void saveStats() { DataOutputStream stats = null; try { - stats = new DataOutputStream(mLauncher.openFileOutput(STATS_FILE_NAME + ".tmp", Context.MODE_PRIVATE)); + stats = new DataOutputStream(mLauncher.openFileOutput( + LauncherFiles.STATS_LOG + ".tmp", Context.MODE_PRIVATE)); stats.writeInt(STATS_VERSION); final int N = mHistogram.size(); stats.writeInt(N); @@ -170,8 +170,8 @@ public class Stats { } stats.close(); stats = null; - mLauncher.getFileStreamPath(STATS_FILE_NAME + ".tmp") - .renameTo(mLauncher.getFileStreamPath(STATS_FILE_NAME)); + mLauncher.getFileStreamPath(LauncherFiles.STATS_LOG + ".tmp") + .renameTo(mLauncher.getFileStreamPath(LauncherFiles.STATS_LOG)); } catch (FileNotFoundException e) { Log.e(TAG, "unable to create stats data: " + e); } catch (IOException e) { @@ -190,7 +190,7 @@ public class Stats { mHistogram = new ArrayList<Integer>(INITIAL_STATS_SIZE); DataInputStream stats = null; try { - stats = new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME)); + stats = new DataInputStream(mLauncher.openFileInput(LauncherFiles.STATS_LOG)); final int version = stats.readInt(); if (version == STATS_VERSION) { final int N = stats.readInt(); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 92d7c7d7f..9cedae0f9 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -638,7 +638,7 @@ public class WidgetPreviewLoader { c.setBitmap(null); } // Render the icon - Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info)); + Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo)); int paddingTop = mContext. getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 5f074c1b4..79d31cdce 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -1267,7 +1267,6 @@ public class Workspace extends SmoothPagedView mCustomContentShowing = false; if (mCustomContentCallbacks != null) { mCustomContentCallbacks.onHide(); - mLauncher.resetQSBScroll(); mLauncher.updateVoiceButtonProxyVisible(false); } } |