Merge "Add an exported flag in manifest" into ub-launcher3-master
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index b84c7aa..0b7d4bf 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -18,6 +18,8 @@
import static android.graphics.Bitmap.Config.ARGB_8888;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
@@ -28,6 +30,11 @@
import androidx.annotation.Keep;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -37,6 +44,7 @@
*/
public class DebugTestInformationHandler extends TestInformationHandler {
private static LinkedList sLeaks;
+ private static Collection<String> sEvents;
public DebugTestInformationHandler(Context context) {
init(context);
@@ -134,6 +142,42 @@
return response;
}
+ case TestProtocol.REQUEST_START_EVENT_LOGGING: {
+ sEvents = new ArrayList<>();
+ TestLogging.setEventConsumer(
+ (sequence, event) -> {
+ final Collection<String> events = sEvents;
+ if (events != null) {
+ synchronized (events) {
+ events.add(sequence + '/' + event);
+ }
+ }
+ });
+ return response;
+ }
+
+ case TestProtocol.REQUEST_STOP_EVENT_LOGGING: {
+ TestLogging.setEventConsumer(null);
+ sEvents = null;
+ return response;
+ }
+
+ case TestProtocol.REQUEST_GET_TEST_EVENTS: {
+ synchronized (sEvents) {
+ response.putStringArrayList(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
+ }
+ return response;
+ }
+
+ case TestProtocol.REQUEST_CLEAR_DATA: {
+ LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ MAIN_EXECUTOR.submit(() ->
+ LauncherAppState.getInstance(mContext).getModel().forceReload());
+ return response;
+ }
+
default:
return super.call(method);
}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index d1185bd..5611969 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -71,6 +71,15 @@
// Represents the apps container within search results.
message SearchResultContainer {
+
+ // Length of search term.
+ optional int32 query_length = 1;
+
+ // Container from where search was invoked.
+ oneof ParentContainer {
+ WorkspaceContainer workspace = 2;
+ AllAppsContainer all_apps_container = 3;
+ }
}
// Container for package specific shortcuts to deep links and notifications.
@@ -96,8 +105,21 @@
ADD_TO_HOMESCREEN = 6; // play install + launcher home setting
ALLAPPS_PREDICTION = 7; // from prediction bar in all apps container
HOTSEAT_PREDICTION = 8; // from prediction bar in hotseat container
- SUGGESTED_LABEL = 9; // folder icon's label was suggested
- MANUAL_LABEL = 10; // folder icon's label was manually edited
+
+ // Folder's label is one of the non-empty suggested values.
+ SUGGESTED_LABEL = 9;
+
+ // Folder's label is non-empty, manually entered by the user
+ // and different from any of suggested values.
+ MANUAL_LABEL = 10;
+
+ // Folder's label is not yet assigned( i.e., title == null).
+ // Eligible for auto-labeling.
+ UNLABELED = 11;
+
+ // Folder's label is empty(i.e., title == "").
+ // Not eligible for auto-labeling.
+ EMPTY_LABEL = 12;
}
// Main app icons
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index c1bf2fd..a1218ae 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -114,9 +114,9 @@
if (!putIntoFolder.isEmpty()) {
ItemInfo firstItem = putIntoFolder.get(0);
FolderInfo folderInfo = new FolderInfo();
- folderInfo.setTitle("");
mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
firstItem.screenId, firstItem.cellX, firstItem.cellY);
+ folderInfo.setTitle("", mLauncher.getModelWriter());
folderInfo.contents.addAll(putIntoFolder);
for (int i = 0; i < folderInfo.contents.size(); i++) {
ItemInfo item = folderInfo.contents.get(i);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index a35e13a..c1a585e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -44,6 +45,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -246,5 +248,9 @@
startContainerType,
mEndState.containerType,
mLauncher.getWorkspace().getCurrentPage());
+ mLauncher.getStatsLogManager().logger()
+ .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
+ .withDstState(StatsLogManager.containerTypeToAtomState(mEndState.containerType))
+ .log(LAUNCHER_HOME_GESTURE);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index da304e5..1b439d1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -28,6 +28,10 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
+import static com.android.launcher3.logging.StatsLogManager.getLauncherAtomEvent;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
@@ -61,6 +65,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
@@ -429,6 +434,13 @@
mStartState.containerType,
targetState.containerType,
mLauncher.getWorkspace().getCurrentPage());
+ mLauncher.getStatsLogManager().logger()
+ .withSrcState(LAUNCHER_STATE_HOME)
+ .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
+ .log(getLauncherAtomEvent(mStartState.containerType, targetState.containerType,
+ targetState.ordinal > mStartState.ordinal
+ ? LAUNCHER_UNKNOWN_SWIPEUP
+ : LAUNCHER_UNKNOWN_SWIPEDOWN));
mLauncher.getStateManager().goToState(targetState, false, this::clearState);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
index e825c5f..413a813 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
@@ -22,6 +22,12 @@
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -62,6 +68,7 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -275,8 +282,7 @@
if (mActivity == activity) {
return true;
}
- mTaskViewSimulator.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
- mDeviceState.getDisplayRotation());
+
if (mActivity != null) {
// The launcher may have been recreated as a result of device rotation.
int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
@@ -329,6 +335,7 @@
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return;
}
+ mTaskViewSimulator.setRecentsConfiguration(mActivity.getResources().getConfiguration());
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
@@ -891,6 +898,27 @@
ContainerType.NAVBAR, ContainerType.APP,
endTarget.containerType,
pageIndex);
+ StatsLogManager.EventEnum event;
+ switch (endTarget) {
+ case HOME:
+ event = LAUNCHER_HOME_GESTURE;
+ break;
+ case RECENTS:
+ event = LAUNCHER_OVERVIEW_GESTURE;
+ break;
+ case LAST_TASK:
+ case NEW_TASK:
+ event = (mLogDirection == Direction.LEFT)
+ ? LAUNCHER_QUICKSWITCH_LEFT
+ : LAUNCHER_QUICKSWITCH_RIGHT;
+ break;
+ default:
+ event = IGNORE;
+ }
+ StatsLogManager.newInstance(mContext).logger()
+ .withSrcState(LAUNCHER_STATE_BACKGROUND)
+ .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType))
+ .log(event);
}
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index ed07062..01936e4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -66,7 +66,7 @@
}
}
RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
- boolean canLauncherRotate = orientedState.canLauncherRotate();
+ boolean canLauncherRotate = orientedState.canRecentsActivityRotate();
boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
// Add overview actions to the menu when in in-place rotate landscape mode.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 37314ea..6e0b517 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -471,7 +471,6 @@
Log.d(TestProtocol.NO_SWIPE_TO_HOME, "TouchInteractionService.onInputEvent:DOWN");
}
mDeviceState.setOrientationTransformIfNeeded(event);
- GestureState newGestureState;
if (mDeviceState.isInSwipeUpTouchRegion(event)) {
if (TestProtocol.sDebugTracing) {
@@ -481,35 +480,31 @@
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
// onConsumerInactive and wipe the previous gesture state
GestureState prevGestureState = new GestureState(mGestureState);
- newGestureState = createGestureState();
+ GestureState newGestureState = createGestureState(mGestureState);
mConsumer.onConsumerAboutToBeSwitched();
- mConsumer = newConsumer(prevGestureState, newGestureState, event);
+ mGestureState = newGestureState;
+ mConsumer = newConsumer(prevGestureState, mGestureState, event);
ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
mUncheckedConsumer = mConsumer;
} else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()) {
- newGestureState = createGestureState();
- ActivityManager.RunningTaskInfo runningTask = newGestureState.getRunningTask();
+ mGestureState = createGestureState(mGestureState);
+ ActivityManager.RunningTaskInfo runningTask = mGestureState.getRunningTask();
if (mDeviceState.canTriggerAssistantAction(event, runningTask)) {
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
// should not interrupt it. QuickSwitch assumes that interruption can only
// happen if the next gesture is also quick switch.
mUncheckedConsumer = new AssistantInputConsumer(
this,
- newGestureState,
+ mGestureState,
InputConsumer.NO_OP, mInputMonitorCompat,
mOverviewComponentObserver.assistantGestureIsConstrained());
} else {
- newGestureState = DEFAULT_STATE;
mUncheckedConsumer = InputConsumer.NO_OP;
}
} else {
- newGestureState = DEFAULT_STATE;
mUncheckedConsumer = InputConsumer.NO_OP;
}
-
- // Save the current gesture state
- mGestureState = newGestureState;
} else {
// Other events
if (mUncheckedConsumer != InputConsumer.NO_OP) {
@@ -519,7 +514,17 @@
}
if (mUncheckedConsumer != InputConsumer.NO_OP) {
- ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
+ switch (event.getActionMasked()) {
+ case ACTION_DOWN:
+ case ACTION_UP:
+ ActiveGestureLog.INSTANCE.addLog("onMotionEvent("
+ + (int) event.getRawX() + ", " + (int) event.getRawY() + ")",
+ event.getActionMasked());
+ break;
+ default:
+ ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
+ break;
+ }
}
boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
@@ -533,14 +538,14 @@
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
}
- private GestureState createGestureState() {
+ private GestureState createGestureState(GestureState previousGestureState) {
GestureState gestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
- gestureState.updateRunningTask(mGestureState.getRunningTask());
- gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
+ gestureState.updateRunningTask(previousGestureState.getRunningTask());
+ gestureState.updateLastStartedTaskId(previousGestureState.getLastStartedTaskId());
gestureState.updatePreviouslyAppearedTaskIds(
- mGestureState.getPreviouslyAppearedTaskIds());
+ previousGestureState.getPreviouslyAppearedTaskIds());
} else {
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
@@ -743,7 +748,7 @@
private void reset() {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
- mGestureState = new GestureState();
+ mGestureState = DEFAULT_STATE;
}
private void preloadOverview(boolean fromInit) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index a9f138e..4e967cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -23,6 +23,8 @@
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.launcher3.PagedView.ACTION_MOVE_ALLOW_EASY_FLING;
+import static com.android.launcher3.PagedView.DEBUG_FAILED_QUICKSWITCH;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
@@ -38,6 +40,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
@@ -76,7 +79,8 @@
private static final String UP_EVT = "OtherActivityInputConsumer.UP";
// TODO: Move to quickstep contract
- public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
+ public static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 9;
+ public static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 2;
private final RecentsAnimationDeviceState mDeviceState;
private final NavBarPosition mNavBarPosition;
@@ -150,10 +154,12 @@
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
- mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
- float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
- mSquaredTouchSlop = slop * slop;
+ float slopMultiplier = mDeviceState.isFullyGesturalNavMode()
+ ? QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL
+ : QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
+ mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+ mSquaredTouchSlop = slopMultiplier * mTouchSlop * mTouchSlop;
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
@@ -187,6 +193,10 @@
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler
.getRecentsViewDispatcher(mNavBarPosition.getRotation()));
+ int action = ev.getAction();
+ ev.setAction(ACTION_MOVE_ALLOW_EASY_FLING);
+ mRecentsViewDispatcher.dispatchEvent(ev);
+ ev.setAction(action);
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -317,6 +327,13 @@
}
case ACTION_CANCEL:
case ACTION_UP: {
+ if (DEBUG_FAILED_QUICKSWITCH && !mPassedWindowMoveSlop) {
+ float displacementX = mLastPos.x - mDownPos.x;
+ float displacementY = mLastPos.y - mDownPos.y;
+ Log.d("Quickswitch", "mPassedWindowMoveSlop=false"
+ + " disp=" + squaredHypot(displacementX, displacementY)
+ + " slop=" + mSquaredTouchSlop);
+ }
finishTouchTracking(ev);
break;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 4440a04..d972c0f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,6 +15,9 @@
*/
package com.android.quickstep.inputconsumers;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
@@ -22,9 +25,10 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.GestureState;
@@ -40,11 +44,13 @@
private final Context mContext;
private final InputMonitorCompat mInputMonitor;
private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker;
+ private final GestureState mGestureState;
public OverviewWithoutFocusInputConsumer(Context context,
RecentsAnimationDeviceState deviceState, GestureState gestureState,
InputMonitorCompat inputMonitor, boolean disableHorizontalSwipe) {
mContext = context;
+ mGestureState = gestureState;
mInputMonitor = inputMonitor;
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe,
deviceState.getNavBarPosition(), this::onInterceptTouch, this);
@@ -81,10 +87,21 @@
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int pageIndex = -1; // This number doesn't reflect workspace page index.
// It only indicates that launcher client screen was shown.
- int containerType = StatsLogUtils.getContainerTypeFromState(activity.getCurrentState());
+ int containerType = (mGestureState != null && mGestureState.getEndTarget() != null)
+ ? mGestureState.getEndTarget().containerType
+ : LauncherLogProto.ContainerType.WORKSPACE;
activity.getUserEventDispatcher().logActionOnContainer(
wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex);
activity.getUserEventDispatcher().setPreviousHomeGesture(true);
+ activity.getStatsLogManager().logger()
+ .withSrcState(LAUNCHER_STATE_HOME)
+ .withDstState(LAUNCHER_STATE_HOME)
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(-1))
+ .build())
+ .log(LAUNCHER_HOME_GESTURE);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 32da52e..46013d3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -22,6 +22,7 @@
import android.animation.TimeInterpolator;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
@@ -125,6 +126,14 @@
}
/**
+ * @see com.android.quickstep.views.RecentsView#onConfigurationChanged(Configuration)
+ */
+ public void setRecentsConfiguration(Configuration configuration) {
+ mOrientationState.setActivityConfiguration(configuration);
+ mLayoutValid = false;
+ }
+
+ /**
* @see com.android.quickstep.views.RecentsView#FULLSCREEN_PROGRESS
*/
public float getFullScreenScale() {
@@ -205,7 +214,8 @@
public void applyWindowToHomeRotation(Matrix matrix) {
mMatrix.postTranslate(mDp.windowX, mDp.windowY);
postDisplayRotation(deltaRotation(
- mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()),
+ mOrientationState.getRecentsActivityRotation(),
+ mOrientationState.getDisplayRotation()),
mDp.widthPx, mDp.heightPx, matrix);
matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y);
}
@@ -226,7 +236,7 @@
mPositionHelper.updateThumbnailMatrix(
mThumbnailPosition, mThumbnailData,
mTaskRect.width(), mTaskRect.height(),
- mDp, mOrientationState.getLauncherRotation());
+ mDp, mOrientationState.getRecentsActivityRotation());
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
PagedOrientationHandler poh = mOrientationState.getOrientationHandler();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index a19026b..846b944 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -31,6 +31,7 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
+import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.Surface;
@@ -42,6 +43,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -52,6 +54,7 @@
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
+import com.android.systemui.shared.recents.model.Task;
/**
* {@link RecentsView} used in Launcher activity
@@ -167,14 +170,21 @@
}
@Override
- protected void onTaskLaunched(boolean success) {
+ protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
mActivity.getStateManager().goToState(NORMAL, false /* animate */);
} else {
LauncherState state = mActivity.getStateManager().getState();
mActivity.getAllAppsController().setState(state);
}
- super.onTaskLaunched(success);
+ super.onTaskLaunchAnimationEnd(success);
+ }
+
+ @Override
+ public void onTaskLaunched(Task task) {
+ UserHandle user = UserHandle.of(task.key.userId);
+ AppLaunchTracker.INSTANCE.get(getContext()).onStartApp(task.getTopComponent(), user,
+ AppLaunchTracker.CONTAINER_OVERVIEW);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index cb22570..68b8975 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -59,6 +59,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
@@ -393,6 +394,7 @@
mActivity = BaseActivity.fromContext(context);
mOrientationState = new RecentsOrientedState(
context, mSizeStrategy, this::animateRecentsRotationInPlace);
+ mOrientationState.setActivityConfiguration(context.getResources().getConfiguration());
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
@@ -1063,7 +1065,7 @@
}
private void animateRecentsRotationInPlace(int newRotation) {
- if (mOrientationState.canLauncherRotate()) {
+ if (mOrientationState.canRecentsActivityRotate()) {
// Let system take care of the rotation
return;
}
@@ -1646,29 +1648,41 @@
}
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mOrientationState.setActivityConfiguration(newConfig)) {
+ updateOrientationHandler();
+ }
+ }
+
public void setLayoutRotation(int touchRotation, int displayRotation) {
if (mOrientationState.update(touchRotation, displayRotation)) {
- mOrientationHandler = mOrientationState.getOrientationHandler();
- mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
- setLayoutDirection(mIsRtl
- ? View.LAYOUT_DIRECTION_RTL
- : View.LAYOUT_DIRECTION_LTR);
- mClearAllButton.setLayoutDirection(mIsRtl
- ? View.LAYOUT_DIRECTION_LTR
- : View.LAYOUT_DIRECTION_RTL);
- mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
- mActivity.getDragLayer().recreateControllers();
- boolean isInLandscape = mOrientationState.getTouchRotation() != 0
- || mOrientationState.getLauncherRotation() != ROTATION_0;
- mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
- !mOrientationState.canLauncherRotate() && isInLandscape);
- resetPaddingFromTaskSize();
- requestLayout();
- // Reapply the current page to update page scrolls.
- setCurrentPage(mCurrentPage);
+ updateOrientationHandler();
}
}
+ private void updateOrientationHandler() {
+ mOrientationHandler = mOrientationState.getOrientationHandler();
+ mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
+ setLayoutDirection(mIsRtl
+ ? View.LAYOUT_DIRECTION_RTL
+ : View.LAYOUT_DIRECTION_LTR);
+ mClearAllButton.setLayoutDirection(mIsRtl
+ ? View.LAYOUT_DIRECTION_LTR
+ : View.LAYOUT_DIRECTION_RTL);
+ mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
+ mActivity.getDragLayer().recreateControllers();
+ boolean isInLandscape = mOrientationState.getTouchRotation() != 0
+ || mOrientationState.getRecentsActivityRotation() != ROTATION_0;
+ mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION,
+ !mOrientationState.canRecentsActivityRotate() && isInLandscape);
+ resetPaddingFromTaskSize();
+ requestLayout();
+ // Reapply the current page to update page scrolls.
+ setCurrentPage(mCurrentPage);
+ }
+
public RecentsOrientedState getPagedViewOrientedState() {
return mOrientationState;
}
@@ -1952,7 +1966,7 @@
mPendingAnimation.addEndListener((endState) -> {
if (endState.isSuccess) {
Consumer<Boolean> onLaunchResult = (result) -> {
- onTaskLaunched(result);
+ onTaskLaunchAnimationEnd(result);
if (!result) {
tv.notifyTaskLaunchFailed(TAG);
}
@@ -1967,7 +1981,7 @@
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
}
} else {
- onTaskLaunched(false);
+ onTaskLaunchAnimationEnd(false);
}
mPendingAnimation = null;
});
@@ -1979,12 +1993,17 @@
public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
- protected void onTaskLaunched(boolean success) {
+ protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
}
}
+ /**
+ * Called when task activity is launched
+ */
+ public void onTaskLaunched(Task task){ }
+
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
@@ -2234,7 +2253,7 @@
getCurrentPageTaskView().setModalness(modalness);
}
// Only show actions view when it's modal for in-place landscape mode.
- boolean inPlaceLandscape = !mOrientationState.canLauncherRotate()
+ boolean inPlaceLandscape = !mOrientationState.canRecentsActivityRotate()
&& mOrientationState.getTouchRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 82fabac..222f6e6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -30,7 +30,8 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
+ .LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import android.animation.Animator;
@@ -384,6 +385,7 @@
}
}, resultCallbackHandler);
}
+ getRecentsView().onTaskLaunched(mTask);
}
}
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index d2e0339..47ce320 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -83,6 +83,7 @@
super.onCreate(savedInstanceState);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
+ addMultiWindowModeChangedListener(mDepthController);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 2b08dcd..fe8f0c6 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -20,11 +20,15 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
import android.os.IBinder;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewTreeObserver;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -41,7 +45,8 @@
/**
* Controls blur and wallpaper zoom, for the Launcher surface only.
*/
-public class DepthController implements StateHandler<LauncherState> {
+public class DepthController implements StateHandler<LauncherState>,
+ BaseActivity.MultiWindowModeChangedListener {
public static final FloatProperty<DepthController> DEPTH =
new FloatProperty<DepthController>("depth") {
@@ -104,6 +109,9 @@
*/
private float mDepth;
+ // Workaround for animating the depth when multiwindow mode changes.
+ private boolean mIgnoreStateChangesDuringMultiWindowAnimation = false;
+
private View.OnAttachStateChangeListener mOnAttachListener;
public DepthController(Launcher l) {
@@ -171,7 +179,7 @@
@Override
public void setState(LauncherState toState) {
- if (mSurface == null) {
+ if (mSurface == null || mIgnoreStateChangesDuringMultiWindowAnimation) {
return;
}
@@ -186,7 +194,8 @@
PendingAnimation animation) {
if (mSurface == null
|| config.onlyPlayAtomicComponent()
- || config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)) {
+ || config.hasAnimationFlag(SKIP_DEPTH_CONTROLLER)
+ || mIgnoreStateChangesDuringMultiWindowAnimation) {
return;
}
@@ -231,4 +240,21 @@
.apply();
}
}
+
+ @Override
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
+ mIgnoreStateChangesDuringMultiWindowAnimation = true;
+
+ ObjectAnimator mwAnimation = ObjectAnimator.ofFloat(this, DEPTH,
+ mLauncher.getStateManager().getState().getDepth(mLauncher, isInMultiWindowMode))
+ .setDuration(300);
+ mwAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIgnoreStateChangesDuringMultiWindowAnimation = false;
+ }
+ });
+ mwAnimation.setAutoCancel(true);
+ mwAnimation.start();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 2fd807d..eac45e9 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -17,13 +17,12 @@
package com.android.quickstep.logging;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
-import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.PREDICTED_HOTSEAT_CONTAINER;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
-import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__SRC_STATE__HOME;
import android.content.Context;
import android.util.Log;
@@ -47,6 +46,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.Executors;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.LogConfig;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -75,6 +75,7 @@
// from nano to lite, bake constant to prevent robo test failure.
private static final int DEFAULT_PAGE_INDEX = -2;
private static final int FOLDER_HIERARCHY_OFFSET = 100;
+ private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
public StatsLogCompatManager(Context context) {
sContext = context;
@@ -124,11 +125,14 @@
writeSnapshot(atomInfo, mInstanceId);
}
for (FolderInfo fInfo : folders) {
- ArrayList<WorkspaceItemInfo> folderContents = (ArrayList) fInfo.contents.clone();
- for (ItemInfo info : folderContents) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
- writeSnapshot(atomInfo, mInstanceId);
- }
+ try {
+ ArrayList<WorkspaceItemInfo> folderContents =
+ (ArrayList) Executors.MAIN_EXECUTOR.submit(fInfo.contents::clone).get();
+ for (ItemInfo info : folderContents) {
+ LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
+ writeSnapshot(atomInfo, mInstanceId);
+ }
+ } catch (Exception e) { }
}
for (ItemInfo info : appWidgets) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
@@ -153,10 +157,10 @@
getComponentName(info) /* component_name */,
getGridX(info, false) /* grid_x */,
getGridY(info, false) /* grid_y */,
- getPageId(info, false) /* page_id */,
+ getPageId(info) /* page_id */,
getGridX(info, true) /* grid_x_parent */,
getGridY(info, true) /* grid_y_parent */,
- getPageId(info, true) /* page_id_parent */,
+ getParentPageId(info) /* page_id_parent */,
getHierarchy(info) /* hierarchy */,
info.getIsWork() /* is_work_profile */,
info.getAttribute().getNumber() /* origin */,
@@ -175,8 +179,8 @@
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private OptionalInt mRank = OptionalInt.empty();
private Optional<ContainerInfo> mContainerInfo = Optional.empty();
- private int mSrcState = LAUNCHER_UICHANGED__SRC_STATE__HOME;
- private int mDstState = LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
+ private int mSrcState = LAUNCHER_STATE_UNSPECIFIED;
+ private int mDstState = LAUNCHER_STATE_UNSPECIFIED;
private Optional<FromState> mFromState = Optional.empty();
private Optional<ToState> mToState = Optional.empty();
private Optional<String> mEditText = Optional.empty();
@@ -318,10 +322,10 @@
getComponentName(atomInfo) /* component_name */,
getGridX(atomInfo, false) /* grid_x */,
getGridY(atomInfo, false) /* grid_y */,
- getPageId(atomInfo, false) /* page_id */,
+ getPageId(atomInfo) /* page_id */,
getGridX(atomInfo, true) /* grid_x_parent */,
getGridY(atomInfo, true) /* grid_y_parent */,
- getPageId(atomInfo, true) /* page_id_parent */,
+ getParentPageId(atomInfo) /* page_id_parent */,
getHierarchy(atomInfo) /* hierarchy */,
atomInfo.getIsWork() /* is_work_profile */,
atomInfo.getRank() /* rank */,
@@ -333,9 +337,14 @@
}
private static int getCardinality(LauncherAtom.ItemInfo info) {
- return info.getContainerInfo().getContainerCase().equals(PREDICTED_HOTSEAT_CONTAINER)
- ? info.getContainerInfo().getPredictedHotseatContainer().getCardinality()
- : info.getFolderIcon().getCardinality();
+ switch (info.getContainerInfo().getContainerCase()){
+ case PREDICTED_HOTSEAT_CONTAINER:
+ return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
+ case SEARCH_RESULT_CONTAINER:
+ return info.getContainerInfo().getSearchResultContainer().getQueryLength();
+ default:
+ return info.getFolderIcon().getCardinality();
+ }
}
private static String getPackageName(LauncherAtom.ItemInfo info) {
@@ -392,15 +401,24 @@
}
}
- private static int getPageId(LauncherAtom.ItemInfo info, boolean parent) {
- if (info.getContainerInfo().getContainerCase() == FOLDER) {
- if (parent) {
- return info.getContainerInfo().getFolder().getWorkspace().getPageIndex();
- } else {
+ private static int getPageId(LauncherAtom.ItemInfo info) {
+ switch (info.getContainerInfo().getContainerCase()) {
+ case FOLDER:
return info.getContainerInfo().getFolder().getPageIndex();
- }
- } else {
- return info.getContainerInfo().getWorkspace().getPageIndex();
+ default:
+ return info.getContainerInfo().getWorkspace().getPageIndex();
+ }
+ }
+
+ private static int getParentPageId(LauncherAtom.ItemInfo info) {
+ switch (info.getContainerInfo().getContainerCase()) {
+ case FOLDER:
+ return info.getContainerInfo().getFolder().getWorkspace().getPageIndex();
+ case SEARCH_RESULT_CONTAINER:
+ return info.getContainerInfo().getSearchResultContainer().getWorkspace()
+ .getPageIndex();
+ default:
+ return info.getContainerInfo().getWorkspace().getPageIndex();
}
}
@@ -408,6 +426,9 @@
if (info.getContainerInfo().getContainerCase() == FOLDER) {
return info.getContainerInfo().getFolder().getParentContainerCase().getNumber()
+ FOLDER_HIERARCHY_OFFSET;
+ } else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
+ return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
+ .getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
} else {
return info.getContainerInfo().getContainerCase().getNumber();
}
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index f302fdd..e998e9a 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -19,6 +19,7 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.ClipData;
@@ -55,6 +56,9 @@
public class ImageActionUtils {
private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".overview.fileprovider";
+ private static final long FILE_LIFE = 1000L /*ms*/ * 60L /*s*/ * 60L /*m*/ * 24L /*h*/;
+ private static final String SUB_FOLDER = "Overview";
+ private static final String BASE_NAME = "overview_image_";
/**
* Saves screenshot to location determine by SystemUiProxy
@@ -110,10 +114,13 @@
*/
@WorkerThread
public static Uri getImageUri(Bitmap bitmap, Rect crop, Context context, String tag) {
+ clearOldCacheFiles(context);
Bitmap croppedBitmap = cropBitmap(bitmap, crop);
int cropHash = crop == null ? 0 : crop.hashCode();
- String baseName = "image_" + bitmap.hashCode() + "_" + cropHash + ".png";
- File file = new File(context.getCacheDir(), baseName);
+ String baseName = BASE_NAME + bitmap.hashCode() + "_" + cropHash + ".png";
+ File parent = new File(context.getCacheDir(), SUB_FOLDER);
+ parent.mkdir();
+ File file = new File(parent, baseName);
try (FileOutputStream fos = new FileOutputStream(file)) {
croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
@@ -175,4 +182,19 @@
.setClipData(clipdata);
return new Intent[]{Intent.createChooser(intent, null).addFlags(FLAG_ACTIVITY_NEW_TASK)};
}
+
+ private static void clearOldCacheFiles(Context context) {
+ THREAD_POOL_EXECUTOR.execute(() -> {
+ File parent = new File(context.getCacheDir(), SUB_FOLDER);
+ File[] files = parent.listFiles((File f, String s) -> s.startsWith(BASE_NAME));
+ if (files != null) {
+ for (File file: files) {
+ if (file.lastModified() + FILE_LIFE < System.currentTimeMillis()) {
+ file.delete();
+ }
+ }
+ }
+ });
+
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index cefab1b..ae19d73 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -58,21 +58,6 @@
}
/**
- * Gets the scale that should be applied to the TaskView so that it matches the target
- * TODO: Remove this method
- */
- public static float getTaskScale(RecentsOrientedState orientedState,
- float srcWidth, float srcHeight, float targetWidth, float targetHeight) {
- if (orientedState == null
- || orientedState.isHomeRotationAllowed()
- || orientedState.isDisplayPhoneNatural()) {
- return srcWidth / targetWidth;
- } else {
- return srcHeight / targetHeight;
- }
- }
-
- /**
* Recursively sets view and all children enabled/disabled.
* @param view Top most parent view to change.
* @param enabled True = enable, False = disable.
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 90ee18f..b359f0f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -33,6 +33,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Matrix;
@@ -47,6 +48,7 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -56,6 +58,7 @@
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.SysUINavigationMode;
+import com.android.systemui.shared.system.ConfigurationCompat;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -87,7 +90,7 @@
private @SurfaceRotation int mTouchRotation = ROTATION_0;
private @SurfaceRotation int mDisplayRotation = ROTATION_0;
- private @SurfaceRotation int mLauncherRotation = ROTATION_0;
+ private @SurfaceRotation int mRecentsActivityRotation = ROTATION_0;
// Launcher activity supports multiple orientation, but fallback activity does not
private static final int FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_ACTIVITY = 1 << 0;
@@ -130,6 +133,8 @@
private int mFlags;
private int mPreviousRotation = ROTATION_0;
+ @Nullable private Configuration mActivityConfiguration;
+
/**
* @param rotationChangeListener Callback for receiving rotation events when rotation watcher
* is enabled
@@ -165,6 +170,15 @@
}
/**
+ * Sets the configuration for the recents activity, which could affect the activity's rotation
+ * @see #update(int, int)
+ */
+ public boolean setActivityConfiguration(Configuration activityConfiguration) {
+ mActivityConfiguration = activityConfiguration;
+ return update(mTouchRotation, mDisplayRotation);
+ }
+
+ /**
* Sets if the host is in multi-window mode
*/
public void setMultiWindowMode(boolean isMultiWindow) {
@@ -188,23 +202,19 @@
*/
public boolean update(
@SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
- if (!isMultipleOrientationSupportedByDevice()) {
- return false;
- }
-
- int launcherRotation = inferLauncherRotation(displayRotation);
+ int recentsActivityRotation = inferRecentsActivityRotation(displayRotation);
if (mDisplayRotation == displayRotation
&& mTouchRotation == touchRotation
- && mLauncherRotation == launcherRotation) {
+ && mRecentsActivityRotation == recentsActivityRotation) {
return false;
}
- mLauncherRotation = launcherRotation;
+ mRecentsActivityRotation = recentsActivityRotation;
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
mPreviousRotation = touchRotation;
- if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
+ if (mRecentsActivityRotation == mTouchRotation || canRecentsActivityRotate()) {
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
@@ -226,9 +236,11 @@
}
@SurfaceRotation
- private int inferLauncherRotation(@SurfaceRotation int displayRotation) {
- if (!isMultipleOrientationSupportedByDevice() || isHomeRotationAllowed()) {
- return displayRotation;
+ private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
+ if (isRecentsActivityRotationAllowed()) {
+ return mActivityConfiguration == null
+ ? displayRotation
+ : ConfigurationCompat.getWindowConfigurationRotation(mActivityConfiguration);
} else {
return ROTATION_0;
}
@@ -236,7 +248,8 @@
private void setFlag(int mask, boolean enabled) {
boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
- && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED;
+ && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
+ && !canRecentsActivityRotate();
if (enabled) {
mFlags |= mask;
} else {
@@ -244,7 +257,8 @@
}
boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation
- && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED;
+ && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
+ && !canRecentsActivityRotate();
if (wasRotationEnabled != isRotationEnabled) {
UI_HELPER_EXECUTOR.execute(() -> {
if (isRotationEnabled) {
@@ -324,8 +338,8 @@
}
@SurfaceRotation
- public int getLauncherRotation() {
- return mLauncherRotation;
+ public int getRecentsActivityRotation() {
+ return mRecentsActivityRotation;
}
public boolean isMultipleOrientationSupportedByDevice() {
@@ -333,14 +347,21 @@
== MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
}
- public boolean isHomeRotationAllowed() {
- return (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS | FLAG_MULTIWINDOW_ROTATION_ALLOWED))
- != 0 ||
- (mFlags & FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING) != 0;
+ public boolean isRecentsActivityRotationAllowed() {
+ // Activity rotation is allowed if the multi-simulated-rotation is not supported
+ // (fallback recents or tablets) or activity rotation is enabled by various settings.
+ return ((mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
+ != MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
+ || (mFlags & (FLAG_HOME_ROTATION_ALLOWED_IN_PREFS
+ | FLAG_MULTIWINDOW_ROTATION_ALLOWED
+ | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0;
}
- public boolean canLauncherRotate() {
- return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isHomeRotationAllowed();
+ /**
+ * Returns true if the activity can rotate, if allowed by system rotation settings
+ */
+ public boolean canRecentsActivityRotate() {
+ return (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0 && isRecentsActivityRotationAllowed();
}
/**
@@ -508,8 +529,8 @@
extractObjectNameAndAddress(mOrientationHandler.toString())
+ " mDisplayRotation=" + mDisplayRotation
+ " mTouchRotation=" + mTouchRotation
- + " mLauncherRotation=" + mLauncherRotation
- + " mHomeRotation=" + isHomeRotationAllowed()
+ + " mRecentsActivityRotation=" + mRecentsActivityRotation
+ + " isRecentsActivityRotationAllowed=" + isRecentsActivityRotationAllowed()
+ " mSystemRotation=" + systemRotationOn
+ " mFlags=" + mFlags
+ "]";
@@ -521,7 +542,8 @@
public DeviceProfile getLauncherDeviceProfile() {
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
// TODO also check the natural orientation is landscape or portrait
- return (mLauncherRotation == ROTATION_90 || mLauncherRotation == ROTATION_270)
+ return (mRecentsActivityRotation == ROTATION_90
+ || mRecentsActivityRotation == ROTATION_270)
? idp.landscapeProfile
: idp.portraitProfile;
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index bd8ab08..b9e0f62 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -131,6 +131,10 @@
TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
getString("result"));
}
+ // b/143488140
+ mDevice.pressHome();
+ mDevice.waitForIdle();
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
}
// b/143488140
diff --git a/res/layout/work_mode_switch.xml b/res/layout/work_mode_switch.xml
index b5237db..31953c7 100644
--- a/res/layout/work_mode_switch.xml
+++ b/res/layout/work_mode_switch.xml
@@ -17,7 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/PrimaryMediumText"
+ style="@style/PrimaryHeadline"
android:id="@+id/work_mode_toggle"
android:drawableStart="@drawable/ic_corp"
android:drawablePadding="16dp"
@@ -25,6 +25,7 @@
android:textColor="?attr/workProfileOverlayTextColor"
android:layout_alignParentBottom="true"
android:ellipsize="end"
+ android:elevation="10dp"
android:gravity="start"
android:lines="1"
android:showText="false"
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 82f5dc6..25f21f3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -246,7 +246,6 @@
<style name="DropTargetButton" parent="DropTargetButtonBase" />
<style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
- <style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
<style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
<style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index d927ffc..b7ba106 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -16,6 +16,7 @@
package com.android.launcher3.folder;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.Context;
@@ -61,16 +62,16 @@
ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
list.add(mItem1);
list.add(mItem2);
- FolderNameInfo[] nameInfos =
- new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfos nameInfos = new FolderNameInfos();
new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
- assertEquals("Work", nameInfos[0].getLabel());
+ assertEquals("Work", nameInfos.getLabels()[0]);
- nameInfos[0] = new FolderNameInfo("candidate1", 0.9);
- nameInfos[1] = new FolderNameInfo("candidate2", 0.8);
- nameInfos[2] = new FolderNameInfo("candidate3", 0.7);
+ nameInfos.setLabel(0, "candidate1", 1.0f);
+ nameInfos.setLabel(1, "candidate2", 1.0f);
+ nameInfos.setLabel(2, "candidate3", 1.0f);
new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
- assertEquals("Work", nameInfos[3].getLabel());
-
+ assertEquals("Work", nameInfos.getLabels()[3]);
+ assertTrue(nameInfos.hasSuggestions());
+ assertTrue(nameInfos.hasPrimary());
}
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 7d80d81..310c306 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -37,8 +37,6 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.StatsLogUtils;
-import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
@@ -52,7 +50,7 @@
/**
* Launcher BaseActivity
*/
-public abstract class BaseActivity extends Activity implements LogStateProvider, ActivityContext {
+public abstract class BaseActivity extends Activity implements ActivityContext {
private static final String TAG = "BaseActivity";
@@ -146,13 +144,11 @@
return mDeviceProfile;
}
- public int getCurrentState() { return StatsLogUtils.LAUNCHER_STATE_BACKGROUND; }
-
public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {}
public final StatsLogManager getStatsLogManager() {
if (mStatsLogManager == null) {
- mStatsLogManager = StatsLogManager.newInstance(this, this);
+ mStatsLogManager = StatsLogManager.newInstance(this);
}
return mStatsLogManager;
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7fc64ea..0970dae 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -38,6 +38,10 @@
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP;
+import static com.android.launcher3.logging.StatsLogManager.containerTypeToAtomState;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -110,8 +114,9 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.BgDataModel.Callbacks;
@@ -920,13 +925,32 @@
private void logStopAndResume(int command) {
+ int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
int containerType = mStateManager.getState().containerType;
+
+ StatsLogManager.EventEnum event;
+ StatsLogManager.StatsLogger logger = getStatsLogManager().logger();
+ if (command == Action.Command.RESUME) {
+ logger.withSrcState(LAUNCHER_STATE_BACKGROUND)
+ .withDstState(containerTypeToAtomState(mStateManager.getState().containerType));
+ event = LAUNCHER_ONRESUME;
+ } else { /* command == Action.Command.STOP */
+ logger.withSrcState(containerTypeToAtomState(mStateManager.getState().containerType))
+ .withDstState(LAUNCHER_STATE_BACKGROUND);
+ event = LAUNCHER_ONSTOP;
+ }
+
if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
getUserEventDispatcher().logActionCommand(command,
- containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0);
+ containerType, -1, pageIndex);
+ logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(pageIndex)).build());
} else {
getUserEventDispatcher().logActionCommand(command, containerType, -1);
}
+ logger.log(event);
}
private void scheduleDeferredCheck() {
@@ -1836,16 +1860,6 @@
}
@Override
- public int getCurrentState() {
- if (mStateManager.getState() == LauncherState.ALL_APPS) {
- return StatsLogUtils.LAUNCHER_STATE_ALLAPPS;
- } else if (mStateManager.getState() == OVERVIEW) {
- return StatsLogUtils.LAUNCHER_STATE_OVERVIEW;
- }
- return StatsLogUtils.LAUNCHER_STATE_HOME;
- }
-
- @Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
@Nullable String sourceContainer) {
if (!hasBeenResumed()) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 31bfc09..c78df62 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -226,7 +226,16 @@
* 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
*/
public final float getDepth(Context context) {
- if (BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode) {
+ return getDepth(context,
+ BaseDraggingActivity.fromContext(context).getDeviceProfile().isMultiWindowMode);
+ }
+
+ /**
+ * Returns the amount of blur and wallpaper zoom for this state with {@param isMultiWindowMode}.
+ * @see #getDepth(Context).
+ */
+ public final float getDepth(Context context, boolean isMultiWindowMode) {
+ if (isMultiWindowMode) {
return 0;
}
return getDepthUnchecked(context);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index dc1ff66..e29faac 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -71,7 +71,9 @@
public abstract class PagedView<T extends View & PageIndicator> extends ViewGroup {
private static final String TAG = "PagedView";
private static final boolean DEBUG = false;
+ public static final boolean DEBUG_FAILED_QUICKSWITCH = false;
+ public static final int ACTION_MOVE_ALLOW_EASY_FLING = MotionEvent.ACTION_MASK - 1;
public static final int INVALID_PAGE = -1;
protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
@@ -89,14 +91,16 @@
// The following constants need to be scaled based on density. The scaled versions will be
// assigned to the corresponding member variables below.
private static final int FLING_THRESHOLD_VELOCITY = 500;
+ private static final int EASY_FLING_THRESHOLD_VELOCITY = 400;
private static final int MIN_SNAP_VELOCITY = 1500;
private static final int MIN_FLING_VELOCITY = 250;
private boolean mFreeScroll = false;
- protected int mFlingThresholdVelocity;
- protected int mMinFlingVelocity;
- protected int mMinSnapVelocity;
+ protected final int mFlingThresholdVelocity;
+ protected final int mEasyFlingThresholdVelocity;
+ protected final int mMinFlingVelocity;
+ protected final int mMinSnapVelocity;
protected boolean mFirstLayout = true;
@@ -118,12 +122,17 @@
private float mLastMotion;
private float mLastMotionRemainder;
private float mTotalMotion;
+ // Used in special cases where the fling checks can be relaxed for an intentional gesture
+ private boolean mAllowEasyFling;
protected PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT;
protected int[] mPageScrolls;
private boolean mIsBeingDragged;
+ // The amount of movement to begin scrolling
protected int mTouchSlop;
+ // The amount of movement to begin paging
+ protected int mPageSlop;
private int mMaximumVelocity;
protected boolean mAllowOverScroll = true;
@@ -170,24 +179,19 @@
setHapticFeedbackEnabled(false);
mIsRtl = Utilities.isRtl(getResources());
- init();
- }
- /**
- * Initializes various states for this workspace.
- */
- protected void init() {
- Context context = getContext();
mScroller = new OverScroller(context);
setDefaultInterpolator(Interpolators.SCROLL);
mCurrentPage = 0;
final ViewConfiguration configuration = ViewConfiguration.get(context);
- mTouchSlop = configuration.getScaledPagingTouchSlop();
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mPageSlop = configuration.getScaledPagingTouchSlop();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
float density = getResources().getDisplayMetrics().density;
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
+ mEasyFlingThresholdVelocity = (int) (EASY_FLING_THRESHOLD_VELOCITY * density);
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
@@ -913,6 +917,7 @@
mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
mLastMotionRemainder = 0;
mTotalMotion = 0;
+ mAllowEasyFling = false;
mActivePointerId = ev.getPointerId(0);
updateIsBeingDraggedOnTouchDown();
@@ -944,7 +949,7 @@
private void updateIsBeingDraggedOnTouchDown() {
// mScroller.isFinished should be false when being flinged.
final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos());
- final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3);
+ final boolean finishedScrolling = (mScroller.isFinished() || xDist < mPageSlop / 3);
if (finishedScrolling) {
mIsBeingDragged = false;
@@ -977,7 +982,7 @@
final float primaryDirection = mOrientationHandler.getPrimaryDirection(ev, pointerIndex);
final int diff = (int) Math.abs(primaryDirection - mLastMotion);
final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
- boolean moved = diff > touchSlop;
+ boolean moved = diff > touchSlop || ev.getAction() == ACTION_MOVE_ALLOW_EASY_FLING;
if (moved) {
// Scroll if the user moved far enough along the X axis
@@ -1160,6 +1165,7 @@
mDownMotionPrimary = mLastMotion = mOrientationHandler.getPrimaryDirection(ev, 0);
mLastMotionRemainder = 0;
mTotalMotion = 0;
+ mAllowEasyFling = false;
mActivePointerId = ev.getPointerId(0);
if (mIsBeingDragged) {
onScrollInteractionBegin();
@@ -1167,8 +1173,14 @@
}
break;
- case MotionEvent.ACTION_MOVE:
- if (mIsBeingDragged) {
+ case ACTION_MOVE_ALLOW_EASY_FLING:
+ // Start scrolling immediately
+ determineScrollingStart(ev);
+ mAllowEasyFling = true;
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (mIsBeingDragged) {
// Scroll to follow the motion event
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
@@ -1214,9 +1226,14 @@
SIGNIFICANT_MOVE_THRESHOLD;
mTotalMotion += Math.abs(mLastMotion + mLastMotionRemainder - primaryDirection);
- boolean isFling = mTotalMotion > mTouchSlop && shouldFlingForVelocity(velocity);
+ boolean passedSlop = mAllowEasyFling || mTotalMotion > mPageSlop;
+ boolean isFling = passedSlop && shouldFlingForVelocity(velocity);
boolean isDeltaLeft = mIsRtl ? delta > 0 : delta < 0;
boolean isVelocityLeft = mIsRtl ? velocity > 0 : velocity < 0;
+ if (DEBUG_FAILED_QUICKSWITCH && !isFling && mAllowEasyFling) {
+ Log.d("Quickswitch", "isFling=false vel=" + velocity
+ + " threshold=" + mEasyFlingThresholdVelocity);
+ }
if (!mFreeScroll) {
// In the case that the page is moved far to one direction and then is flung
@@ -1316,7 +1333,8 @@
}
protected boolean shouldFlingForVelocity(int velocity) {
- return Math.abs(velocity) > mFlingThresholdVelocity;
+ float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
+ return Math.abs(velocity) > threshold;
}
private void resetTouchState() {
@@ -1393,8 +1411,7 @@
}
private void onSecondaryPointerUp(MotionEvent ev) {
- final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
- MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ final int pointerIndex = ev.getActionIndex();
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a8dca12..1441e0b 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -28,6 +28,9 @@
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -79,6 +82,7 @@
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.logging.UserEventDispatcher;
@@ -999,6 +1003,15 @@
if (!mOverlayShown) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
+ mLauncher.getStatsLogManager().logger()
+ .withSrcState(LAUNCHER_STATE_HOME)
+ .withDstState(LAUNCHER_STATE_HOME)
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(0))
+ .build())
+ .log(LAUNCHER_SWIPELEFT);
}
mOverlayShown = true;
// Not announcing the overlay page for accessibility since it announces itself.
@@ -1008,6 +1021,15 @@
if (!ued.isPreviousHomeGesture()) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+ mLauncher.getStatsLogManager().logger()
+ .withSrcState(LAUNCHER_STATE_HOME)
+ .withDstState(LAUNCHER_STATE_HOME)
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(-1))
+ .build())
+ .log(LAUNCHER_SWIPERIGHT);
}
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
@@ -1099,9 +1121,20 @@
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
if (prevPage != mCurrentPage) {
- int swipeDirection = (prevPage < mCurrentPage) ? Action.Direction.RIGHT : Action.Direction.LEFT;
+ int swipeDirection = (prevPage < mCurrentPage)
+ ? Action.Direction.RIGHT : Action.Direction.LEFT;
+ StatsLogManager.EventEnum event = (prevPage < mCurrentPage)
+ ? LAUNCHER_SWIPERIGHT : LAUNCHER_SWIPELEFT;
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
swipeDirection, ContainerType.WORKSPACE, prevPage);
+ mLauncher.getStatsLogManager().logger()
+ .withSrcState(LAUNCHER_STATE_HOME)
+ .withDstState(LAUNCHER_STATE_HOME)
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(prevPage)).build())
+ .log(event);
}
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index e950f3c..d01e189 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -26,10 +26,6 @@
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
-import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
-
-import static java.util.Arrays.asList;
-import static java.util.Optional.ofNullable;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -37,7 +33,6 @@
import android.annotation.SuppressLint;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
@@ -104,6 +99,7 @@
import java.util.Objects;
import java.util.StringJoiner;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -327,11 +323,7 @@
public void startEditingFolderName() {
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- ofNullable(mInfo)
- .map(info -> info.suggestedFolderNames)
- .map(folderNames -> (FolderNameInfo[]) folderNames
- .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
- .ifPresent(this::showLabelSuggestions);
+ showLabelSuggestions();
}
mFolderName.setHint("");
mIsEditingName = true;
@@ -346,11 +338,8 @@
if (DEBUG) {
Log.d(TAG, "onBackKey newTitle=" + newTitle);
}
- mInfo.setTitle(newTitle);
- mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
- mLauncher.getModelWriter());
+ mInfo.setTitle(newTitle, mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
- mLauncher.getModelWriter().updateItemInDatabase(mInfo);
if (TextUtils.isEmpty(mInfo.title)) {
mFolderName.setHint(R.string.folder_hint_text);
@@ -461,32 +450,24 @@
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
* rest of the suggestions to InputMethodManager.
*/
- private void showLabelSuggestions(FolderNameInfo[] nameInfos) {
- if (nameInfos == null) {
+ private void showLabelSuggestions() {
+ if (mInfo.suggestedFolderNames == null) {
return;
}
- // Open the Folder and Keyboard when the first or second suggestion is valid non-empty
- // string.
- boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
- nameInfos[0].getLabel())
- || nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
- nameInfos[1].getLabel());
-
- if (shouldOpen) {
+ if (mInfo.suggestedFolderNames.hasSuggestions()) {
// update the primary suggestion if the folder name is empty.
if (isEmpty(mFolderName.getText())) {
- CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
- if (!isEmpty(firstLabel)) {
+ if (mInfo.suggestedFolderNames.hasPrimary()) {
mFolderName.setHint("");
- mFolderName.setText(firstLabel);
+ mFolderName.setText(mInfo.suggestedFolderNames.getLabels()[0]);
mFolderName.selectAll();
}
}
mFolderName.showKeyboard();
mFolderName.displayCompletions(
- asList(nameInfos).subList(0, nameInfos.length).stream()
+ Stream.of(mInfo.suggestedFolderNames.getLabels())
.filter(Objects::nonNull)
- .map(s -> s.getLabel().toString())
+ .map(Object::toString)
.filter(s -> !s.isEmpty())
.filter(s -> !s.equalsIgnoreCase(mFolderName.getText().toString()))
.collect(Collectors.toList()));
@@ -1021,14 +1002,11 @@
if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind
&& total > 1 /* no need to update if there's one icon */) {
Executors.MODEL_EXECUTOR.post(() -> {
- FolderNameInfo[] nameInfos =
- new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfos nameInfos = new FolderNameInfos();
FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
fnp.getSuggestedFolderName(
getContext(), mInfo.contents, nameInfos);
- mInfo.suggestedFolderNames = new Intent().putExtra(
- FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
- nameInfos);
+ mInfo.suggestedFolderNames = nameInfos;
});
}
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index ce37506..75275b2 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -16,11 +16,11 @@
package com.android.launcher3.folder;
-import static android.text.TextUtils.isEmpty;
-
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -70,6 +70,7 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo.FolderListener;
+import com.android.launcher3.model.data.FolderInfo.LabelState;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
@@ -83,6 +84,7 @@
import java.util.List;
import java.util.function.Predicate;
+
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
@@ -408,8 +410,7 @@
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
final int finalIndex = index;
- FolderNameInfo[] nameInfos =
- new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfos nameInfos = new FolderNameInfos();
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
Executors.MODEL_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
@@ -425,7 +426,7 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- FolderNameInfo[] nameInfos, InstanceId instanceId) {
+ FolderNameInfos nameInfos, InstanceId instanceId) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
@@ -437,24 +438,33 @@
/**
* Set the suggested folder name.
*/
- public void setLabelSuggestion(FolderNameInfo[] nameInfos, InstanceId instanceId) {
+ public void setLabelSuggestion(FolderNameInfos nameInfos, InstanceId instanceId) {
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
return;
}
- if (!isEmpty(mFolderName.getText().toString())
- || mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
+ if (!mInfo.getLabelState().equals(LabelState.UNLABELED)) {
return;
}
- if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
+ if (nameInfos == null || !nameInfos.hasSuggestions()) {
+ StatsLogManager.newInstance(getContext()).logger()
+ .withInstanceId(instanceId)
+ .withItemInfo(mInfo)
+ .log(LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS);
return;
}
- CharSequence newTitle = nameInfos[0].getLabel();
+ if (!nameInfos.hasPrimary()) {
+ StatsLogManager.newInstance(getContext()).logger()
+ .withInstanceId(instanceId)
+ .withItemInfo(mInfo)
+ .log(LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY);
+ return;
+ }
+ CharSequence newTitle = nameInfos.getLabels()[0];
FromState fromState = mInfo.getFromLabelState();
- mInfo.setTitle(newTitle);
+ mInfo.setTitle(newTitle, mFolder.mLauncher.getModelWriter());
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
- mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
// Logging for folder creation flow
StatsLogManager.newInstance(getContext()).logger()
diff --git a/src/com/android/launcher3/folder/FolderNameInfo.java b/src/com/android/launcher3/folder/FolderNameInfo.java
deleted file mode 100644
index 1841cd9..0000000
--- a/src/com/android/launcher3/folder/FolderNameInfo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.folder;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-
-/**
- * Information about a single label suggestions of the Folder.
- */
-
-public final class FolderNameInfo implements Parcelable {
- private final double mScore;
- private final CharSequence mLabel;
-
- /**
- * Create a simple completion with label.
- *
- * @param label The text that should be inserted into the editor and pushed to
- * InputMethodManager suggestions.
- * @param score The score for the label between 0.0 and 1.0.
- */
- public FolderNameInfo(CharSequence label, double score) {
- mScore = score;
- mLabel = label;
- }
-
- private FolderNameInfo(Parcel source) {
- mScore = source.readDouble();
- mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- }
-
- public CharSequence getLabel() {
- return mLabel;
- }
-
- public double getScore() {
- return mScore;
- }
-
- /**
- * Used to package this object into a {@link Parcel}.
- *
- * @param dest The {@link Parcel} to be written.
- * @param flags The flags used for parceling.
- */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeDouble(mScore);
- TextUtils.writeToParcel(mLabel, dest, flags);
- }
-
- /**
- * Used to make this class parcelable.
- */
- @NonNull
- public static final Parcelable.Creator<FolderNameInfo> CREATOR =
- new Parcelable.Creator<FolderNameInfo>() {
- public FolderNameInfo createFromParcel(Parcel source) {
- return new FolderNameInfo(source);
- }
-
- public FolderNameInfo[] newArray(int size) {
- return new FolderNameInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- @NonNull
- public String toString() {
- return String.format("%s:%.2f", mLabel, mScore);
- }
-}
diff --git a/src/com/android/launcher3/folder/FolderNameInfos.java b/src/com/android/launcher3/folder/FolderNameInfos.java
new file mode 100644
index 0000000..457ae87
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderNameInfos.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Information about a label suggestions of a Folder.
+ */
+
+public class FolderNameInfos {
+ public static final int SUCCESS = 1;
+ public static final int HAS_PRIMARY = 1 << 1;
+ public static final int HAS_SUGGESTIONS = 1 << 2;
+ public static final int ERROR_NO_PROVIDER = 1 << 3;
+ public static final int ERROR_APP_LOOKUP_FAILED = 1 << 4;
+ public static final int ERROR_ALL_APP_LOOKUP_FAILED = 1 << 5;
+ public static final int ERROR_NO_LABELS_GENERATED = 1 << 6;
+ public static final int ERROR_LABEL_LOOKUP_FAILED = 1 << 7;
+ public static final int ERROR_ALL_LABEL_LOOKUP_FAILED = 1 << 8;
+ public static final int ERROR_NO_PACKAGES = 1 << 9;
+
+ private int mStatus;
+ private final CharSequence[] mLabels;
+ private final Float[] mScores;
+
+ public FolderNameInfos() {
+ mStatus = 0;
+ mLabels = new CharSequence[FolderNameProvider.SUGGEST_MAX];
+ mScores = new Float[FolderNameProvider.SUGGEST_MAX];
+ }
+
+ /**
+ * set the status of FolderNameInfos.
+ */
+ public void setStatus(int statusBit) {
+ mStatus = mStatus | statusBit;
+ }
+
+ /**
+ * returns status of FolderNameInfos generations.
+ */
+ public int status() {
+ return mStatus;
+ }
+
+ /**
+ * return true if the first suggestion is a Primary suggestion.
+ */
+ public boolean hasPrimary() {
+ return (mStatus & HAS_PRIMARY) > 0 && (mLabels[0] != null);
+ }
+
+ /**
+ * return true if there is at least one valid suggestion.
+ */
+ public boolean hasSuggestions() {
+ for (CharSequence l : mLabels) {
+ if (l != null && !TextUtils.isEmpty(l)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * assign label and score in the specified index.
+ */
+ public void setLabel(int index, CharSequence label, Float score) {
+ if (index < mLabels.length) {
+ mLabels[index] = label;
+ mScores[index] = score;
+ }
+ }
+
+ /**
+ * returns true if the label is found in label suggestions/
+ */
+ public boolean contains(CharSequence label) {
+ return Arrays.stream(mLabels)
+ .filter(Objects::nonNull)
+ .anyMatch(l -> l.toString().equalsIgnoreCase(label.toString()));
+ }
+
+
+ public CharSequence[] getLabels() {
+ return mLabels;
+ }
+
+ public Float[] getScores() {
+ return mScores;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return String.format("status=%s, labels=%s", Integer.toBinaryString(mStatus),
+ Arrays.toString(mLabels));
+ }
+}
+
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 2be0bce..d166e27 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -96,10 +96,10 @@
*/
public void getSuggestedFolderName(Context context,
ArrayList<WorkspaceItemInfo> workspaceItemInfos,
- FolderNameInfo[] nameInfos) {
+ FolderNameInfos nameInfos) {
Preconditions.assertWorkerThread();
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
+ Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
}
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
@@ -124,7 +124,7 @@
info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
+ Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
}
}
@@ -138,39 +138,37 @@
.findAny();
}
- private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
- if (nameInfos.length == 0 || contains(nameInfos, label)) {
+ private void setAsFirstSuggestion(FolderNameInfos nameInfos, CharSequence label) {
+ if (nameInfos == null || nameInfos.contains(label)) {
return;
}
- for (int i = nameInfos.length - 1; i > 0; i--) {
- if (nameInfos[i - 1] != null && !TextUtils.isEmpty(nameInfos[i - 1].getLabel())) {
- nameInfos[i] = nameInfos[i - 1];
+ nameInfos.setStatus(FolderNameInfos.HAS_PRIMARY);
+ nameInfos.setStatus(FolderNameInfos.HAS_SUGGESTIONS);
+ CharSequence[] labels = nameInfos.getLabels();
+ Float[] scores = nameInfos.getScores();
+ for (int i = labels.length - 1; i > 0; i--) {
+ if (labels[i - 1] != null && !TextUtils.isEmpty(labels[i - 1])) {
+ nameInfos.setLabel(i, labels[i - 1], scores[i - 1]);
}
}
- nameInfos[0] = new FolderNameInfo(label, 1.0);
+ nameInfos.setLabel(0, label, 1.0f);
}
- private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
- if (nameInfos.length == 0 || contains(nameInfos, label)) {
+ private void setAsLastSuggestion(FolderNameInfos nameInfos, CharSequence label) {
+ if (nameInfos == null || nameInfos.contains(label)) {
return;
}
-
- for (int i = 0; i < nameInfos.length; i++) {
- if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
- nameInfos[i] = new FolderNameInfo(label, 1.0);
+ nameInfos.setStatus(FolderNameInfos.HAS_PRIMARY);
+ nameInfos.setStatus(FolderNameInfos.HAS_SUGGESTIONS);
+ CharSequence[] labels = nameInfos.getLabels();
+ for (int i = 0; i < labels.length; i++) {
+ if (labels[i] == null || TextUtils.isEmpty(labels[i])) {
+ nameInfos.setLabel(i, label, 1.0f);
return;
}
}
// Overwrite the last suggestion.
- int lastIndex = nameInfos.length - 1;
- nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
- }
-
- private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
- return Arrays.stream(nameInfos)
- .filter(Objects::nonNull)
- .anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
- label.toString()));
+ nameInfos.setLabel(labels.length - 1, label, 1.0f);
}
private class FolderNameWorker extends BaseModelUpdateTask {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index be8edc7..8e23b65 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -15,6 +15,12 @@
*/
package com.android.launcher3.logging;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_OPEN_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
+
import android.content.Context;
import androidx.annotation.Nullable;
@@ -23,8 +29,8 @@
import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
-import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.userevent.LauncherLogProto;
import com.android.launcher3.util.ResourceBasedOverride;
/**
@@ -35,6 +41,54 @@
*/
public class StatsLogManager implements ResourceBasedOverride {
+ public static final int LAUNCHER_STATE_UNSPECIFIED = 0;
+ public static final int LAUNCHER_STATE_BACKGROUND = 1;
+ public static final int LAUNCHER_STATE_HOME = 2;
+ public static final int LAUNCHER_STATE_OVERVIEW = 3;
+ public static final int LAUNCHER_STATE_ALLAPPS = 4;
+ public static final int LAUNCHER_STATE_UNCHANGED = 5;
+
+ /**
+ * Returns proper launcher state enum for {@link StatsLogManager}
+ * (to be removed during UserEventDispatcher cleanup)
+ */
+ public static int containerTypeToAtomState(int containerType) {
+ switch (containerType) {
+ case LauncherLogProto.ContainerType.ALLAPPS_VALUE:
+ return LAUNCHER_STATE_ALLAPPS;
+ case LauncherLogProto.ContainerType.OVERVIEW_VALUE:
+ return LAUNCHER_STATE_OVERVIEW;
+ case LauncherLogProto.ContainerType.WORKSPACE_VALUE:
+ return LAUNCHER_STATE_HOME;
+ case LauncherLogProto.ContainerType.APP_VALUE:
+ return LAUNCHER_STATE_BACKGROUND;
+ }
+ return LAUNCHER_STATE_UNSPECIFIED;
+ }
+
+ /**
+ * Returns event enum based on the two {@link ContainerType} transition information when
+ * swipe gesture happens.
+ * (to be removed during UserEventDispatcher cleanup)
+ */
+ public static EventEnum getLauncherAtomEvent(int startContainerType,
+ int targetContainerType, EventEnum fallbackEvent) {
+ if (startContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()
+ && targetContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()) {
+ return LAUNCHER_HOME_GESTURE;
+ } else if (startContainerType != LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()
+ && targetContainerType == LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()) {
+ return LAUNCHER_OVERVIEW_GESTURE;
+ } else if (startContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()
+ && targetContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+ return LAUNCHER_ALLAPPS_OPEN_UP;
+ } else if (startContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()
+ && targetContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+ return LAUNCHER_ALLAPPS_CLOSE_DOWN;
+ }
+ return fallbackEvent; // TODO fix
+ }
+
public interface EventEnum {
int getId();
}
@@ -71,6 +125,12 @@
@UiEvent(doc = "Folder's label is automatically assigned.")
LAUNCHER_FOLDER_AUTO_LABELED(591),
+ @UiEvent(doc = "Could not auto-label a folder because primary suggestion is null or empty.")
+ LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY(592),
+
+ @UiEvent(doc = "Could not auto-label a folder because no suggestions exist.")
+ LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS(593),
+
@UiEvent(doc = "User manually updated the folder label.")
LAUNCHER_FOLDER_LABEL_UPDATED(460),
@@ -158,6 +218,44 @@
@UiEvent(doc = "App launch ranking logged for hotseat predictions)")
LAUNCHER_HOTSEAT_RANKED(553),
+ @UiEvent(doc = "Launcher is now in background. e.g., Screen off event")
+ LAUNCHER_ONSTOP(562),
+
+ @UiEvent(doc = "Launcher is now in foreground. e.g., Screen on event, back button")
+ LAUNCHER_ONRESUME(563),
+
+ @UiEvent(doc = "User swipes or fling in LEFT direction on workspace.")
+ LAUNCHER_SWIPELEFT(564),
+
+ @UiEvent(doc = "User swipes or fling in RIGHT direction on workspace.")
+ LAUNCHER_SWIPERIGHT(565),
+
+ @UiEvent(doc = "User swipes or fling in UP direction in unknown way.")
+ LAUNCHER_UNKNOWN_SWIPEUP(566),
+
+ @UiEvent(doc = "User swipes or fling in DOWN direction in unknown way.")
+ LAUNCHER_UNKNOWN_SWIPEDOWN(567),
+
+ @UiEvent(doc = "User swipes or fling in UP direction to open apps drawer.")
+ LAUNCHER_ALLAPPS_OPEN_UP(568),
+
+ @UiEvent(doc = "User swipes or fling in DOWN direction to close apps drawer.")
+ LAUNCHER_ALLAPPS_CLOSE_DOWN(569),
+
+ @UiEvent(doc = "User swipes or fling in UP direction and hold from the bottom bazel area")
+ LAUNCHER_OVERVIEW_GESTURE(570),
+
+ @UiEvent(doc = "User swipes or fling in LEFT direction on the bottom bazel area.")
+ LAUNCHER_QUICKSWITCH_LEFT(571),
+
+ @UiEvent(doc = "User swipes or fling in RIGHT direction on the bottom bazel area.")
+ LAUNCHER_QUICKSWITCH_RIGHT(572),
+
+ @UiEvent(doc = "User swipes or fling in DOWN direction on the bottom bazel area.")
+ LAUNCHER_SWIPEDOWN_NAVBAR(573),
+
+ @UiEvent(doc = "User swipes or fling in UP direction from bottom bazel area.")
+ LAUNCHER_HOME_GESTURE(574),
@UiEvent(doc = "User's workspace layout information is snapshot in the background.")
LAUNCHER_WORKSPACE_SNAPSHOT(579),
@@ -178,6 +276,7 @@
LAUNCHER_SELECT_MODE_ITEM(584);
// ADD MORE
+
private final int mId;
LauncherEvent(int id) {
@@ -295,19 +394,12 @@
};
}
- protected LogStateProvider mStateProvider;
-
/**
* Creates a new instance of {@link StatsLogManager} based on provided context.
*/
public static StatsLogManager newInstance(Context context) {
- return newInstance(context, null);
- }
-
- public static StatsLogManager newInstance(Context context, LogStateProvider stateProvider) {
StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
context.getApplicationContext(), R.string.stats_log_manager_class);
- mgr.mStateProvider = stateProvider;
return mgr;
}
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index 10d88e5..a5cc7ea 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -6,28 +6,13 @@
import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import java.util.ArrayList;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
-
public class StatsLogUtils {
-
- // Defined in android.stats.launcher.nano
- // As they cannot be linked in this file, defining again.
- public final static int LAUNCHER_STATE_BACKGROUND = 0;
- public final static int LAUNCHER_STATE_HOME = 1;
- public final static int LAUNCHER_STATE_OVERVIEW = 2;
- public final static int LAUNCHER_STATE_ALLAPPS = 3;
-
private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
- public interface LogStateProvider {
- int getCurrentState();
- }
-
/**
* Implemented by containers to provide a container source for a given child.
*/
@@ -61,20 +46,4 @@
}
return null;
}
-
- public static int getContainerTypeFromState(int state) {
- int containerType = DEFAULT_CONTAINERTYPE;
- switch (state) {
- case StatsLogUtils.LAUNCHER_STATE_ALLAPPS:
- containerType = ContainerType.ALLAPPS;
- break;
- case StatsLogUtils.LAUNCHER_STATE_HOME:
- containerType = ContainerType.WORKSPACE;
- break;
- case StatsLogUtils.LAUNCHER_STATE_OVERVIEW:
- containerType = ContainerType.OVERVIEW;
- break;
- }
- return containerType;
- }
}
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 13ab033..629a0ee 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -40,6 +40,7 @@
public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
+ public static final String CONTAINER_OVERVIEW = Integer.toString(ContainerType.OVERVIEW);
public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 25a2c69..ebdfa8c 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -248,10 +248,10 @@
/** Return what's in the src but not in the dest */
private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
Set<String> destIntentSet = new HashSet<>();
- Set<Set<String>> destFolderIntentSet = new HashSet<>();
+ Set<Map<String, Integer>> destFolderIntentSet = new HashSet<>();
for (DbEntry entry : dest) {
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- destFolderIntentSet.add(entry.mFolderItems.keySet());
+ destFolderIntentSet.add(getFolderIntents(entry));
} else {
destIntentSet.add(entry.mIntent);
}
@@ -259,7 +259,7 @@
List<DbEntry> diff = new ArrayList<>();
for (DbEntry entry : src) {
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- if (!destFolderIntentSet.contains(entry.mFolderItems.keySet())) {
+ if (!destFolderIntentSet.contains(getFolderIntents(entry))) {
diff.add(entry);
}
} else {
@@ -271,13 +271,23 @@
return diff;
}
+ private static Map<String, Integer> getFolderIntents(DbEntry entry) {
+ Map<String, Integer> folder = new HashMap<>();
+ for (String intent : entry.mFolderItems.keySet()) {
+ folder.put(intent, entry.mFolderItems.get(intent).size());
+ }
+ return folder;
+ }
+
private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry,
String srcTableName, String destTableName) {
int id = copyEntryAndUpdate(db, context, entry, srcTableName, destTableName);
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- for (int itemId : entry.mFolderItems.values()) {
- copyEntryAndUpdate(db, context, itemId, id, srcTableName, destTableName);
+ for (Set<Integer> itemIds : entry.mFolderItems.values()) {
+ for (int itemId : itemIds) {
+ copyEntryAndUpdate(db, context, itemId, id, srcTableName, destTableName);
+ }
}
}
}
@@ -675,7 +685,10 @@
String intent = c.getString(1);
verifyIntent(intent);
total++;
- entry.mFolderItems.put(intent, id);
+ if (!entry.mFolderItems.containsKey(intent)) {
+ entry.mFolderItems.put(intent, new HashSet<>());
+ }
+ entry.mFolderItems.get(intent).add(id);
} catch (Exception e) {
removeEntryFromDb(mDb, mTableName, IntArray.wrap(c.getInt(0)));
}
@@ -714,7 +727,7 @@
private String mIntent;
private String mProvider;
- private Map<String, Integer> mFolderItems = new HashMap<>();
+ private Map<String, Set<Integer>> mFolderItems = new HashMap<>();
/** Comparator according to the reading order */
@Override
@@ -748,9 +761,5 @@
values.put(LauncherSettings.Favorites.SPANX, spanX);
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
-
- public String getIntentStr() {
- return mIntent;
- }
}
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f2073ef..102ec31 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -59,7 +59,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
-import com.android.launcher3.folder.FolderNameInfo;
+import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
@@ -953,13 +953,12 @@
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
- FolderNameInfo[] suggestionInfos =
- new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfos suggestionInfos = new FolderNameInfos();
FolderInfo info = mBgDataModel.folders.valueAt(i);
if (info.suggestedFolderNames == null) {
provider.getSuggestedFolderName(mApp.getContext(), info.contents,
suggestionInfos);
- info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
+ info.suggestedFolderNames = suggestionInfos;
}
}
}
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 08eb383..05ce06a 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.logger.LauncherAtom.Attribute.EMPTY_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
@@ -29,18 +30,16 @@
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
-import static java.util.Arrays.stream;
-import static java.util.Optional.ofNullable;
-
-import android.content.Intent;
import android.os.Process;
-import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderNameInfo;
+import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.Attribute;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.model.ModelWriter;
@@ -51,8 +50,6 @@
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
-import java.util.Objects;
-import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
@@ -81,11 +78,35 @@
public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
+ /**
+ * Different states of folder label.
+ */
+ public enum LabelState {
+ // Folder's label is not yet assigned( i.e., title == null). Eligible for auto-labeling.
+ UNLABELED(Attribute.UNLABELED),
+
+ // Folder's label is empty(i.e., title == ""). Not eligible for auto-labeling.
+ EMPTY(EMPTY_LABEL),
+
+ // Folder's label is one of the non-empty suggested values.
+ SUGGESTED(SUGGESTED_LABEL),
+
+ // Folder's label is non-empty, manually entered by the user
+ // and different from any of suggested values.
+ MANUAL(MANUAL_LABEL);
+
+ private final LauncherAtom.Attribute mLogAttribute;
+
+ LabelState(Attribute logAttribute) {
+ this.mLogAttribute = logAttribute;
+ }
+ }
+
public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
public int options;
- public Intent suggestedFolderNames;
+ public FolderNameInfos suggestedFolderNames;
/**
* The apps and shortcuts
@@ -183,8 +204,7 @@
@Override
protected String dumpProperties() {
- return super.dumpProperties()
- + " manuallyTypedTitle=" + hasOption(FLAG_MANUAL_FOLDER_NAME);
+ return String.format("%s; labelState=%s", super.dumpProperties(), getLabelState());
}
@Override
@@ -192,14 +212,41 @@
return getDefaultItemInfoBuilder()
.setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
.setRank(rank)
- .setAttribute(hasOption(FLAG_MANUAL_FOLDER_NAME) ? MANUAL_LABEL : SUGGESTED_LABEL)
+ .setAttribute(getLabelState().mLogAttribute)
.setContainerInfo(getContainerInfo())
.build();
}
@Override
- public void setTitle(CharSequence title) {
+ public void setTitle(@Nullable CharSequence title, ModelWriter modelWriter) {
+ // Updating label from null to empty is considered as false touch.
+ // Retaining null title(ie., UNLABELED state) allows auto-labeling when new items added.
+ if (isEmpty(title) && this.title == null) {
+ return;
+ }
+
+ // Updating title to same value does not change any states.
+ if (title != null && title == this.title) {
+ return;
+ }
+
this.title = title;
+ LabelState newLabelState =
+ title == null ? LabelState.UNLABELED
+ : title.length() == 0 ? LabelState.EMPTY :
+ getAcceptedSuggestionIndex().isPresent() ? LabelState.SUGGESTED
+ : LabelState.MANUAL;
+ setOption(FLAG_MANUAL_FOLDER_NAME, newLabelState.equals(LabelState.MANUAL), modelWriter);
+ }
+
+ /**
+ * Returns current state of the current folder label.
+ */
+ public LabelState getLabelState() {
+ return title == null ? LabelState.UNLABELED
+ : title.length() == 0 ? LabelState.EMPTY :
+ hasOption(FLAG_MANUAL_FOLDER_NAME) ? LabelState.MANUAL
+ : LabelState.SUGGESTED;
}
@Override
@@ -224,30 +271,33 @@
public OptionalInt getAcceptedSuggestionIndex() {
String newLabel = checkNotNull(title,
"Expected valid folder label, but found null").toString();
- return getSuggestedLabels()
- .map(suggestionsArray ->
- IntStream.range(0, suggestionsArray.length)
- .filter(
- index -> !isEmpty(suggestionsArray[index])
- && newLabel.equalsIgnoreCase(
- suggestionsArray[index]))
- .sequential()
- .findFirst()
- ).orElse(OptionalInt.empty());
-
+ if (suggestedFolderNames == null || !suggestedFolderNames.hasSuggestions()) {
+ return OptionalInt.empty();
+ }
+ CharSequence[] labels = suggestedFolderNames.getLabels();
+ return IntStream.range(0, labels.length)
+ .filter(index -> !isEmpty(labels[index])
+ && newLabel.equalsIgnoreCase(
+ labels[index].toString()))
+ .sequential()
+ .findFirst();
}
/**
* Returns {@link FromState} based on current {@link #title}.
*/
public LauncherAtom.FromState getFromLabelState() {
- return title == null
- ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
- : title.length() == 0
- ? LauncherAtom.FromState.FROM_EMPTY
- : hasOption(FLAG_MANUAL_FOLDER_NAME)
- ? LauncherAtom.FromState.FROM_CUSTOM
- : LauncherAtom.FromState.FROM_SUGGESTED;
+ switch (getLabelState()){
+ case EMPTY:
+ return LauncherAtom.FromState.FROM_EMPTY;
+ case MANUAL:
+ return LauncherAtom.FromState.FROM_CUSTOM;
+ case SUGGESTED:
+ return LauncherAtom.FromState.FROM_SUGGESTED;
+ case UNLABELED:
+ default:
+ return LauncherAtom.FromState.FROM_STATE_UNSPECIFIED;
+ }
}
/**
@@ -264,19 +314,15 @@
: LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
}
- Optional<String[]> suggestedLabels = getSuggestedLabels();
- boolean isEmptySuggestions = suggestedLabels
- .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
- .orElse(true);
- if (isEmptySuggestions) {
+ // TODO: if suggestedFolderNames is null then it infrastructure issue, not
+ // ranking issue. We should log these appropriately.
+ if (suggestedFolderNames == null || !suggestedFolderNames.hasSuggestions()) {
return title.length() > 0
? LauncherAtom.ToState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS
: LauncherAtom.ToState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
}
- boolean hasValidPrimary = suggestedLabels
- .map(labels -> !isEmpty(labels[0]))
- .orElse(false);
+ boolean hasValidPrimary = suggestedFolderNames != null && suggestedFolderNames.hasPrimary();
if (title.length() == 0) {
return hasValidPrimary ? LauncherAtom.ToState.TO_EMPTY_WITH_VALID_PRIMARY
: LauncherAtom.ToState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
@@ -306,20 +352,6 @@
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
}
- private Optional<String[]> getSuggestedLabels() {
- return ofNullable(suggestedFolderNames)
- .map(folderNames ->
- (FolderNameInfo[])
- folderNames.getParcelableArrayExtra(EXTRA_FOLDER_SUGGESTIONS))
- .map(folderNameInfoArray ->
- stream(folderNameInfoArray)
- .filter(Objects::nonNull)
- .map(FolderNameInfo::getLabel)
- .filter(Objects::nonNull)
- .map(CharSequence::toString)
- .toArray(String[]::new));
- }
-
/**
* Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
*
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 66c3cbb..0d3ddad 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -52,6 +52,7 @@
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
+import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.util.ContentWriter;
import java.util.Optional;
@@ -343,7 +344,7 @@
return itemBuilder;
}
- ContainerInfo getContainerInfo() {
+ protected ContainerInfo getContainerInfo() {
switch (container) {
case CONTAINER_HOTSEAT:
return ContainerInfo.newBuilder()
@@ -405,7 +406,10 @@
return itemInfo;
}
- public void setTitle(CharSequence title) {
+ /**
+ * Sets the title of the item and writes to DB model if needed.
+ */
+ public void setTitle(CharSequence title, ModelWriter modelWriter) {
this.title = title;
}
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index f0e0557..2a4f887 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -35,7 +35,7 @@
| FLAG_HIDE_BACK_BUTTON;
public SpringLoadedState(int id) {
- super(id, ContainerType.OVERVIEW, STATE_FLAGS);
+ super(id, ContainerType.WORKSPACE, STATE_FLAGS);
}
@Override
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index d522d81..51e0819 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -21,9 +21,17 @@
import com.android.launcher3.Utilities;
+import java.util.function.BiConsumer;
+
public final class TestLogging {
+ private static BiConsumer<String, String> sEventConsumer;
+
private static void recordEventSlow(String sequence, String event) {
Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
+ final BiConsumer<String, String> eventConsumer = sEventConsumer;
+ if (eventConsumer != null) {
+ eventConsumer.accept(sequence, event);
+ }
}
public static void recordEvent(String sequence, String event) {
@@ -43,4 +51,8 @@
recordEventSlow(sequence, message + ": " + event);
}
}
+
+ static void setEventConsumer(BiConsumer<String, String> consumer) {
+ sEventConsumer = consumer;
+ }
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 519b1b9..3ca08fd 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -88,6 +88,10 @@
public static final String REQUEST_NATIVE_LEAK = "native-leak";
public static final String REQUEST_VIEW_LEAK = "view-leak";
public static final String REQUEST_RECENT_TASKS_LIST = "recent-tasks-list";
+ public static final String REQUEST_START_EVENT_LOGGING = "start-event-logging";
+ public static final String REQUEST_GET_TEST_EVENTS = "get-test-events";
+ public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
+ public static final String REQUEST_CLEAR_DATA = "clear-data";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 171c5ee..3c78b08 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -21,6 +21,8 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
@@ -42,12 +44,14 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
@@ -298,11 +302,11 @@
public boolean onDrag(float displacement, MotionEvent ev) {
if (!mIsLogContainerSet) {
if (mStartState == ALL_APPS) {
- mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
+ mStartContainerType = ContainerType.ALLAPPS;
} else if (mStartState == NORMAL) {
mStartContainerType = getLogContainerTypeForNormalState(ev);
} else if (mStartState == OVERVIEW) {
- mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
+ mStartContainerType = ContainerType.TASKSWITCHER;
}
mIsLogContainerSet = true;
}
@@ -559,10 +563,22 @@
// Transition complete. log the action
mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
- mStartContainerType,
- mStartState.containerType,
+ mStartContainerType /* e.g., hotseat */,
+ mStartState.containerType /* e.g., workspace */,
targetState.containerType,
mLauncher.getWorkspace().getCurrentPage());
+ mLauncher.getStatsLogManager().logger()
+ .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
+ .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
+ .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
+ .setWorkspace(
+ LauncherAtom.WorkspaceContainer.newBuilder()
+ .setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
+ .build())
+ .log(StatsLogManager.getLauncherAtomEvent(mStartState.containerType,
+ targetState.containerType, mToState.ordinal > mFromState.ordinal
+ ? LAUNCHER_UNKNOWN_SWIPEUP
+ : LAUNCHER_UNKNOWN_SWIPEDOWN));
}
protected void clearState() {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 3d39d25..858e183 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -300,17 +300,9 @@
checkDetectedLeaks(mLauncher);
}
- protected void clearLauncherData() throws IOException, InterruptedException {
- if (TestHelpers.isInLauncherProcess()) {
- LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- resetLoaderState();
- } else {
- clearPackageData(mDevice.getLauncherPackageName());
- mLauncher.enableDebugTracing();
- mLauncherPid = mLauncher.getPid();
- mLauncher.waitForLauncherInitialized();
- }
+ protected void clearLauncherData() {
+ mLauncher.clearLauncherData();
+ mLauncher.waitForLauncherInitialized();
}
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9f4d9ce..5cf5d0f 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -165,9 +165,7 @@
private Consumer<ContainerType> mOnSettledStateAction;
- private static LogEventChecker sEventChecker;
- // True if there is an gesture in progress that needs event verification.
- private static boolean sCheckingEvents;
+ private LogEventChecker mEventChecker;
private boolean mCheckEventsForSuccessfulGestures = false;
private Runnable mOnLauncherCrashed;
@@ -437,15 +435,16 @@
}
private String formatErrorWithEvents(String message, boolean checkEvents) {
- if (sCheckingEvents) {
- sCheckingEvents = false;
+ if (mEventChecker != null) {
+ final LogEventChecker eventChecker = mEventChecker;
+ mEventChecker = null;
if (checkEvents) {
- final String eventMismatch = sEventChecker.verify(0, false);
+ final String eventMismatch = eventChecker.verify(0, false);
if (eventMismatch != null) {
message = message + ", having produced " + eventMismatch;
}
} else {
- sEventChecker.finishNoWait();
+ eventChecker.finishNoWait();
}
}
@@ -1336,13 +1335,16 @@
return tasks;
}
+ public void clearLauncherData() {
+ getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
+ }
+
public Closable eventsCheck() {
- Assert.assertTrue("Nested event checking", !sCheckingEvents);
+ Assert.assertTrue("Nested event checking", mEventChecker == null);
disableSensorRotation();
final Integer initialPid = getPid();
- if (sEventChecker == null) sEventChecker = new LogEventChecker();
- sEventChecker.start();
- sCheckingEvents = true;
+ final LogEventChecker eventChecker = new LogEventChecker(this);
+ if (eventChecker.start()) mEventChecker = eventChecker;
return () -> {
if (initialPid != null && initialPid.intValue() != getPid()) {
@@ -1353,10 +1355,10 @@
formatErrorWithEvents("Launcher crashed", false)));
}
- if (sCheckingEvents) {
- sCheckingEvents = false;
+ if (mEventChecker != null) {
+ mEventChecker = null;
if (mCheckEventsForSuccessfulGestures) {
- final String message = sEventChecker.verify(WAIT_TIME_MS, true);
+ final String message = eventChecker.verify(WAIT_TIME_MS, true);
if (message != null) {
dumpDiagnostics();
checkForAnomaly();
@@ -1364,7 +1366,7 @@
"http://go/tapl : successful gesture produced " + message));
}
} else {
- sEventChecker.finishNoWait();
+ eventChecker.finishNoWait();
}
}
};
@@ -1375,7 +1377,11 @@
}
void expectEvent(String sequence, Pattern expected) {
- if (sCheckingEvents) sEventChecker.expectPattern(sequence, expected);
+ if (mEventChecker != null) {
+ mEventChecker.expectPattern(sequence, expected);
+ } else {
+ Log.d(TAG, "Expecting: " + sequence + " / " + expected);
+ }
}
Rect getVisibleBounds(UiObject2 object) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 79d20ac..4440b82 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -15,169 +15,81 @@
*/
package com.android.launcher3.tapl;
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
-import android.util.Log;
+import android.os.SystemClock;
import com.android.launcher3.testing.TestProtocol;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Semaphore;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * Utility class to read log on a background thread.
+ * Utility class to verify expected events.
*/
public class LogEventChecker {
- private static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
- ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / (?<event>.*)");
-
- private static final String START_PREFIX = "START_READER ";
- private static final String FINISH_PREFIX = "FINISH_READER ";
- private static final String SKIP_EVENTS_TAG = "b/153670015";
-
- private volatile CountDownLatch mFinished;
+ private final LauncherInstrumentation mLauncher;
// Map from an event sequence name to an ordered list of expected events in that sequence.
private final ListMap<Pattern> mExpectedEvents = new ListMap<>();
- private final ListMap<String> mEvents = new ListMap<>();
- private final Semaphore mEventsCounter = new Semaphore(0);
-
- private volatile String mStartCommand;
- private volatile String mFinishCommand;
-
- LogEventChecker() {
- final Thread thread = new Thread(this::onRun, "log-reader-thread");
- thread.setPriority(Thread.NORM_PRIORITY);
- thread.start();
+ LogEventChecker(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
}
- void start() {
- if (mFinished != null) {
- try {
- mFinished.await();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } finally {
- mFinished = null;
- }
- }
- mEvents.clear();
- Log.d(SKIP_EVENTS_TAG, "Cleared events");
+ boolean start() {
mExpectedEvents.clear();
- mEventsCounter.drainPermits();
- final String id = UUID.randomUUID().toString();
- mStartCommand = START_PREFIX + id;
- mFinishCommand = FINISH_PREFIX + id;
- Log.d(SKIP_EVENTS_TAG, "Expected finish command: " + mFinishCommand);
- Log.d(TestProtocol.TAPL_EVENTS_TAG, mStartCommand);
- }
-
- private void onRun() {
- while (true) readEvents();
- }
-
- private void readEvents() {
- try {
- // Note that we use Runtime.exec to start the log reading process instead of running
- // it via UIAutomation, so that we can directly access the "Process" object and
- // ensure that the instrumentation is not stuck forever.
- final String cmd = "logcat -s " + TestProtocol.TAPL_EVENTS_TAG;
-
- final Process logcatProcess = Runtime.getRuntime().exec(cmd);
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(
- logcatProcess.getInputStream()))) {
- while (true) {
- // Skip everything before the next start command.
- for (; ; ) {
- final String event = reader.readLine();
- if (event == null) {
- Log.d(SKIP_EVENTS_TAG, "Read a null line while waiting for start");
- return;
- }
- if (event.contains(mStartCommand)) {
- Log.d(SKIP_EVENTS_TAG, "Read start: " + event);
- break;
- }
- }
-
- // Store all actual events until the finish command.
- for (; ; ) {
- final String event = reader.readLine();
- if (event == null) {
- Log.d(SKIP_EVENTS_TAG, "Read a null line after waiting for start");
- mEventsCounter.drainPermits();
- mEvents.clear();
- return;
- }
- if (event.contains(mFinishCommand)) {
- mFinished.countDown();
- Log.d(SKIP_EVENTS_TAG, "Read finish: " + event);
- break;
- } else {
- final Matcher matcher = EVENT_LOG_ENTRY.matcher(event);
- if (matcher.find()) {
- mEvents.add(matcher.group("sequence"), matcher.group("event"));
- Log.d(SKIP_EVENTS_TAG, "Read event: " + event);
- mEventsCounter.release();
- } else {
- Log.d(SKIP_EVENTS_TAG, "Read something unexpected: " + event);
- }
- }
- }
- }
- } finally {
- logcatProcess.destroyForcibly();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_START_EVENT_LOGGING) != null;
}
void expectPattern(String sequence, Pattern pattern) {
mExpectedEvents.add(sequence, pattern);
}
- private void finishSync(long waitForExpectedCountMs) {
- try {
- // Wait until Launcher generates the expected number of events.
- int expectedCount = mExpectedEvents.entrySet()
+ // Waits for the expected number of events and returns them.
+ private ListMap<String> finishSync(long waitForExpectedCountMs) {
+ final long startTime = SystemClock.uptimeMillis();
+ // Event strings with '/' separating the sequence and the event.
+ ArrayList<String> rawEvents;
+
+ while (true) {
+ rawEvents = mLauncher.getTestInfo(TestProtocol.REQUEST_GET_TEST_EVENTS)
+ .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final int expectedCount = mExpectedEvents.entrySet()
.stream().mapToInt(e -> e.getValue().size()).sum();
- mEventsCounter.tryAcquire(expectedCount, waitForExpectedCountMs, MILLISECONDS);
- finishNoWait();
- mFinished.await();
- mFinished = null;
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
+ if (rawEvents.size() >= expectedCount
+ || SystemClock.uptimeMillis() > startTime + waitForExpectedCountMs) {
+ break;
+ }
+ SystemClock.sleep(100);
}
+
+ finishNoWait();
+
+ // Parse raw events into a map.
+ final ListMap<String> eventSequences = new ListMap<>();
+ for (String rawEvent : rawEvents) {
+ final String[] split = rawEvent.split("/");
+ eventSequences.add(split[0], split[1]);
+ }
+ return eventSequences;
}
void finishNoWait() {
- mFinished = new CountDownLatch(1);
- Log.d(TestProtocol.TAPL_EVENTS_TAG, mFinishCommand);
+ mLauncher.getTestInfo(TestProtocol.REQUEST_STOP_EVENT_LOGGING);
}
String verify(long waitForExpectedCountMs, boolean successfulGesture) {
- finishSync(waitForExpectedCountMs);
+ final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
final StringBuilder sb = new StringBuilder();
boolean hasMismatches = false;
for (Map.Entry<String, List<Pattern>> expectedEvents : mExpectedEvents.entrySet()) {
String sequence = expectedEvents.getKey();
- List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
- Log.d(SKIP_EVENTS_TAG, "Verifying events");
+ List<String> actual = new ArrayList<>(actualEvents.getNonNull(sequence));
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
hasMismatches = hasMismatches
|| mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
@@ -189,7 +101,7 @@
mismatchPosition);
}
// Check for unexpected event sequences in the actual data.
- for (String actualNamedSequence : mEvents.keySet()) {
+ for (String actualNamedSequence : actualEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
hasMismatches = hasMismatches
|| !ignoreMistatch(successfulGesture, actualNamedSequence);
@@ -197,7 +109,7 @@
sb,
actualNamedSequence,
new ArrayList<>(),
- mEvents.get(actualNamedSequence),
+ actualEvents.get(actualNamedSequence),
0);
}
}