Merge "Disables Back gesture while sandbox is running." into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index fc99880..cb695df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,7 +32,7 @@
}
java_library_static {
- name: "launcher-log-protos-lite",
+ name: "launcher_log_protos_lite",
srcs: [
"protos/*.proto",
"proto_overrides/*.proto",
@@ -45,4 +45,5 @@
"proto_overrides",
],
},
+ static_libs: ["libprotobuf-java-lite"],
}
diff --git a/Android.mk b/Android.mk
index 66ccae0..c066a12 100644
--- a/Android.mk
+++ b/Android.mk
@@ -48,7 +48,9 @@
androidx.preference_preference \
iconloader_base
-LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ LauncherPluginLib \
+ launcher_log_protos_lite
LOCAL_SRC_FILES := \
$(call all-proto-files-under, protos) \
@@ -144,7 +146,10 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUISharedLib \
+ launcherprotosnano \
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -213,7 +218,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUISharedLib \
+ launcherprotosnano \
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 26698eb..d747bb8 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!--
diff --git a/proguard.flags b/proguard.flags
index 01302cf..e556c94 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -48,3 +48,6 @@
-dontwarn android.view.**
-dontwarn android.os.**
-dontwarn android.graphics.**
+
+# Ignore warnings for hidden utility classes referenced from the shared lib
+-dontwarn com.android.internal.util.**
\ No newline at end of file
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 0560d68..ec1d55b 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -58,6 +58,35 @@
optional TipType tip_type = 17;
optional int32 search_query_length = 18;
optional bool is_work_app = 19;
+ optional FromFolderLabelState from_folder_label_state = 20;
+ optional ToFolderLabelState to_folder_label_state = 21;
+
+ // Note: proto does not support duplicate enum values, even if they belong to different enum type.
+ // Hence "FROM" and "TO" prefix added.
+ enum FromFolderLabelState{
+ FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+ FROM_EMPTY = 1;
+ FROM_CUSTOM = 2;
+ FROM_SUGGESTED = 3;
+ }
+
+ enum ToFolderLabelState{
+ TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+ TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
+ TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
+ TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
+ TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
+ TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
+ TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
+ TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
+ TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
+ TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
+ TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
+ TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
+ TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
+ TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
+ UNCHANGED = 14;
+ }
}
// Used to define what type of item a Target would represent.
@@ -120,6 +149,8 @@
BACK_GESTURE = 19;
UNDO = 20;
DISMISS_PREDICTION = 21;
+ HYBRID_HOTSEAT_ACCEPTED = 22;
+ HYBRID_HOTSEAT_CANCELED = 23;
}
enum TipType {
@@ -129,6 +160,7 @@
QUICK_SCRUB_TEXT = 3;
PREDICTION_TEXT = 4;
DWB_TOAST = 5;
+ HYBRID_HOTSEAT = 6;
}
// Used to define the action component of the LauncherEvent.
@@ -138,7 +170,8 @@
AUTOMATED = 1;
COMMAND = 2;
TIP = 3;
- // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
+ SOFT_KEYBOARD = 4;
+ // HARD_KEYBOARD, ASSIST
}
enum Touch {
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 5d871c3..d3cec28 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index f82af62..834e6cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -296,7 +296,7 @@
predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
predictedApps.add(predictedApp);
} else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 4c87945..00e72b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.hybridhotseat;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
+
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Configuration;
@@ -32,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.AbstractSlideInView;
@@ -95,6 +99,7 @@
handleClose(true);
mHotseatEduController.migrate();
mHotseatEduController.finishOnboarding();
+ logUserAction(true);
Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
}
@@ -102,6 +107,7 @@
if (mHotseatEduController == null) return;
Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
mHotseatEduController.finishOnboarding();
+ logUserAction(false);
handleClose(true);
}
@@ -133,7 +139,28 @@
mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
}
+ private void logUserAction(boolean migrated) {
+ LauncherLogProto.Action action = new LauncherLogProto.Action();
+ LauncherLogProto.Target target = new LauncherLogProto.Target();
+ action.type = LauncherLogProto.Action.Type.TOUCH;
+ action.touch = LauncherLogProto.Action.Touch.TAP;
+ target.containerType = LauncherLogProto.ContainerType.TIP;
+ target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
+ target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
+ : HYBRID_HOTSEAT_CANCELED;
+ LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+ UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
+ }
+ private void logOnBoardingSeen() {
+ LauncherLogProto.Action action = new LauncherLogProto.Action();
+ LauncherLogProto.Target target = new LauncherLogProto.Target();
+ action.type = LauncherLogProto.Action.Type.TIP;
+ target.containerType = LauncherLogProto.ContainerType.TIP;
+ target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
+ LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+ UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
+ }
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@@ -165,6 +192,7 @@
return;
}
mLauncher.getDragLayer().addView(this);
+ logOnBoardingSeen();
animateOpen();
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
WorkspaceItemInfo info = predictions.get(i);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index b2e1798..4bbb48c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -86,6 +86,7 @@
mIsDrawingDot = true;
int count = canvas.save();
canvas.translate(-getWidth() * RING_EFFECT_RATIO, -getHeight() * RING_EFFECT_RATIO);
+ canvas.scale(1 + 2 * RING_EFFECT_RATIO, 1 + 2 * RING_EFFECT_RATIO);
super.drawDotIfNecessary(canvas);
canvas.restoreToCount(count);
mIsDrawingDot = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 28fc3da..1b6d291 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -234,6 +234,7 @@
public void adjustActivityControllerInterpolators() {
if (mAdjustInterpolatorsRunnable != null) {
mAdjustInterpolatorsRunnable.run();
+ mAdjustInterpolatorsRunnable = null;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 482348f..345a147 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -917,6 +917,12 @@
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
// Finalize the state and notify of the change
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
@@ -938,6 +944,12 @@
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
if (target == NEW_TASK && mRecentsView != null
&& mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
// We are about to launch the current running task, so use LAST_TASK state
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 b0cb35a..3364b66 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -142,7 +142,7 @@
@BinderThread
@Override
public void onOverviewToggle() {
- TestLogging.recordEvent("onOverviewToggle");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
mOverviewCommandHelper.onOverviewToggle();
}
@@ -422,13 +422,17 @@
Log.e(TAG, "Unknown event " + ev);
return;
}
+ MotionEvent event = (MotionEvent) ev;
+
+ TestLogging.recordMotionEvent(
+ TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
+
if (!mDeviceState.isUserUnlocked()) {
return;
}
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
- MotionEvent event = (MotionEvent) ev;
if (event.getAction() == ACTION_DOWN) {
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
@@ -709,20 +713,15 @@
}
} else {
// Dump everything
+ FeatureFlags.dump(pw);
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
mDeviceState.dump(pw);
+ mOverviewComponentObserver.dump(pw);
pw.println("TouchState:");
boolean resumed = mOverviewComponentObserver != null
&& mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" resumed=" + resumed);
pw.println(" mConsumer=" + mConsumer.getName());
- pw.println("FeatureFlags:");
- pw.println(" APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
- pw.println(" QUICKSTEP_SPRINGS=" + QUICKSTEP_SPRINGS.get());
- pw.println(" UNSTABLE_SPRINGS=" + UNSTABLE_SPRINGS.get());
- pw.println(" ADAPTIVE_ICON_WINDOW_ANIM=" + ADAPTIVE_ICON_WINDOW_ANIM.get());
- pw.println(" ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
- pw.println(" ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
- pw.println(" FAKE_LANDSCAPE_UI=" + FAKE_LANDSCAPE_UI.get());
ActiveGestureLog.INSTANCE.dump("", pw);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 8ae4f06..05c206f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -3,6 +3,7 @@
import android.view.MotionEvent;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -35,7 +36,7 @@
protected void setActive(MotionEvent ev) {
mState = STATE_ACTIVE;
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
// Send cancel event
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index d01e1a4..ba1d38c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -38,6 +38,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
@@ -203,7 +204,7 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitorCompat.pilferPointers();
Intent intent = new Intent(Intent.ACTION_MAIN)
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 1b0e05a..3ee3c2d 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
@@ -45,6 +45,7 @@
import com.android.launcher3.R;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.BaseActivityInterface;
@@ -303,7 +304,7 @@
if (mInteractionHandler == null) {
return;
}
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitorCompat.pilferPointers();
mActivityInterface.closeOverlay();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 32a67ca..f161cc0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -26,6 +26,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
@@ -107,7 +108,7 @@
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
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 6bfc3fd..823b254 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
@@ -23,6 +23,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.GestureState;
@@ -64,7 +65,7 @@
private void onInterceptTouch() {
if (mInputMonitor != null) {
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
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 321af6c..ef1698e 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
@@ -1251,7 +1251,7 @@
}
public PendingAnimation createAllTasksDismissAnimation(long duration) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
AnimatorSet anim = new AnimatorSet();
@@ -1595,7 +1595,7 @@
}
public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 49f667e..8ed1392 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -54,6 +54,7 @@
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ConfigurationCompat;
/**
* A task in the Recents view.
@@ -357,7 +358,8 @@
final float thumbnailScale;
int thumbnailRotation = mThumbnailData.rotation;
- int currentRotation = getDisplay() != null ? getDisplay().getRotation() : 0;
+ int currentRotation = ConfigurationCompat.getWindowConfigurationRotation(
+ getResources().getConfiguration());
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
// Landscape vs portrait change
boolean windowingModeSupportsRotation = !mActivity.isInMultiWindowMode()
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 294bb7b..8b7ce10 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
@@ -58,6 +58,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
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;
@@ -334,9 +335,8 @@
Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
if (mTask != null) {
final ActivityOptions opts;
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("startActivityFromRecentsAsync:" + mTask);
- }
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
if (animate) {
opts = mActivity.getActivityLaunchOptions(this);
if (freezeTaskList) {
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 5f81e1f..45a62ab 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -67,13 +67,13 @@
<string name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
<!-- Hotseat migration notification title -->
- <string translatable="false" name="hotseat_migrate_prompt_title">Get suggested apps on the home screen</string>
+ <string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
<!-- Hotseat migration notification content -->
- <string translatable="false" name="hotseat_migrate_prompt_content">Tap to set up</string>
+ <string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
<!-- Hotseat migration wizard title -->
<string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
<!-- Hotseat migration wizard message -->
- <string translatable="false" name="hotseat_migrate_message">To pin a favorite app, drag it over a suggested app. To hide a suggested app, touch & hold it.</string>
+ <string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 94ff63b..15503b8 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -167,7 +167,7 @@
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if (requestCode != -1) {
- mPendingActivityRequestCode = -1;
+ mPendingActivityRequestCode = requestCode;
StartActivityParams params = new StartActivityParams(this, requestCode);
params.intent = intent;
params.options = options;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DisplayRotationListener.java b/quickstep/src/com/android/launcher3/uioverrides/DisplayRotationListener.java
deleted file mode 100644
index 2d9a161..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/DisplayRotationListener.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2018 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.uioverrides;
-
-import android.content.Context;
-import android.os.Handler;
-
-import com.android.systemui.shared.system.RotationWatcher;
-
-/**
- * Utility class for listening for rotation changes
- */
-public class DisplayRotationListener extends RotationWatcher {
-
- private final Runnable mCallback;
- private Handler mHandler;
-
- public DisplayRotationListener(Context context, Runnable callback) {
- super(context);
- mCallback = callback;
- }
-
- @Override
- public void enable() {
- if (mHandler == null) {
- mHandler = new Handler();
- }
- super.enable();
- }
-
- @Override
- protected void onRotationChanged(int i) {
- mHandler.post(mCallback);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 6e7c087..2e422b7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -14,8 +14,12 @@
package com.android.launcher3.uioverrides.plugins;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
@@ -24,6 +28,9 @@
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
public class PluginManagerWrapper {
@@ -75,4 +82,27 @@
public static boolean hasPlugins(Context context) {
return PluginPrefs.hasPlugins(context);
}
+
+ public void dump(PrintWriter pw) {
+ final List<ComponentName> enabledPlugins = new ArrayList<>();
+ final List<ComponentName> disabledPlugins = new ArrayList<>();
+ for (String action : getPluginActions()) {
+ for (ResolveInfo resolveInfo : mContext.getPackageManager().queryIntentServices(
+ new Intent(action), MATCH_DISABLED_COMPONENTS)) {
+ ComponentName installedPlugin = new ComponentName(
+ resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
+ if (mPluginEnabler.isEnabled(installedPlugin)) {
+ enabledPlugins.add(installedPlugin);
+ } else {
+ disabledPlugins.add(installedPlugin);
+ }
+ }
+ }
+
+ pw.println("PluginManager:");
+ pw.println(" numEnabledPlugins=" + enabledPlugins.size());
+ pw.println(" numDisabledPlugins=" + disabledPlugins.size());
+ pw.println(" enabledPlugins=" + enabledPlugins);
+ pw.println(" disabledPlugins=" + disabledPlugins);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 6c65e01..b3875ae 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -130,7 +130,7 @@
final LinkedList<Runnable> callbacks;
if (mCallbacks.indexOfKey(stateMask) >= 0) {
callbacks = mCallbacks.get(stateMask);
- if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
+ if (FeatureFlags.IS_STUDIO_BUILD && callbacks.contains(callback)) {
throw new IllegalStateException("Existing callback for state found");
}
} else {
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 73b78db..85ef4c6 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -35,6 +35,7 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
@@ -233,4 +234,13 @@
public BaseActivityInterface getActivityInterface() {
return mActivityInterface;
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("OverviewComponentObserver:");
+ pw.println(" isDefaultHome=" + mIsDefaultHome);
+ pw.println(" isHomeDisabled=" + mIsHomeDisabled);
+ pw.println(" homeAndOverviewSame=" + mIsHomeAndOverviewSame);
+ pw.println(" overviewIntent=" + mOverviewIntent);
+ pw.println(" homeIntent=" + mCurrentHomeIntent);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index acf61b4..0f98b32 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -62,6 +62,12 @@
mListeners.remove(listener);
}
+ @UiThread
+ public void removeAllListeners() {
+ Preconditions.assertUIThread();
+ mListeners.clear();
+ }
+
public void notifyAnimationCanceled() {
mCancelled = true;
onAnimationCanceled(null);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 46af8bf..00adfbe 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -201,6 +201,7 @@
if (mInputConsumerController != null) {
mInputConsumerController.setInputListener(null);
}
+ mInputProxySupplier = null;
}
private boolean onInputConsumerEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index d0afb21..4b33d21 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -180,7 +180,7 @@
mDefaultDisplay.addChangeListener(this);
}
- if (mMode == NO_BUTTON) {
+ if (newMode == NO_BUTTON) {
mExclusionListener.register();
} else {
mExclusionListener.unregister();
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index e3e8ace..6902e37 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -55,7 +55,7 @@
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalArgumentException(msg);
} else {
Log.e("TaskAnimationManager", msg, new Exception());
@@ -156,9 +156,9 @@
mTargets.release();
}
- // Remove gesture state from callbacks
- if (mCallbacks != null && mLastGestureState != null) {
- mCallbacks.removeListener(mLastGestureState);
+ // Clean up all listeners to ensure we don't get subsequent callbacks
+ if (mCallbacks != null) {
+ mCallbacks.removeAllListeners();
}
mController = null;
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
index 32d0d53..cb04e5b 100644
--- a/quickstep/src/com/android/quickstep/util/BinderTracker.java
+++ b/quickstep/src/com/android/quickstep/util/BinderTracker.java
@@ -31,7 +31,7 @@
private static final String TAG = "BinderTracker";
public static void start() {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
@@ -40,7 +40,7 @@
}
public static void stop() {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 9cf55b3..1d8a79f 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -84,6 +84,7 @@
float taskWidth, taskHeight, paddingHorz;
Resources res = context.getResources();
Rect insets = dp.getInsets();
+ final boolean overviewActionsEnabled = ENABLE_OVERVIEW_ACTIONS.get();
if (dp.isMultiWindowMode) {
if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
@@ -113,7 +114,7 @@
final int paddingResId;
if (dp.isVerticalBarLayout()) {
paddingResId = R.dimen.landscape_task_card_horz_space;
- } else if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
+ } else if (overviewActionsEnabled && removeShelfFromOverview(context)) {
paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
} else {
paddingResId = R.dimen.portrait_task_card_horz_space;
@@ -121,9 +122,11 @@
paddingHorz = res.getDimension(paddingResId);
}
- float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+ float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
float bottomMargin = thumbnailBottomMargin(context);
- float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
+
+ float paddingVert = overviewActionsEnabled && removeShelfFromOverview(context)
+ ? 0 : res.getDimension(R.dimen.task_card_vert_space);
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
// we override the insets ourselves.
@@ -141,7 +144,7 @@
// Center in the visible space
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
float y = insets.top + Math.max(topIconMargin,
- (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+ (launcherVisibleHeight - extraVerticalSpace - outHeight - bottomMargin) / 2);
outRect.set(Math.round(x), Math.round(y),
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index d2f5d8f..724af66 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -25,7 +25,6 @@
import static org.junit.Assert.assertTrue;
import android.content.Intent;
-import android.os.RemoteException;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,7 +47,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -84,17 +82,6 @@
}
@Test
- @PortraitLandscape
- @Ignore // Enable after b/131115533
- public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
- mDevice.pressRecentApps();
- waitForState("Launcher internal state didn't switch to Overview",
- () -> LauncherState.OVERVIEW);
-
- assertNotNull("getOverview() returned null", mLauncher.getOverview());
- }
-
- @Test
@NavigationModeSwitch
@PortraitLandscape
public void testWorkspaceSwitchToAllApps() {
@@ -271,10 +258,8 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ // b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake.
@TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
- // b/143285809
- @Ignore
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 5607e78..33502d0 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -23,14 +23,14 @@
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/work_apps_paused_title"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:tint="?attr/folderTextColor"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:tint="?attr/workProfileOverlayTextColor"
android:src="@drawable/ic_corp_off" />
<TextView
style="@style/TextHeadline"
- android:textColor="?attr/folderTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -43,7 +43,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?attr/folderTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_paused_body"
android:textAlignment="center"
android:textSize="16sp" />
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index db95416..2cffedd 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -32,6 +32,8 @@
android:layout_weight="1"
android:drawableStart="@drawable/ic_corp"
android:drawablePadding="3dp"
+ android:drawableTint="?attr/workProfileOverlayTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index aef878b..5a15ec6 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -41,6 +41,7 @@
<attr name="folderIconBorderColor" format="color" />
<attr name="folderTextColor" format="color" />
<attr name="folderHintColor" format="color" />
+ <attr name="workProfileOverlayTextColor" format="color" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bde3c31..0775c0c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -142,7 +142,7 @@
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
<!-- Default folder title -->
- <string name="folder_hint_text">Tap to edit</string>
+ <string name="folder_hint_text">Edit Name</string>
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1174a2f..cee268b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,6 +50,7 @@
<item name="folderTextColor">#FF212121</item>
<item name="folderHintColor">#FF616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
+ <item name="workProfileOverlayTextColor">#FF212121</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -77,6 +78,7 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
+
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -100,6 +102,7 @@
<item name="folderHintColor">#FFCCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
+ <item name="workProfileOverlayTextColor">@android:color/white</item>
</style>
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index f769055..74ef55e 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.folder;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
import android.content.Context;
@@ -61,15 +61,16 @@
ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
list.add(mItem1);
list.add(mItem2);
- String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
- new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
- assertTrue(suggestedNameOut[0].equals("Work"));
+ FolderNameInfo[] nameInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+ assertEquals("Work", nameInfos[0].getLabel());
- suggestedNameOut[0] = "candidate1";
- suggestedNameOut[1] = "candidate2";
- suggestedNameOut[2] = "candidate3";
- new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
- assertTrue(suggestedNameOut[3].equals("Work"));
+ nameInfos[0] = new FolderNameInfo("candidate1", 0.9);
+ nameInfos[1] = new FolderNameInfo("candidate2", 0.8);
+ nameInfos[2] = new FolderNameInfo("candidate3", 0.7);
+ new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+ assertEquals("Work", nameInfos[3].getLabel());
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index 48b5a45..95a4146 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -49,8 +49,9 @@
@After
public void tearDown() {
// Clear existing logs
- new File(mTempDir, "log-0").delete();
- new File(mTempDir, "log-1").delete();
+ for (int i = 0; i < FileLog.LOG_DAYS; i++) {
+ new File(mTempDir, "log-" + i).delete();
+ }
mTempDir.delete();
mTestActive = false;
@@ -89,8 +90,9 @@
Calendar threeDaysAgo = Calendar.getInstance();
threeDaysAgo.add(Calendar.HOUR, -72);
- new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
- new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
+ for (int i = 0; i < FileLog.LOG_DAYS; i++) {
+ new File(mTempDir, "log-" + i).setLastModified(threeDaysAgo.getTimeInMillis());
+ }
FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
writer = new StringWriter();
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 50f9494..7072adf 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -134,7 +134,7 @@
{ APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
}}, 2, OLD_WORK_PROFILE_ID);
// simulates the creation of backup upon restore
- new GridBackupTable(RuntimeEnvironment.application, mDb, mDb, mIdp.numHotseatIcons,
+ new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numHotseatIcons,
mIdp.numColumns, mIdp.numRows).doBackup(
MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
// reset favorites table
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
index 36fd96b..f46b849 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
@@ -63,7 +63,7 @@
@Test
public void backupTableCreated() {
- GridBackupTable backupTable = new GridBackupTable(mContext, mDb, mDb, 4, 4, 4);
+ GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
assertFalse(backupTable.backupOrRestoreAsNeeded());
Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
@@ -75,14 +75,14 @@
@Test
public void backupTableRestored() {
- assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
// Delete entries
mDb.delete(TABLE_NAME, null, null);
assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
- GridBackupTable backupTable = new GridBackupTable(mContext, mDb, mDb, 3, 3, 3);
+ GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
assertTrue(backupTable.backupOrRestoreAsNeeded());
// Items have been restored
@@ -96,7 +96,7 @@
@Test
public void backupTableRemovedOnAdd() {
- assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
@@ -107,7 +107,7 @@
@Test
public void backupTableRemovedOnDelete() {
- assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
@@ -118,7 +118,7 @@
@Test
public void backupTableRetainedOnUpdate() {
- assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+ assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index f3c5191..217a41c 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -42,6 +42,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
@@ -330,9 +331,7 @@
return;
}
try {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("start: shortcut: " + packageName);
- }
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: shortcut", packageName);
getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
startActivityOptions, user);
} catch (SecurityException | IllegalStateException e) {
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index dda38b3..9f3b48f 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
+
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@@ -37,9 +39,12 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DefaultDisplay.DisplayInfoChangeListener;
+import com.android.launcher3.util.DefaultDisplay.Info;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
@@ -48,7 +53,7 @@
* Extension of BaseActivity allowing support for drag-n-drop
*/
public abstract class BaseDraggingActivity extends BaseActivity
- implements WallpaperColorInfo.OnChangeListener {
+ implements WallpaperColorInfo.OnChangeListener, DisplayInfoChangeListener {
private static final String TAG = "BaseDraggingActivity";
@@ -63,8 +68,6 @@
private int mThemeRes = R.style.AppTheme;
- private DisplayRotationListener mRotationListener;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -72,7 +75,7 @@
mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
+ DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
// Update theme
WallpaperColorInfo.INSTANCE.get(this).addOnChangeListener(this);
@@ -167,9 +170,7 @@
startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("start: activity: " + intent);
- }
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: activity", intent);
startActivity(intent, optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
@@ -238,7 +239,7 @@
protected void onDestroy() {
super.onDestroy();
WallpaperColorInfo.INSTANCE.get(this).removeOnChangeListener(this);
- mRotationListener.disable();
+ DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
}
public void runOnceOnStart(Runnable action) {
@@ -251,15 +252,13 @@
protected void onDeviceProfileInitiated() {
if (mDeviceProfile.isVerticalBarLayout()) {
- mRotationListener.enable();
mDeviceProfile.updateIsSeascape(this);
- } else {
- mRotationListener.disable();
}
}
- private void onDeviceRotationChanged() {
- if (mDeviceProfile.updateIsSeascape(this)) {
+ @Override
+ public void onDisplayInfoChanged(Info info, int flags) {
+ if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.updateIsSeascape(this)) {
reapplyUi();
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ac61c49..8718820 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -538,7 +538,7 @@
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 466b7b2..f07040d 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -96,7 +96,7 @@
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 787eee1..336e423 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -48,6 +48,8 @@
public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
+ public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
+
public int options;
public Intent suggestedFolderNames;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 89c3b93..3a275ff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1789,17 +1789,13 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("Key event: " + event);
- }
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event);
return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS && ev.getAction() != MotionEvent.ACTION_MOVE) {
- TestLogging.recordEvent("Touch event: " + ev);
- }
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@@ -2148,7 +2144,7 @@
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4cd038d..d77285d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -91,7 +91,7 @@
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index bb6c330..f618fe1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
-import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -55,7 +55,6 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
@@ -67,7 +66,6 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -128,16 +126,6 @@
}
/**
- * Returns AppInfo with corresponding package name.
- * TODO: move to enqueueModelTask
- */
- public Optional<AppInfo> getAppInfoByPackageName(String pkg) {
- return mBgAllAppsList.data.stream()
- .filter(info -> info.componentName.getPackageName().equals(pkg))
- .findAny();
- }
-
- /**
* Adds the provided items to the workspace.
*/
public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
@@ -250,7 +238,7 @@
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
- } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+ } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
for (Callbacks cb : getCallbacks()) {
if (cb instanceof Launcher) {
((Launcher) cb).recreate();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5544240..697048a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -119,7 +119,7 @@
@Override
public boolean onCreate() {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Launcher process started");
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ae4eae9..4c6e1f3 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1502,7 +1502,7 @@
return false;
}
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
duration *= Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 1);
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b7f8547..a8f492f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1858,7 +1858,7 @@
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2153,7 +2153,7 @@
ItemInfo item = d.dragInfo;
if (item == null) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -2775,7 +2775,7 @@
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
}
@@ -2794,7 +2794,7 @@
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 56bd1b6..4e5b031 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -153,6 +153,11 @@
return mMultiValueAlpha.getProperty(index);
}
+ public WorkFooterContainer getWorkFooterContainer() {
+ return mWorkFooterContainer;
+ }
+
+
@Override
protected void setDampedScrollShift(float shift) {
// Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index cc33965..6413044 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.DeviceFlag;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -42,7 +43,10 @@
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
}
- public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG;
+ /**
+ * True when the build has come from Android Studio and is being used for local debugging.
+ */
+ public static final boolean IS_STUDIO_BUILD = BuildConfig.DEBUG;
/**
* Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
@@ -90,13 +94,13 @@
"FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
public static final BooleanFlag FOLDER_NAME_SUGGEST = getDebugFlag(
- "FOLDER_NAME_SUGGEST", false, "Suggests folder names instead of blank text.");
+ "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text.");
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
- public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
+ public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
"ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
@@ -106,7 +110,7 @@
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
- public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
+ public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
@@ -145,6 +149,15 @@
}
}
+ public static void dump(PrintWriter pw) {
+ pw.println("FeatureFlags:");
+ synchronized (sDebugFlags) {
+ for (DebugFlag flag : sDebugFlags) {
+ pw.println(" " + flag.key + "=" + flag.get());
+ }
+ }
+ }
+
public static class BooleanFlag {
public final String key;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 1b7b015..de7bc6d 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -160,8 +160,8 @@
mOptions = options;
if (mOptions.systemDndStartPoint != null) {
- mMotionDownX = mOptions.systemDndStartPoint.x;
- mMotionDownY = mOptions.systemDndStartPoint.y;
+ mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
+ mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
}
final int registrationX = mMotionDownX - dragLayerX;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 8e80916..bbe6cbf 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -16,9 +16,23 @@
package com.android.launcher3.folder;
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
+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.asList;
+import static java.util.Arrays.stream;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -75,20 +89,24 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-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.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Action;
+import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.LauncherLogProto.ItemType;
+import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -186,6 +204,9 @@
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
+ private String mPreviousLabel;
+ private boolean mIsPreviousLabelSuggested;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -300,13 +321,13 @@
public void startEditingFolderName() {
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- if (TextUtils.isEmpty(mFolderName.getText())) {
- String[] suggestedNames =
- mInfo.suggestedFolderNames.getStringArrayExtra("suggest");
- mFolderName.setText(suggestedNames[0]);
- mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
- suggestedNames.length));
- mFolderName.setEnteredCompose(false);
+ if (isEmpty(mFolderName.getText())) {
+ FolderNameInfo[] nameInfos =
+ (FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
+ FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
+ if (nameInfos != null) {
+ showLabelSuggestion(nameInfos);
+ }
}
}
mFolderName.setHint("");
@@ -324,23 +345,16 @@
}
mInfo.title = newTitle;
- mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
+ mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mFolderName.setText(mInfo.title);
- // TODO: depending on whether the title was manually edited or automatically
- // suggested, apply different hint.
- mFolderName.setHint("");
+ if (TextUtils.isEmpty(mInfo.title)) {
+ mFolderName.setHint(R.string.folder_hint_text);
+ mFolderName.setText("");
} else {
- if (TextUtils.isEmpty(mInfo.title)) {
- mFolderName.setHint(R.string.folder_hint_text);
- mFolderName.setText("");
- } else {
- mFolderName.setHint(null);
- }
+ mFolderName.setHint(null);
}
sendCustomAccessibilityEvent(
@@ -423,8 +437,10 @@
mItemsInvalidated = true;
mInfo.addListener(this);
- if (!TextUtils.isEmpty(mInfo.title)) {
+ if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
+ mPreviousLabel = mInfo.title.toString();
+ mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
@@ -443,27 +459,53 @@
}
/**
- * Show suggested folder title.
+ * Show suggested folder title in FolderEditText, push InputMethodManager suggestions and save
+ * the suggestedFolderNames.
*/
- public void showSuggestedTitle(String[] suggestName) {
+ public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mInfo.suggestedFolderNames = new Intent().putExtra("suggest", suggestName);
- if (TextUtils.isEmpty(mFolderName.getText().toString())
- && !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
- if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
- mFolderName.setHint("");
- mFolderName.setText(suggestName[0]);
- mInfo.title = suggestName[0];
- animateOpen(mInfo.contents, 0, true);
- mFolderName.showKeyboard();
- mFolderName.displayCompletions(
- Arrays.asList(suggestName).subList(1, suggestName.length));
- }
+ mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
+ nameInfos);
+ if (isEmpty(mFolderName.getText().toString())
+ && !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
+ showLabelSuggestion(nameInfos);
}
}
}
/**
+ * Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
+ * InputMethodManager suggestions.
+ */
+ private void showLabelSuggestion(FolderNameInfo[] nameInfos) {
+ if (nameInfos == 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());
+ CharSequence firstLabel = nameInfos[0].getLabel();
+
+ if (shouldOpen) {
+ if (!isEmpty(firstLabel)) {
+ mFolderName.setHint("");
+ mFolderName.setText(firstLabel);
+ mInfo.title = firstLabel;
+ }
+ animateOpen(mInfo.contents, 0, true);
+ mFolderName.showKeyboard();
+ mFolderName.displayCompletions(
+ asList(nameInfos).subList(1, nameInfos.length).stream()
+ .filter(Objects::nonNull)
+ .map(s -> s.getLabel().toString())
+ .collect(Collectors.toList()));
+ }
+ }
+
+ /**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
* @param launcher The main activity.
@@ -580,7 +622,7 @@
dragLayer.addView(this);
mDragController.addDropTarget(this);
} else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ getParent());
}
@@ -608,9 +650,9 @@
if (!skipUserEventLog) {
mLauncher.getUserEventDispatcher().logActionOnItem(
- Touch.TAP,
- Direction.NONE,
- ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
+ LauncherLogProto.Action.Touch.TAP,
+ LauncherLogProto.Action.Direction.NONE,
+ LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
}
@@ -1392,6 +1434,7 @@
if (hasFocus) {
startEditingFolderName();
} else {
+ logEditFolderLabel();
mFolderName.dispatchBackKey();
}
}
@@ -1405,11 +1448,12 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
+ public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+ LauncherLogProto.Target targetParent) {
target.gridX = info.cellX;
target.gridY = info.cellY;
target.pageIndex = mContent.getCurrentPage();
- targetParent.containerType = ContainerType.FOLDER;
+ targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
}
private class OnScrollHintListener implements OnAlarmListener {
@@ -1507,7 +1551,7 @@
@Override
public int getLogContainerType() {
- return ContainerType.FOLDER;
+ return LauncherLogProto.ContainerType.FOLDER;
}
/**
@@ -1542,7 +1586,7 @@
}
} else {
mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(ContainerType.FOLDER));
+ LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
close(true);
return true;
}
@@ -1572,4 +1616,112 @@
super.draw(canvas);
}
}
+
+ private void logEditFolderLabel() {
+ LauncherEvent launcherEvent = LauncherEvent.newBuilder()
+ .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
+ .addSrcTarget(newEditTextTargetBuilder()
+ .setFromFolderLabelState(getFromFolderLabelState())
+ .setToFolderLabelState(getToFolderLabelState()))
+ .addSrcTarget(newFolderTargetBuilder())
+ .addSrcTarget(newParentContainerTarget())
+ .build();
+ mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
+ mPreviousLabel = mFolderName.getText().toString();
+ mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
+ }
+
+ private Target.FromFolderLabelState getFromFolderLabelState() {
+ return mPreviousLabel == null
+ ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
+ : mPreviousLabel.isEmpty()
+ ? FROM_EMPTY
+ : mIsPreviousLabelSuggested
+ ? FROM_SUGGESTED
+ : FROM_CUSTOM;
+ }
+
+ private Target.ToFolderLabelState getToFolderLabelState() {
+ String newLabel =
+ checkNotNull(mFolderName.getText().toString(),
+ "Expected valid folder label, but found null");
+
+ Optional<String[]> suggestedLabels = Optional.ofNullable(
+ (FolderNameInfo[]) mInfo.suggestedFolderNames
+ .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+ .map(folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .map(FolderNameInfo::getLabel)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+
+
+ int accepted_suggestion_index = suggestedLabels
+ .map(folderNameInfoArray ->
+ IntStream.range(0, folderNameInfoArray.length)
+ .filter(index -> newLabel.equalsIgnoreCase(
+ folderNameInfoArray[index]))
+ .findFirst()
+ .orElse(-1)
+ ).orElse(-1);
+
+ boolean hasValidPrimary = suggestedLabels
+ .map(labels -> labels.length > 0 && !isEmpty(labels[0]))
+ .orElse(false);
+ String primarySuffix = hasValidPrimary
+ ? "_WITH_VALID_PRIMARY"
+ : "_WITH_EMPTY_PRIMARY";
+
+ boolean isEmptySuggestions = suggestedLabels
+ .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+ .orElse(true);
+ boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
+ String suggestionsSuffix = isSuggestionsEnabled
+ ? isEmptySuggestions
+ ? "_WITH_EMPTY_SUGGESTIONS"
+ : "_WITH_VALID_SUGGESTIONS"
+ : "_WITH_SUGGESTIONS_DISABLED";
+
+ return newLabel.equals(mPreviousLabel)
+ ? Target.ToFolderLabelState.UNCHANGED
+ : newLabel.isEmpty()
+ ? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
+ : accepted_suggestion_index >= 0
+ ? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
+ + accepted_suggestion_index
+ + primarySuffix)
+ : Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
+ }
+
+
+ private Target.Builder newEditTextTargetBuilder() {
+ return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
+ }
+
+ private Target.Builder newFolderTargetBuilder() {
+ return Target.newBuilder()
+ .setType(Target.Type.CONTAINER)
+ .setContainerType(ContainerType.FOLDER)
+ .setPageIndex(mInfo.screenId)
+ .setGridX(mInfo.cellX)
+ .setGridY(mInfo.cellY)
+ .setCardinality(mInfo.contents.size());
+ }
+
+ private Target.Builder newParentContainerTarget() {
+ Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+
+ switch (mInfo.container) {
+ case CONTAINER_HOTSEAT:
+ return builder.setContainerType(ContainerType.HOTSEAT);
+ case CONTAINER_DESKTOP:
+ return builder.setContainerType(ContainerType.WORKSPACE);
+ default:
+ throw new AssertionError(String
+ .format("Expected container to be either %s or %s but found %s.",
+ CONTAINER_HOTSEAT,
+ CONTAINER_DESKTOP,
+ mInfo.container));
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6a47b98..ab1ff10 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -393,15 +393,16 @@
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
final int finalIndex = index;
- String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfo[] nameInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
- getContext(), mInfo.contents, suggestedNameOut);
- showFinalView(finalIndex, item, suggestedNameOut);
+ getContext(), mInfo.contents, nameInfos);
+ showFinalView(finalIndex, item, nameInfos);
});
} else {
- showFinalView(finalIndex, item, suggestedNameOut);
+ showFinalView(finalIndex, item, nameInfos);
}
} else {
addItem(item);
@@ -409,12 +410,12 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- String[] suggestedNameOut) {
+ FolderNameInfo[] nameInfos) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
invalidate();
- mFolder.showSuggestedTitle(suggestedNameOut);
+ mFolder.showSuggestedTitle(nameInfos);
}, DROP_IN_ANIMATION_DURATION);
}
diff --git a/src/com/android/launcher3/folder/FolderNameInfo.java b/src/com/android/launcher3/folder/FolderNameInfo.java
new file mode 100644
index 0000000..eb9da90
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderNameInfo.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 957e636..26fe059 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -21,15 +21,21 @@
import android.util.Log;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -49,6 +55,8 @@
* name edit box can also be used to provide suggestion.
*/
public static final int SUGGEST_MAX = 4;
+ protected IntSparseArrayMap<FolderInfo> mFolderInfos;
+ protected List<AppInfo> mAppInfos;
/**
* Retrieve instance of this object that can be overridden in runtime based on the build
@@ -57,78 +65,99 @@
public static FolderNameProvider newInstance(Context context) {
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
context.getApplicationContext(), R.string.folder_name_provider_class);
+ fnp.load(context);
+
return fnp;
}
- public CharSequence getSuggestedFolderName(Context context,
- ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
+ private void load(Context context) {
+ LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
+ new FolderNameWorker());
+ }
+
+ /**
+ * Generate and rank the suggested Folder names.
+ */
+ public void getSuggestedFolderName(Context context,
+ ArrayList<WorkspaceItemInfo> workspaceItemInfos,
+ FolderNameInfo[] nameInfos) {
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+ Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p-> p.user))
+ .filter(distinctByKey(p -> p.user))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1
&& !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
// Place it as last viable suggestion
- setAsLastSuggestion(candidates,
+ setAsLastSuggestion(nameInfos,
context.getResources().getString(R.string.work_folder_name));
}
// If all the icons are from same package (e.g., main icon, shortcut, shortcut)
// Then, suggest the package's title as the folder name
distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p-> p.getTargetComponent() != null
+ .filter(distinctByKey(p -> p.getTargetComponent() != null
? p.getTargetComponent().getPackageName() : ""))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1) {
- Optional<AppInfo> info = LauncherAppState.getInstance(context).getModel()
- .getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
- .getPackageName());
+ Optional<AppInfo> info = getAppInfoByPackageName(
+ distinctItemInfos.get(0).getTargetComponent().getPackageName());
// Place it as first viable suggestion and shift everything else
- info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
+ info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+ Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
- return candidates[0];
}
- private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
- if (contains(candidatesOut, candidate)) {
+ private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
+ if (mAppInfos == null || mAppInfos.isEmpty()) {
+ return Optional.empty();
+ }
+ return mAppInfos.stream()
+ .filter(info -> info.componentName.getPackageName().equals(packageName))
+ .findAny();
+ }
+
+ private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+ if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
- for (int i = candidatesOut.length - 1; i > 0; i--) {
- if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
- candidatesOut[i] = candidatesOut[i - 1];
+ 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];
}
}
- candidatesOut[0] = candidate;
+ nameInfos[0] = new FolderNameInfo(label, 1.0);
}
- private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
- if (contains(candidatesOut, candidate)) {
+ private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+ if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
- for (int i = 0; i < candidate.length(); i++) {
- if (TextUtils.isEmpty(candidatesOut[i])) {
- candidatesOut[i] = candidate;
+ for (int i = 0; i < nameInfos.length; i++) {
+ if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
+ nameInfos[i] = new FolderNameInfo(label, 1.0);
return;
}
}
- candidatesOut[candidate.length() - 1] = candidate;
+ // Overwrite the last suggestion.
+ int lastIndex = nameInfos.length - 1;
+ nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
}
- private boolean contains(CharSequence[] list, CharSequence key) {
- return Arrays.asList(list).stream()
- .filter(s -> s != null)
- .anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
+ private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
+ return Arrays.stream(nameInfos)
+ .filter(Objects::nonNull)
+ .anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
+ label.toString()));
}
// This method can be moved to some Utility class location.
@@ -136,4 +165,13 @@
Map<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
+
+ private class FolderNameWorker extends BaseModelUpdateTask {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ mFolderInfos = dataModel.folders.clone();
+ mAppInfos = Arrays.asList(apps.copyData());
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index f579451..94d30f6 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -152,7 +152,7 @@
}
public final void generateDragOutline(Bitmap preview) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mOutlineGeneratorCallback != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
throw new RuntimeException("Drag outline generated twice");
}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 2c972a0..67f07b1 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -42,7 +42,7 @@
private static Handler sHandler = null;
private static File sLogsDirectory = null;
- private static final int LOG_DAYS = 2;
+ public static final int LOG_DAYS = 4;
public static void setDir(File logsDir) {
if (ENABLED) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 8289da9..199d13f 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -69,8 +69,7 @@
public class UserEventDispatcher implements ResourceBasedOverride {
private static final String TAG = "UserEvent";
- private static final boolean IS_VERBOSE =
- FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+ private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final String UUID_STORAGE = "uuid";
public static UserEventDispatcher newInstance(Context context,
@@ -372,6 +371,25 @@
dispatchUserEvent(event, null);
}
+ /**
+ * Logs proto lite version of LauncherEvent object to clearcut.
+ */
+ public void logLauncherEvent(
+ com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
+
+ if (mPreviousHomeGesture) {
+ mPreviousHomeGesture = false;
+ }
+ mAppOrTaskLaunch = false;
+ launcherEvent.toBuilder()
+ .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
+ .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
+ if (!IS_VERBOSE) {
+ return;
+ }
+ Log.d(TAG, launcherEvent.toString());
+ }
+
public void logDeepShortcutsOpen(View icon) {
LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index c24b939..32fce0b 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -273,7 +273,7 @@
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folders.remove(item.id);
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
for (ItemInfo info : itemsIdMap) {
if (info.container == item.id) {
// We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index eb99af5..07a7551 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -33,8 +33,6 @@
import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.pm.UserCache;
-import java.util.Objects;
-
/**
* Helper class to backup and restore Favorites table into a separate table
* within the same data base.
@@ -63,8 +61,7 @@
private static final int STATE_SANITIZED = 2;
private final Context mContext;
- private final SQLiteDatabase mFavoritesDb;
- private final SQLiteDatabase mBackupDb;
+ private final SQLiteDatabase mDb;
private final int mOldHotseatSize;
private final int mOldGridX;
@@ -77,11 +74,10 @@
@IntDef({STATE_NOT_FOUND, STATE_RAW, STATE_SANITIZED})
private @interface BackupState { }
- public GridBackupTable(Context context, SQLiteDatabase favoritesDb, SQLiteDatabase backupDb,
- int hotseatSize, int gridX, int gridY) {
+ public GridBackupTable(Context context, SQLiteDatabase db, int hotseatSize, int gridX,
+ int gridY) {
mContext = context;
- mFavoritesDb = favoritesDb;
- mBackupDb = backupDb;
+ mDb = db;
mOldHotseatSize = hotseatSize;
mOldGridX = gridX;
@@ -94,7 +90,7 @@
*/
public boolean backupOrRestoreAsNeeded() {
// Check if backup table exists
- if (!tableExists(mBackupDb, BACKUP_TABLE_NAME)) {
+ if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
.getBoolean(Settings.EXTRA_VALUE, false)) {
// No need to copy if empty DB was created.
@@ -109,7 +105,7 @@
}
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
Process.myUserHandle());
- copyTable(mBackupDb, BACKUP_TABLE_NAME, mFavoritesDb, Favorites.TABLE_NAME, userSerial);
+ copyTable(mDb, BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
Log.d(TAG, "Backup table found");
return true;
}
@@ -122,37 +118,28 @@
/**
* Copy valid grid entries from one table to another.
*/
- private static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
- String toTable, long userSerial) {
- dropTable(toDb, toTable);
- Favorites.addTableToDb(toDb, userSerial, false, toTable);
- if (fromDb != toDb) {
- toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
- toDb.execSQL(
- "INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable
- + " where _id > " + ID_PROPERTY);
- } else {
- toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable + " where _id > "
- + ID_PROPERTY);
- }
+ private static void copyTable(SQLiteDatabase db, String from, String to, long userSerial) {
+ dropTable(db, to);
+ Favorites.addTableToDb(db, userSerial, false, to);
+ db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
}
private void encodeDBProperties(int options) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, ID_PROPERTY);
- values.put(KEY_DB_VERSION, mFavoritesDb.getVersion());
+ values.put(KEY_DB_VERSION, mDb.getVersion());
values.put(KEY_GRID_X_SIZE, mOldGridX);
values.put(KEY_GRID_Y_SIZE, mOldGridY);
values.put(KEY_HOTSEAT_SIZE, mOldHotseatSize);
values.put(Favorites.OPTIONS, options);
- mBackupDb.insert(BACKUP_TABLE_NAME, null, values);
+ mDb.insert(BACKUP_TABLE_NAME, null, values);
}
/**
* Load DB properties from grid backup table.
*/
public @BackupState int loadDBProperties() {
- try (Cursor c = mBackupDb.query(BACKUP_TABLE_NAME, new String[] {
+ try (Cursor c = mDb.query(BACKUP_TABLE_NAME, new String[] {
KEY_DB_VERSION, // 0
KEY_GRID_X_SIZE, // 1
KEY_GRID_Y_SIZE, // 2
@@ -163,7 +150,7 @@
Log.e(TAG, "Meta data not found in backup table");
return STATE_NOT_FOUND;
}
- if (!validateDBVersion(mBackupDb.getVersion(), c.getInt(0))) {
+ if (!validateDBVersion(mDb.getVersion(), c.getInt(0))) {
return STATE_NOT_FOUND;
}
@@ -179,7 +166,7 @@
* Restore workspace from raw backup if available.
*/
public boolean restoreFromRawBackupIfAvailable(long oldProfileId) {
- if (!tableExists(mBackupDb, Favorites.BACKUP_TABLE_NAME)
+ if (!tableExists(mDb, Favorites.BACKUP_TABLE_NAME)
|| loadDBProperties() != STATE_RAW
|| mOldHotseatSize != mRestoredHotseatSize
|| mOldGridX != mRestoredGridX
@@ -187,8 +174,7 @@
// skip restore if dimensions in backup table differs from current setup.
return false;
}
- copyTable(mBackupDb, Favorites.BACKUP_TABLE_NAME, mFavoritesDb, Favorites.TABLE_NAME,
- oldProfileId);
+ copyTable(mDb, Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
Log.d(TAG, "Backup restored");
return true;
}
@@ -197,8 +183,7 @@
* Performs a backup on the workspace layout.
*/
public void doBackup(long profileId, int options) {
- copyTable(mFavoritesDb, Favorites.TABLE_NAME, mBackupDb, Favorites.BACKUP_TABLE_NAME,
- profileId);
+ copyTable(mDb, Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
encodeDBProperties(options);
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 4f92066..c35c4b9 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -909,7 +909,7 @@
boolean dbChanged = false;
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
- transaction.getDb(), srcHotseatCount, sourceSize.x, sourceSize.y);
+ srcHotseatCount, sourceSize.x, sourceSize.y);
if (backupTable.backupOrRestoreAsNeeded()) {
dbChanged = true;
srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4f61a2a..af802ef 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -59,6 +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.FolderNameProvider;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
@@ -904,11 +905,13 @@
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
- String[] suggestedOut = new String[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfo[] suggestionInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
FolderInfo info = mBgDataModel.folders.valueAt(i);
if (info.suggestedFolderNames == null) {
- provider.getSuggestedFolderName(mApp.getContext(), info.contents, suggestedOut);
- info.suggestedFolderNames = new Intent().putExtra("suggest", suggestedOut);
+ provider.getSuggestedFolderName(mApp.getContext(), info.contents,
+ suggestionInfos);
+ info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
}
}
}
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 628dd95..1473124 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -97,7 +97,7 @@
return Integer.compare(lhs.screenId, rhs.screenId);
}
default:
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException(
"Unexpected container type when sorting workspace items.");
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ccd1554..27fa580 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -115,8 +115,9 @@
ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
- if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
- modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
+ if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_STUDIO_BUILD
+ && modelItem instanceof WorkspaceItemInfo
+ && item instanceof WorkspaceItemInfo) {
if (modelItem.title.toString().equals(item.title.toString()) &&
modelItem.getIntent().filterEquals(item.getIntent()) &&
modelItem.id == item.id &&
@@ -320,7 +321,7 @@
*/
public void prepareToUndoDelete() {
if (!mPreparingToUndo) {
- if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("There are still uncommitted delete operations!");
}
mDeleteRunnables.clear();
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 842be40..0fe3673 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -108,7 +108,7 @@
private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
// TODO(pinyaoting): Support backing up workspace with multiple grid options.
- new GridBackupTable(context, db, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
+ new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
.doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
}
@@ -117,8 +117,8 @@
throws Exception {
// TODO(pinyaoting): Support restoring workspace with multiple grid options.
final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
- GridBackupTable backupTable = new GridBackupTable(context, db, db,
- idp.numHotseatIcons, idp.numColumns, idp.numRows);
+ GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
+ idp.numColumns, idp.numRows);
if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
sanitizeDB(helper, db, backupManager);
LauncherAppState.getInstance(context).getModel().forceReload();
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index fd066c1..d522d81 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -17,13 +17,30 @@
package com.android.launcher3.testing;
import android.util.Log;
+import android.view.MotionEvent;
import com.android.launcher3.Utilities;
public final class TestLogging {
- public static void recordEvent(String event) {
+ private static void recordEventSlow(String sequence, String event) {
+ Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
+ }
+
+ public static void recordEvent(String sequence, String event) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d(TestProtocol.TAPL_EVENTS_TAG, event);
+ recordEventSlow(sequence, event);
+ }
+ }
+
+ public static void recordEvent(String sequence, String message, Object parameter) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ recordEventSlow(sequence, message + ": " + parameter);
+ }
+ }
+
+ public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS && event.getAction() != MotionEvent.ACTION_MOVE) {
+ recordEventSlow(sequence, message + ": " + event);
}
}
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 7aa5140..f995c61 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -33,6 +33,8 @@
public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
public static final int HINT_STATE_ORDINAL = 7;
public static final String TAPL_EVENTS_TAG = "TaplEvents";
+ public static final String SEQUENCE_MAIN = "Main";
+ public static final String SEQUENCE_TIS = "TIS";
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index 4a4a5ca..fcb96d7 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.util.Log;
-import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
@@ -67,7 +66,7 @@
* Utility method to debug binary data
*/
public static String createTempFile(Context context, byte[] data) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Method only allowed in development mode");
}
@@ -87,7 +86,7 @@
try {
c.close();
} catch (IOException e) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Error closing", e);
}
}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index ed66422..63d56e4 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -28,25 +28,25 @@
public class Preconditions {
public static void assertNotNull(Object o) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && o == null) {
throw new IllegalStateException();
}
}
public static void assertWorkerThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
throw new IllegalStateException();
}
}
public static void assertUIThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
public static void assertNonUiThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
index f8add9a..9ac8230 100644
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -119,6 +119,13 @@
mWorkModeSwitch.refresh();
}
+ /**
+ * Returns work mode switch
+ */
+ public WorkModeSwitch getWorkModeSwitch() {
+ return mWorkModeSwitch;
+ }
+
private boolean shouldShowWorkFooter() {
Launcher launcher = Launcher.getLauncher(getContext());
return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 5ebf8d3..282867a 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -115,7 +115,7 @@
}
setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+ if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
// time Launcher starts.
// TODO: after figuring out a repro step, introduce a dirty bit to check when
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DisplayRotationListener.java b/src_ui_overrides/com/android/launcher3/uioverrides/DisplayRotationListener.java
deleted file mode 100644
index b1a67e9..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/DisplayRotationListener.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 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.uioverrides;
-
-import android.content.Context;
-import android.view.OrientationEventListener;
-
-/**
- * Utility class for listening for rotation changes
- */
-public class DisplayRotationListener extends OrientationEventListener {
-
- private final Runnable mCallback;
-
- public DisplayRotationListener(Context context, Runnable callback) {
- super(context);
- mCallback = callback;
- }
-
- @Override
- public void onOrientationChanged(int i) {
- mCallback.run();
- }
-}
diff --git a/tests/Android.mk b/tests/Android.mk
index d1a6c06..a9fff8e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -51,7 +51,7 @@
androidx.test.rules \
androidx.test.uiautomator_uiautomator \
mockito-target-minus-junit4 \
- launcher-log-protos-lite
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index d9edc35..5aa0090 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -16,17 +16,32 @@
package com.android.launcher3.ui;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.views.WorkFooterContainer;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+import java.util.Objects;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
public class WorkTabTest extends AbstractLauncherUiTest {
@@ -52,16 +67,50 @@
}
@Test
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
public void workTabExists() {
mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
- /*
- assertTrue("Personal tab is missing", waitForLauncherCondition(
- launcher -> launcher.getAppsView().isPersonalTabVisible()));
- assertTrue("Work tab is missing", waitForLauncherCondition(
- launcher -> launcher.getAppsView().isWorkTabVisible()));
- */
+ waitForLauncherCondition("Personal tab is missing",
+ launcher -> launcher.getAppsView().isPersonalTabVisible());
+ waitForLauncherCondition("Work tab is missing",
+ launcher -> launcher.getAppsView().isWorkTabVisible());
}
+
+ @Test
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
+ public void toggleWorks() {
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+ executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+ waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+ getOnceNotNull("Apps view did not bind",
+ launcher -> launcher.getAppsView().getWorkFooterContainer());
+
+ UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
+ assertEquals(2, userManager.getUserProfiles().size());
+ UserHandle workProfile = getFromLauncher(l -> {
+ UserHandle myHandle = Process.myUserHandle();
+ List<UserHandle> userProfiles = userManager.getUserProfiles();
+ return userProfiles.get(0) == myHandle ? userProfiles.get(1) : userProfiles.get(0);
+ });
+
+ waitForLauncherCondition("work profile can't be turned off",
+ l -> userManager.requestQuietModeEnabled(true, workProfile));
+
+ assertTrue(userManager.isQuietModeEnabled(workProfile));
+ executeOnLauncher(launcher -> {
+ WorkFooterContainer wf = launcher.getAppsView().getWorkFooterContainer();
+ ((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
+ AllAppsContainerView.AdapterHolder.WORK);
+ wf.getWorkModeSwitch().toggle();
+ });
+ waitForLauncherCondition("Work toggle did not work",
+ l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
+ }
+
}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 02d07bb..cdda0f0 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -8,7 +8,6 @@
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -16,7 +15,6 @@
public class FailureWatcher extends TestWatcher {
private static final String TAG = "FailureWatcher";
- private static boolean sHadFailedTestDeinitialization;
final private UiDevice mDevice;
public FailureWatcher(UiDevice device) {
@@ -62,35 +60,4 @@
device.takeScreenshot(new File(pathname));
}
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (sHadFailedTestDeinitialization) {
- Log.d(TAG, "Skipping due to a recent test deinitialization failure: " +
- description.getDisplayName());
- return;
- }
-
- try {
- base.evaluate();
- } catch (Throwable e) {
- final String stackTrace = Log.getStackTraceString(e);
- if (!stackTrace.contains(
- "androidx.test.internal.runner.junit4.statement.RunBefores.evaluate")) {
- // Test failed to deinitialize. Since the global state is probably
- // corrupted, won't execute other tests.
- Log.d(TAG,
- "Detected an exception from test finalizer, will skip further "
- + "tests: " + stackTrace);
- sHadFailedTestDeinitialization = true;
- }
- throw e;
- }
- }
- };
- }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index afb50e0..468f54c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -21,6 +21,8 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
public class AddToHomeScreenPrompt {
@@ -38,6 +40,13 @@
public void addAutomatically() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ if (mLauncher.getNavigationModel()
+ != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
+ }
mLauncher.waitForObjectInContainer(
mWidgetCell.getParent().getParent().getParent().getParent(),
By.text(ADD_AUTOMATICALLY)).click();
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 3f814fd..8932291 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -22,6 +22,8 @@
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
/**
@@ -56,6 +58,6 @@
@Override
protected void expectActivityStartEvents() {
- mLauncher.expectEvent(START_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, START_EVENT);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index fadfd9f..f8dd89c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -18,6 +18,8 @@
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
/**
@@ -45,6 +47,6 @@
@Override
protected void expectActivityStartEvents() {
- mLauncher.expectEvent(START_SHORTCUT_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, START_SHORTCUT_EVENT);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 9f29a1a..2acab97 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -131,7 +131,7 @@
}
case THREE_BUTTON:
- mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(
() -> mLauncher.waitForSystemUiObject("recent_apps").click(),
OVERVIEW_STATE_ORDINAL);
@@ -195,14 +195,14 @@
case THREE_BUTTON:
// Double press the recents button.
UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
- mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
mLauncher.getOverview();
- mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
recentsButton.click();
break;
}
- mLauncher.expectEvent(TASK_START_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
}
protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c8f22c2..c5fbd7c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -76,8 +76,10 @@
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -102,13 +104,17 @@
static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
"(?<time>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] "
+ "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9])"
- + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<event>.*)");
+ + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / "
+ + "(?<event>.*)");
private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
+ static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
+ static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
@@ -170,19 +176,27 @@
private Consumer<ContainerType> mOnSettledStateAction;
+ // Map from an event sequence name to an ordered list of expected events in that sequence.
// Not null when we are collecting expected events to compare with actual ones.
- private List<Pattern> mExpectedEvents;
+ private Map<String, List<Pattern>> mExpectedEvents;
private Date mStartRecordingTime;
private boolean mCheckEventsForSuccessfulGestures = false;
- private static Pattern getTouchEventPattern(String action) {
+ private static Pattern getTouchEventPattern(String prefix, String action) {
// The pattern includes sanity checks that we don't get a multi-touch events or other
// surprises.
return Pattern.compile(
- "Touch event: MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
- +
- ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
+ prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
+ + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
+ }
+
+ private static Pattern getTouchEventPattern(String action) {
+ return getTouchEventPattern("Touch event", action);
+ }
+
+ private static Pattern getTouchEventPatternTIS(String action) {
+ return getTouchEventPattern("TouchInteractionService.onInputEvent", action);
}
/**
@@ -652,10 +666,14 @@
if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
hasLauncherObject(WIDGETS_RES_ID)
&& !mDevice.isNaturalOrientation()) {
- expectEvent(EVENT_PILFER_POINTERS);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
}
}
+ if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
runToState(
waitForSystemUiObject("home")::click,
NORMAL_STATE_ORDINAL,
@@ -935,8 +953,12 @@
}
void clickLauncherObject(UiObject2 object) {
- expectEvent(LauncherInstrumentation.EVENT_TOUCH_DOWN);
- expectEvent(LauncherInstrumentation.EVENT_TOUCH_UP);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+ if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
+ expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
+ }
object.click();
}
@@ -1078,17 +1100,23 @@
switch (action) {
case MotionEvent.ACTION_DOWN:
if (gestureScope != GestureScope.OUTSIDE) {
- expectEvent(EVENT_TOUCH_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
+ }
+ if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
}
break;
case MotionEvent.ACTION_UP:
if (gestureScope != GestureScope.INSIDE) {
- expectEvent(EVENT_PILFER_POINTERS);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
}
if (gestureScope != GestureScope.OUTSIDE) {
- expectEvent(gestureScope == GestureScope.INSIDE
+ expectEvent(TestProtocol.SEQUENCE_MAIN, gestureScope == GestureScope.INSIDE
? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
}
+ if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
break;
}
@@ -1217,16 +1245,20 @@
return tasks;
}
- private List<String> getEvents() {
- final ArrayList<String> events = new ArrayList<>();
+ // Returns actual events retrieved from logcat. The return value's key set is the set of all
+ // sequence names that actually had at least one event, and the values are lists of events in
+ // the given sequence, in the order they were recorded.
+ private Map<String, List<String>> getEvents() {
+ final Map<String, List<String>> events = new HashMap<>();
try {
- // Logcat may skip events after the specified time. Querying for events starting 1 sec
+ // Logcat may skip events after the specified time. Querying for events starting 1 min
// earlier.
- final Date startTime = new Date(mStartRecordingTime.getTime() - 10000);
- final String logcatEvents = mDevice.executeShellCommand(
- "logcat -d -v year --pid=" + getPid() + " -t "
- + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
- + " -s " + TestProtocol.TAPL_EVENTS_TAG);
+ final Date startTime = new Date(mStartRecordingTime.getTime() - 60000);
+ final String logcatCommand = "logcat -d -v year --pid=" + getPid() + " -t "
+ + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
+ + " -s " + TestProtocol.TAPL_EVENTS_TAG;
+ log("Events query command: " + logcatCommand);
+ final String logcatEvents = mDevice.executeShellCommand(logcatCommand);
final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
while (matcher.find()) {
// Skip events before recording start time.
@@ -1235,7 +1267,8 @@
continue;
}
- events.add(matcher.group("event"));
+ eventsListForSequence(matcher.group("sequence"), events).add(
+ matcher.group("event"));
}
return events;
} catch (IOException e) {
@@ -1245,9 +1278,20 @@
}
}
+ // Returns an event list for a given sequence, adding it to the map as needed.
+ private static <T> List<T> eventsListForSequence(
+ String sequenceName, Map<String, List<T>> events) {
+ List<T> eventSequence = events.get(sequenceName);
+ if (eventSequence == null) {
+ eventSequence = new ArrayList<>();
+ events.put(sequenceName, eventSequence);
+ }
+ return eventSequence;
+ }
+
private void startRecordingEvents() {
Assert.assertTrue("Already recording events", mExpectedEvents == null);
- mExpectedEvents = new ArrayList<>();
+ mExpectedEvents = new HashMap<>();
mStartRecordingTime = new Date();
log("startRecordingEvents: " + DATE_TIME_FORMAT.format(mStartRecordingTime));
}
@@ -1281,65 +1325,124 @@
final String message = getEventMismatchMessage(true);
if (message != null) {
Assert.fail(formatSystemHealthMessage(
- "http://go/tapl : unexpected event sequence: " + message));
+ "http://go/tapl : successful gesture produced " + message));
}
};
}
- void expectEvent(Pattern expected) {
- if (mExpectedEvents != null) mExpectedEvents.add(expected);
+ void expectEvent(String sequence, Pattern expected) {
+ if (mExpectedEvents != null) {
+ eventsListForSequence(sequence, mExpectedEvents).add(expected);
+ }
}
+ // Returns non-null error message if the actual events in logcat don't match expected events.
+ // If we are not checking events, returns null.
private String getEventMismatchMessage(boolean waitForExpectedCount) {
if (mExpectedEvents == null) return null;
try {
- List<String> actual = getEvents();
+ Map<String, List<String>> actual = getEvents();
if (waitForExpectedCount) {
// Wait until Launcher generates the expected number of events.
final long endTime = SystemClock.uptimeMillis() + WAIT_TIME_MS;
while (SystemClock.uptimeMillis() < endTime
- && actual.size() < mExpectedEvents.size()) {
+ && !receivedEnoughEvents(actual)) {
SystemClock.sleep(100);
actual = getEvents();
}
}
- for (int i = 0; i < mExpectedEvents.size(); ++i) {
- if (i >= actual.size()) {
- return formatEventMismatchMessage("too few actual events", actual, i);
- }
- if (!mExpectedEvents.get(i).matcher(actual.get(i)).find()) {
- return formatEventMismatchMessage("a mismatched event", actual, i);
- }
- }
-
- if (actual.size() > mExpectedEvents.size()) {
- return formatEventMismatchMessage(
- "too many actual events", actual, mExpectedEvents.size());
- }
+ return getEventMismatchErrorMessage(actual);
} finally {
stopRecordingEvents();
}
-
- return null;
}
- private String formatEventList(List events, int position) {
+ // Returns whether there is a sufficient number of events in the logcat to match the expected
+ // events.
+ private boolean receivedEnoughEvents(Map<String, List<String>> actual) {
+ for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
+ final List<String> actualEventSequence = actual.get(expectedNamedSequence.getKey());
+ if (actualEventSequence == null
+ || actualEventSequence.size() < expectedNamedSequence.getValue().size()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // If the list of actual events matches the list of expected events, returns -1, otherwise
+ // the position of the mismatch.
+ private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
+ for (int i = 0; i < expected.size(); ++i) {
+ if (i >= actual.size()
+ || !expected.get(i).matcher(actual.get(i)).find()) {
+ return i;
+ }
+ }
+
+ if (actual.size() > expected.size()) return expected.size();
+
+ return -1;
+ }
+
+ // Returns non-null error message if the actual events passed as a param don't match expected
+ // events.
+ private String getEventMismatchErrorMessage(Map<String, List<String>> actualEvents) {
final StringBuilder sb = new StringBuilder();
+
+ // Check that all expected even sequences match the actual data.
+ for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
+ List<String> actualEventSequence = actualEvents.get(expectedNamedSequence.getKey());
+ if (actualEventSequence == null) actualEventSequence = new ArrayList<>();
+ final int mismatchPosition = getMismatchPosition(
+ expectedNamedSequence.getValue(), actualEventSequence);
+ if (mismatchPosition != -1) {
+ formatSequenceWithMismatch(
+ sb,
+ expectedNamedSequence.getKey(),
+ expectedNamedSequence.getValue(),
+ actualEventSequence,
+ mismatchPosition);
+ }
+ }
+
+ // Check for unexpected event sequences in the actual data.
+ for (Map.Entry<String, List<String>> actualNamedSequence : actualEvents.entrySet()) {
+ if (!mExpectedEvents.containsKey(actualNamedSequence.getKey())) {
+ formatSequenceWithMismatch(
+ sb,
+ actualNamedSequence.getKey(),
+ new ArrayList<>(),
+ actualNamedSequence.getValue(),
+ 0);
+ }
+ }
+
+ return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
+ }
+
+ private static void formatSequenceWithMismatch(
+ StringBuilder sb,
+ String sequenceName,
+ List<Pattern> expected,
+ List<String> actualEvents,
+ int mismatchPosition) {
+ sb.append("\n>> Sequence " + sequenceName);
+ sb.append("\n Expected:");
+ formatEventListWithMismatch(sb, expected, mismatchPosition);
+ sb.append("\n Actual:");
+ formatEventListWithMismatch(sb, actualEvents, mismatchPosition);
+ }
+
+ private static void formatEventListWithMismatch(StringBuilder sb, List events, int position) {
for (int i = 0; i < events.size(); ++i) {
- sb.append("\n| ");
+ sb.append("\n | ");
sb.append(i == position ? "---> " : " ");
sb.append(events.get(i).toString());
}
- if (position == events.size()) sb.append("\n| ---> (end)");
- return sb.toString();
- }
-
- private String formatEventMismatchMessage(String message, List<String> actual, int position) {
- return message + ":"
- + "\nExpected:" + formatEventList(mExpectedEvents, position)
- + "\nActual:" + formatEventList(actual, position);
+ if (position == events.size()) sb.append("\n | ---> (end)");
}
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 410e5a1..f955cf2 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -22,6 +22,8 @@
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
/**
@@ -76,7 +78,7 @@
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
() -> "Launching task didn't open a new window: "
+ mTask.getParent().getContentDescription());
- mLauncher.expectEvent(TASK_START_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
}
return new Background(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index a0d5443..3f5dc8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -260,8 +260,8 @@
public Widgets openAllWidgets() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
verifyActiveContainer();
- mLauncher.expectEvent(EVENT_CTRL_W_DOWN);
- mLauncher.expectEvent(EVENT_CTRL_W_UP);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_UP);
mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
return new Widgets(mLauncher);