Merging from ub-launcher3-qt-qpr1-dev @ build 5834630

am: 151970d535

Change-Id: I62da9e8c0c5eb1d7b9221c5a869ff2a39ce947c8
This commit is contained in:
Hyunyoung Song
2019-09-10 11:19:39 -07:00
committed by android-build-merger
24 changed files with 336 additions and 120 deletions
@@ -25,11 +25,13 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.view.ViewConfiguration;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.views.RecentsView;
@@ -162,6 +164,9 @@ public class OverviewCommandHelper {
@Override
public void run() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "RecentsActivityCommand.run");
}
long elapsedTime = mCreateTime - mLastToggleTime;
mLastToggleTime = mCreateTime;
@@ -16,11 +16,6 @@
package com.android.quickstep.logging;
import android.content.Context;
import android.content.Intent;
import android.stats.launcher.nano.LauncherExtension;
import android.stats.launcher.nano.LauncherTarget;
import static android.stats.launcher.nano.Launcher.ALLAPPS;
import static android.stats.launcher.nano.Launcher.HOME;
import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
@@ -29,6 +24,11 @@ import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
import static android.stats.launcher.nano.Launcher.BACKGROUND;
import static android.stats.launcher.nano.Launcher.OVERVIEW;
import android.content.Context;
import android.content.Intent;
import android.stats.launcher.nano.Launcher;
import android.stats.launcher.nano.LauncherExtension;
import android.stats.launcher.nano.LauncherTarget;
import android.view.View;
import com.android.launcher3.ItemInfo;
@@ -39,8 +39,6 @@ import com.android.launcher3.util.ComponentKey;
import com.android.systemui.shared.system.StatsLogCompat;
import com.google.protobuf.nano.MessageNano;
import androidx.annotation.Nullable;
/**
* This method calls the StatsLog hidden method until they are made available public.
*
@@ -85,6 +83,17 @@ public class StatsLogCompatManager extends StatsLogManager {
MessageNano.toByteArray(ext), true);
}
@Override
public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[1];
int srcState = mStateProvider.getCurrentState();
fillInLauncherExtensionWithPageId(ext, pageId);
int launcherAction = isSwipingToLeft ? Launcher.SWIPE_LEFT : Launcher.SWIPE_RIGHT;
StatsLogCompat.write(launcherAction, srcState, srcState,
MessageNano.toByteArray(ext), true);
}
public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
@@ -99,6 +108,13 @@ public class StatsLogCompatManager extends StatsLogManager {
return true;
}
public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
Target target = new Target();
target.pageIndex = pageId;
copy(target, ext.srcTarget[0]);
return true;
}
private static void copy(Target src, LauncherTarget dst) {
// fill in
}
@@ -34,9 +34,9 @@ import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Assert;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
@@ -79,6 +79,14 @@ public class NavigationModeSwitchRule implements TestRule {
description.getAnnotation(NavigationModeSwitch.class) != null) {
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
private void assertTrue(String message, boolean condition) {
if(!condition) {
final AssertionError assertionError = new AssertionError(message);
FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
throw assertionError;
}
}
@Override
public void evaluate() throws Throwable {
mLauncher.enableDebugTracing();
@@ -107,7 +115,8 @@ public class NavigationModeSwitchRule implements TestRule {
Log.e(TAG, "Exception", e);
throw e;
} finally {
Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode));
assertTrue("Couldn't set overlay",
setActiveOverlay(prevOverlayPkg, originalMode));
}
mLauncher.disableDebugTracing();
}
@@ -176,7 +185,7 @@ public class NavigationModeSwitchRule implements TestRule {
latch.await(10, TimeUnit.SECONDS);
targetContext.getMainExecutor().execute(() ->
sysUINavigationMode.removeModeChangeListener(listener));
Assert.assertTrue("Navigation mode didn't change to " + expectedMode,
assertTrue("Navigation mode didn't change to " + expectedMode,
currentSysUiNavigationMode() == expectedMode);
}
@@ -184,7 +193,7 @@ public class NavigationModeSwitchRule implements TestRule {
if (mLauncher.getNavigationModel() == expectedMode) break;
Thread.sleep(100);
}
Assert.assertTrue("Couldn't switch to " + overlayPackage,
assertTrue("Couldn't switch to " + overlayPackage,
mLauncher.getNavigationModel() == expectedMode);
for (int i = 0; i != 100; ++i) {
@@ -192,7 +201,7 @@ public class NavigationModeSwitchRule implements TestRule {
Thread.sleep(100);
}
final String error = mLauncher.getNavigationModeMismatchError();
Assert.assertTrue("Switching nav mode: " + error, error == null);
assertTrue("Switching nav mode: " + error, error == null);
Thread.sleep(5000);
return true;
@@ -97,6 +97,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
@PortraitLandscape
public void testOverview() throws Exception {
mLauncher.enableDebugTracing();
startTestApps();
Overview overview = mLauncher.pressHome().switchToOverview();
assertTrue("Launcher internal state didn't switch to Overview",
@@ -176,6 +177,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
executeOnLauncher(
launcher -> assertEquals("Still have tasks after dismissing all",
0, getTaskCount(launcher)));
mLauncher.disableDebugTracing();
}
private int getCurrentOverviewPage(Launcher launcher) {
@@ -29,7 +29,8 @@ public class PackageInstallStateChangedTaskTest extends BaseModelUpdateTaskTestC
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
int state = PackageInstallerCompat.STATUS_INSTALLING;
PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress,
android.os.Process.myUserHandle());
return new PackageInstallStateChangedTask(installInfo);
}
+1 -1
View File
@@ -89,7 +89,7 @@ public class AllAppsList {
public void addPromiseApp(Context context,
PackageInstallerCompat.PackageInstallInfo installInfo) {
ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
.getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
.getApplicationInfo(installInfo.packageName, 0, installInfo.user);
// only if not yet installed
if (applicationInfo == null) {
PromiseAppInfo info = new PromiseAppInfo(installInfo);
@@ -19,6 +19,7 @@ package com.android.launcher3;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -29,7 +30,6 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.Process;
@@ -141,7 +141,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
String pkg = getIntentPackage(info.launchIntent);
if (!TextUtils.isEmpty(pkg)
&& !launcherApps.isPackageEnabledForProfile(pkg, info.user)) {
&& !launcherApps.isPackageEnabledForProfile(pkg, info.user)
&& !info.isActivity) {
if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+ info.launchIntent);
continue;
@@ -250,7 +251,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
public static WorkspaceItemInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
return (WorkspaceItemInfo)
new PendingInstallShortcutInfo(info, context).getItemInfo().first;
}
public static void queueShortcut(ShortcutInfo info, Context context) {
@@ -261,8 +263,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
}
public static void queueActivityInfo(LauncherActivityInfo activity, Context context) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(activity, context), context);
public static void queueApplication(Intent data, UserHandle user, Context context) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user),
context);
}
public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
@@ -326,7 +329,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
private static class PendingInstallShortcutInfo {
final LauncherActivityInfo activityInfo;
final boolean isActivity;
final ShortcutInfo shortcutInfo;
final AppWidgetProviderInfo providerInfo;
@@ -340,7 +343,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
* Initializes a PendingInstallShortcutInfo received from a different app.
*/
public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
activityInfo = null;
isActivity = false;
shortcutInfo = null;
providerInfo = null;
@@ -350,14 +353,13 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
}
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
activityInfo = info;
isActivity = true;
shortcutInfo = null;
providerInfo = null;
@@ -369,11 +371,27 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
label = info.getLabel().toString();
}
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) {
isActivity = true;
shortcutInfo = null;
providerInfo = null;
this.data = data;
this.user = user;
mContext = context;
launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
}
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
activityInfo = null;
isActivity = false;
shortcutInfo = info;
providerInfo = null;
@@ -390,7 +408,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
*/
public PendingInstallShortcutInfo(
AppWidgetProviderInfo info, int widgetId, Context context) {
activityInfo = null;
isActivity = false;
shortcutInfo = null;
providerInfo = info;
@@ -405,17 +423,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
public String encodeToString() {
try {
if (activityInfo != null) {
// If it a launcher target, we only need component name, and user to
// recreate this.
return new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
.key(APP_SHORTCUT_TYPE_KEY).value(true)
.key(USER_HANDLE_KEY).value(UserManagerCompat.getInstance(mContext)
.getSerialNumberForUser(user))
.endObject().toString();
} else if (shortcutInfo != null) {
if (shortcutInfo != null) {
// If it a launcher target, we only need component name, and user to
// recreate this.
return new JSONStringer()
@@ -457,7 +465,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
JSONStringer json = new JSONStringer()
.object()
.key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
.key(NAME_KEY).value(name);
.key(NAME_KEY).value(name)
.key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
if (icon != null) {
byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
json = json.key(ICON_KEY).value(
@@ -477,29 +486,18 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
}
public Pair<ItemInfo, Object> getItemInfo() {
if (activityInfo != null) {
AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
final LauncherAppState app = LauncherAppState.getInstance(mContext);
// Set default values until proper values is loaded.
appInfo.title = "";
appInfo.applyFrom(app.getIconCache().getDefaultIcon(user));
final WorkspaceItemInfo si = appInfo.makeWorkspaceItem();
if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
} else {
app.getModel().updateAndBindWorkspaceItem(() -> {
app.getIconCache().getTitleAndIcon(
si, activityInfo, false /* useLowResIcon */);
return si;
});
}
return Pair.create((ItemInfo) si, (Object) activityInfo);
if (isActivity) {
WorkspaceItemInfo si = createWorkspaceItemInfo(data,
LauncherAppState.getInstance(mContext));
si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
return Pair.create(si, null);
} else if (shortcutInfo != null) {
WorkspaceItemInfo si = new WorkspaceItemInfo(shortcutInfo, mContext);
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
LauncherIcons li = LauncherIcons.obtain(mContext);
si.applyFrom(li.createShortcutIcon(shortcutInfo));
itemInfo.applyFrom(li.createShortcutIcon(shortcutInfo));
li.recycle();
return Pair.create((ItemInfo) si, (Object) shortcutInfo);
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
.fromProviderInfo(mContext, providerInfo);
@@ -511,15 +509,16 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
widgetInfo.minSpanY = info.minSpanY;
widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
return Pair.create(widgetInfo, providerInfo);
} else {
WorkspaceItemInfo si = createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
return Pair.create((ItemInfo) si, null);
WorkspaceItemInfo itemInfo =
createWorkspaceItemInfo(data, LauncherAppState.getInstance(mContext));
return Pair.create(itemInfo, null);
}
}
public boolean isLauncherActivity() {
return activityInfo != null;
return isActivity;
}
}
@@ -534,7 +533,9 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
.resolveActivity(decoder.launcherIntent, decoder.user);
return info == null ? null : new PendingInstallShortcutInfo(info, context);
if (info != null) {
return new PendingInstallShortcutInfo(info, context);
}
} else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
List<ShortcutInfo> si = sm.queryForFullDetails(
@@ -578,7 +579,11 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
}
return new PendingInstallShortcutInfo(data, decoder.user, context);
if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
return new PendingInstallShortcutInfo(data, context, decoder.user);
} else {
return new PendingInstallShortcutInfo(data, decoder.user, context);
}
} catch (JSONException | URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
}
@@ -227,6 +227,10 @@ public class LauncherStateManager {
private void goToState(LauncherState state, boolean animated, long delay,
final Runnable onCompleteRunnable) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "goToState: " + state + " @ " +
Log.getStackTraceString(new Throwable()));
}
animated &= Utilities.areAnimationsEnabled(mLauncher);
if (mLauncher.isInState(state)) {
if (mConfig.mCurrentAnimation == null) {
@@ -426,6 +430,9 @@ public class LauncherStateManager {
if (state != mCurrentStableState) {
mLastStableState = state.getHistoryForState(mCurrentStableState);
mCurrentStableState = state;
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "onStateTransitionEnd: " + state);
}
}
state.onStateTransitionEnd(mLauncher);
@@ -18,28 +18,33 @@ package com.android.launcher3;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import java.util.List;
import static com.android.launcher3.compat.PackageInstallerCompat.getUserHandle;
/**
* BroadcastReceiver to handle session commit intent.
*/
@@ -66,15 +71,29 @@ public class SessionCommitReceiver extends BroadcastReceiver {
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
PackageInstallerCompat packageInstallerCompat = PackageInstallerCompat.getInstance(context);
if (TextUtils.isEmpty(info.getAppPackageName()) ||
info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
if (TextUtils.isEmpty(info.getAppPackageName())
|| info.getInstallReason() != PackageManager.INSTALL_REASON_USER
|| packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
packageInstallerCompat.removePromiseIconId(info.getSessionId());
return;
}
queueAppIconAddition(context, info.getAppPackageName(), user);
}
public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
String packageName = sessionInfo.getAppPackageName();
List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
.getActivityList(packageName, getUserHandle(sessionInfo));
if (activities == null || activities.isEmpty()) {
// Ensure application isn't already installed.
queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
}
}
public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
.getActivityList(packageName, user);
@@ -82,7 +101,18 @@ public class SessionCommitReceiver extends BroadcastReceiver {
// no activity found
return;
}
InstallShortcutReceiver.queueActivityInfo(activities.get(0), context);
queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user);
}
private static void queueAppIconAddition(Context context, String packageName,
CharSequence label, Bitmap icon, UserHandle user) {
Intent data = new Intent();
data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
new ComponentName(packageName, "")).setPackage(packageName));
data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
InstallShortcutReceiver.queueApplication(data, user, context);
}
public static boolean isEnabled(Context context) {
+2
View File
@@ -1052,6 +1052,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
if (!mOverlayShown) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
mLauncher.getStatsLogManager().logSwipeOnContainer(true, 0);
}
mOverlayShown = true;
// Not announcing the overlay page for accessibility since it announces itself.
@@ -1061,6 +1062,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
if (!ued.isPreviousHomeGesture()) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
mLauncher.getStatsLogManager().logSwipeOnContainer(false, -1);
}
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
@@ -50,24 +50,26 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
* The icon was added as an auto-install app, and is not ready to be used. This flag can't
* be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
* parsing.
*
* OR this icon was added due to it being an active install session created by the user.
*/
public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
/**
* The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
* is set, then the icon is either being installed or is in a broken state.
*/
public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
/**
* Indicates that the widget restore has started.
*/
public static final int FLAG_RESTORE_STARTED = 8; //0B1000;
public static final int FLAG_RESTORE_STARTED = 1 << 3;
/**
* Web UI supported.
*/
public static final int FLAG_SUPPORTS_WEB_UI = 16; //0B10000;
public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
/**
* The intent used to start the application.
@@ -210,7 +212,7 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon {
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI))) {
|| hasStatusFlag(FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON))) {
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
// a packageName. In that case create a dummy componentName instead of adding additional
// check everywhere.
@@ -628,20 +628,4 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
return super.performAccessibilityAction(action, arguments);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean result = super.dispatchTouchEvent(ev);
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (result) mAllAppsStore.enableDeferUpdates(
AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mAllAppsStore.disableDeferUpdates(AllAppsStore.DEFER_UPDATES_USER_INTERACTION);
break;
}
return result;
}
}
@@ -39,10 +39,8 @@ public class AllAppsStore {
// Defer updates flag used to defer all apps updates to the next draw.
public static final int DEFER_UPDATES_NEXT_DRAW = 1 << 0;
// Defer updates flag used to defer all apps updates while the user interacts with all apps.
public static final int DEFER_UPDATES_USER_INTERACTION = 1 << 1;
// Defer updates flag used to defer all apps updates by a test's request.
public static final int DEFER_UPDATES_TEST = 1 << 2;
public static final int DEFER_UPDATES_TEST = 1 << 1;
private PackageUserKey mTempKey = new PackageUserKey(null, null);
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
@@ -31,11 +31,14 @@ import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import androidx.annotation.NonNull;
@@ -167,6 +170,10 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
@Override
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.APP_NOT_DISABLED, "onPackagesSuspended: " +
Arrays.toString(packageNames));
}
mCallback.onPackagesSuspended(packageNames, user);
}
@@ -19,15 +19,23 @@ package com.android.launcher3.compat;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInstaller;
import android.os.Process;
import android.os.UserHandle;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import androidx.annotation.NonNull;
import com.android.launcher3.Utilities;
public abstract class PackageInstallerCompat {
// Set<String> of session ids of promise icons that have been added to the home screen
// as FLAG_PROMISE_NEW_INSTALLS.
protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
public static final int STATUS_INSTALLED = 0;
public static final int STATUS_INSTALLING = 1;
public static final int STATUS_FAILED = 2;
@@ -44,6 +52,10 @@ public abstract class PackageInstallerCompat {
}
}
public static UserHandle getUserHandle(PackageInstaller.SessionInfo info) {
return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
}
/**
* @return a map of active installs to their progress
*/
@@ -61,30 +73,44 @@ public abstract class PackageInstallerCompat {
public final String packageName;
public final int state;
public final int progress;
public final UserHandle user;
private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
this.state = STATUS_INSTALLING;
this.packageName = info.getAppPackageName();
this.componentName = new ComponentName(packageName, "");
this.progress = (int) (info.getProgress() * 100f);
this.user = getUserHandle(info);
}
public PackageInstallInfo(String packageName, int state, int progress) {
public PackageInstallInfo(String packageName, int state, int progress, UserHandle user) {
this.state = state;
this.packageName = packageName;
this.componentName = new ComponentName(packageName, "");
this.progress = progress;
this.user = user;
}
public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
return new PackageInstallInfo(info);
}
public static PackageInstallInfo fromState(int state, String packageName) {
return new PackageInstallInfo(packageName, state, 0 /* progress */);
public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
}
}
public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
/**
* Returns true if a promise icon was already added to the home screen for {@param sessionId}.
* Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
*/
public abstract boolean promiseIconAddedForId(int sessionId);
/**
* Applicable only for icons with flag FLAG_PROMISE_NEW_INSTALLS.
*/
public abstract void removePromiseIconId(int sessionId);
}
@@ -21,17 +21,21 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionCallback;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.SparseArray;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -39,11 +43,13 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import static com.android.launcher3.Utilities.getPrefs;
public class PackageInstallerCompatVL extends PackageInstallerCompat {
private static final boolean DEBUG = false;
@Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
@Thunk final SparseArray<PackageUserKey> mActiveSessions = new SparseArray<>();
@Thunk final PackageInstaller mInstaller;
private final IconCache mCache;
@@ -51,6 +57,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
private final Context mAppContext;
private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
private final LauncherAppsCompat mLauncherApps;
private final IntSet mPromiseIconIds;
PackageInstallerCompatVL(Context context) {
mAppContext = context.getApplicationContext();
@@ -59,17 +66,38 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
mWorker = new Handler(LauncherModel.getWorkerLooper());
mInstaller.registerSessionCallback(mCallback, mWorker);
mLauncherApps = LauncherAppsCompat.getInstance(context);
mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString(
getPrefs(context).getString(PROMISE_ICON_IDS, ""))));
cleanUpPromiseIconIds();
}
private void cleanUpPromiseIconIds() {
IntArray existingIds = new IntArray();
for (SessionInfo info : updateAndGetActiveSessionCache().values()) {
existingIds.add(info.getSessionId());
}
IntArray idsToRemove = new IntArray();
for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
idsToRemove.add(mPromiseIconIds.getArray().get(i));
}
}
for (int i = idsToRemove.size() - 1; i >= 0; --i) {
mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
}
}
@Override
public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
HashMap<String, SessionInfo> activePackages = new HashMap<>();
UserHandle primaryUser = Process.myUserHandle();
for (SessionInfo info : getAllVerifiedSessions()) {
addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser);
addSessionInfoToCache(info, getUserHandle(info));
if (info.getAppPackageName() != null) {
activePackages.put(info.getAppPackageName(), info);
mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
mActiveSessions.put(info.getSessionId(),
new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
}
}
return activePackages;
@@ -78,7 +106,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
for (SessionInfo info : getAllVerifiedSessions()) {
boolean match = pkg.equals(info.getAppPackageName());
if (Utilities.ATLEAST_Q && !user.equals(info.getUser())) {
if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
match = false;
}
if (match) {
@@ -120,19 +148,38 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
PackageInstallInfo.fromInstallingState(sessionInfo));
}
}
if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
&& SessionCommitReceiver.isEnabled(mAppContext)
&& sessionInfo != null
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER) {
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
if (!mPromiseIconIds.contains(sessionInfo.getSessionId())) {
mPromiseIconIds.add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
}
}
}
@Override
public void onFinished(int sessionId, boolean success) {
// For a finished session, we can't get the session info. So use the
// packageName from our local cache.
String packageName = mActiveSessions.get(sessionId);
PackageUserKey key = mActiveSessions.get(sessionId);
mActiveSessions.remove(sessionId);
if (packageName != null) {
sendUpdate(PackageInstallInfo.fromState(
success ? STATUS_INSTALLED : STATUS_FAILED,
packageName));
if (key != null && key.mPackageName != null) {
String packageName = key.mPackageName;
sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
packageName, key.mUser));
if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
if (appState != null) {
LauncherModel model = appState.getModel();
model.onPackageRemoved(packageName, key.mUser);
}
}
}
}
@@ -155,8 +202,9 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
if (session != null && session.getAppPackageName() != null) {
mActiveSessions.put(sessionId, session.getAppPackageName());
addSessionInfoToCache(session, Process.myUserHandle());
mActiveSessions.put(session.getSessionId(),
new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
addSessionInfoToCache(session, getUserHandle(session));
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
if (app != null) {
app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
@@ -178,7 +226,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
if (!mSessionVerifiedMap.containsKey(pkg)) {
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
}
}
@@ -198,4 +246,23 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat {
}
return list;
}
@Override
public boolean promiseIconAddedForId(int sessionId) {
return mPromiseIconIds.contains(sessionId);
}
@Override
public void removePromiseIconId(int sessionId) {
if (mPromiseIconIds.contains(sessionId)) {
mPromiseIconIds.getArray().removeValue(sessionId);
updatePromiseIconPrefs();
}
}
private void updatePromiseIconPrefs() {
getPrefs(mAppContext).edit()
.putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
.apply();
}
}
@@ -60,6 +60,11 @@ public abstract class BaseFlags {
// When enabled the promise icon is visible in all apps while installation an app.
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
// When enabled a promise icon is added to the home screen when install session is active.
public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS =
new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true,
"Adds a promise icon to the home screen for new install sessions.");
// Enable moving the QSB on the 0th screen of the workspace
public static final boolean QSB_ON_FIRST_SCREEN = true;
@@ -41,5 +41,6 @@ public class StatsLogManager implements ResourceBasedOverride {
public void logAppLaunch(View v, Intent intent) { }
public void logTaskLaunch(View v, ComponentKey key) { }
public void logTaskDismiss(View v, ComponentKey key) { }
public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) { }
public void verify() {} // TODO: should move into robo tests
}
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.UserHandle;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -32,6 +34,8 @@ import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
@@ -85,6 +89,10 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
}
}
PackageInstallerCompat packageInstaller =
PackageInstallerCompat.getInstance(app.getContext());
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(app.getContext());
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
int[] coords = findSpaceForItem(app, dataModel, workspaceScreens,
@@ -101,6 +109,36 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
throw new RuntimeException("Unexpected info type");
}
if (item instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) item).isPromise()) {
WorkspaceItemInfo workspaceInfo = (WorkspaceItemInfo) item;
String packageName = item.getTargetComponent() != null
? item.getTargetComponent().getPackageName() : null;
if (packageName == null) {
continue;
}
SessionInfo sessionInfo = packageInstaller.getActiveSessionInfo(item.user,
packageName);
if (sessionInfo == null) {
List<LauncherActivityInfo> activities = launcherApps
.getActivityList(packageName, item.user);
if (activities != null && !activities.isEmpty()) {
// App was installed while launcher was in the background.
itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
.makeWorkspaceItem();
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.applyFrom(app.getIconCache().getDefaultIcon(item.user));
app.getIconCache().getTitleAndIcon(wii,
((WorkspaceItemInfo) itemInfo).usingLowResIcon());
} else {
// Session was cancelled, do not add.
continue;
}
} else {
workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
}
}
// Add the shortcut to the db
getModelWriter().addItemToDatabase(itemInfo,
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
@@ -227,7 +227,7 @@ public class LoaderCursor extends CursorWrapper {
if (!TextUtils.isEmpty(title)) {
info.title = Utilities.trim(title);
}
} else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
} else if (hasRestoreFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)) {
if (TextUtils.isEmpty(info.title)) {
info.title = getTitle();
}
@@ -18,7 +18,6 @@ package com.android.launcher3.model;
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Process;
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
@@ -56,7 +55,7 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
ApplicationInfo ai = app.getContext()
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
}
} catch (PackageManager.NameNotFoundException e) {
// Ignore
@@ -43,6 +43,7 @@ import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -55,6 +56,8 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
/**
* Handles updates due to changes in package manager (app installed/updated/removed)
* or when a user availability changes.
@@ -85,6 +88,10 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.APP_NOT_DISABLED, "PackageUpdatedTask: " + mOp + ", " +
Arrays.toString(mPackages));
}
final Context context = app.getContext();
final IconCache iconCache = app.getIconCache();
@@ -99,7 +106,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
appsList.removePackage(packages[i], Process.myUserHandle());
appsList.removePackage(packages[i], mUser);
}
appsList.addPackage(context, packages[i], mUser);
@@ -227,8 +234,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask {
isTargetValid = LauncherAppsCompat.getInstance(context)
.isActivityEnabledForProfile(cn, mUser);
}
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON)
&& !isTargetValid) {
if (si.hasStatusFlag(FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
@@ -81,4 +81,6 @@ public final class TestProtocol {
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
public static final String APP_NOT_DISABLED = "b/139891609";
public static final String ALL_APPS_UPON_RECENTS = "b/139941530";
}
@@ -21,10 +21,10 @@ public class FailureWatcher extends TestWatcher {
mDevice = device;
}
private void dumpViewHierarchy() {
private static void dumpViewHierarchy(UiDevice device) {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
mDevice.dumpWindowHierarchy(stream);
device.dumpWindowHierarchy(stream);
stream.flush();
stream.close();
for (String line : stream.toString().split("\\r?\\n")) {
@@ -37,7 +37,11 @@ public class FailureWatcher extends TestWatcher {
@Override
protected void failed(Throwable e, Description description) {
if (mDevice == null) return;
onError(mDevice, description, e);
}
public static void onError(UiDevice device, Description description, Throwable e) {
if (device == null) return;
final String pathname = getInstrumentation().getTargetContext().
getFilesDir().getPath() + "/TestScreenshot-" + description.getMethodName()
+ ".png";
@@ -45,15 +49,15 @@ public class FailureWatcher extends TestWatcher {
", screenshot will be saved to " + pathname +
", track trace is below, UI object dump is further below:\n" +
Log.getStackTraceString(e));
dumpViewHierarchy();
dumpViewHierarchy(device);
try {
final String dumpsysResult = mDevice.executeShellCommand(
final String dumpsysResult = device.executeShellCommand(
"dumpsys activity service TouchInteractionService");
Log.d(TAG, "TouchInteractionService: " + dumpsysResult);
} catch (IOException ex) {
}
mDevice.takeScreenshot(new File(pathname));
device.takeScreenshot(new File(pathname));
}
}