Merge "Remove mSourceWindowClipInsetsForLiveTile" into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index cb695df..e132854 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,3 +47,14 @@
},
static_libs: ["libprotobuf-java-lite"],
}
+
+java_library {
+ name: "LauncherPluginLib",
+
+ static_libs: ["PluginCoreLib"],
+
+ srcs: ["src_plugins/**/*.java"],
+
+ sdk_version: "current",
+ min_sdk_version: "28",
+}
diff --git a/Android.mk b/Android.mk
index c066a12..9cfcf17 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,24 +17,6 @@
LOCAL_PATH := $(call my-dir)
#
-# Build rule for plugin lib (needed to write a plugin).
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT2_ONLY := true
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src_plugins)
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 28
-LOCAL_MODULE := LauncherPluginLib
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
# Build rule for Launcher3 dependencies lib.
#
include $(CLEAR_VARS)
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 7690b9d..3b3dc01 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
+import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
@@ -68,4 +69,9 @@
public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
LauncherAppState app) {
}
+
+ public WidgetItem getWidgetProviderInfoByProviderName(
+ ComponentName providerName) {
+ return null;
+ }
}
\ No newline at end of file
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index ec1d55b..d08d851 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -151,6 +151,10 @@
DISMISS_PREDICTION = 21;
HYBRID_HOTSEAT_ACCEPTED = 22;
HYBRID_HOTSEAT_CANCELED = 23;
+ OVERVIEW_ACTIONS_SHARE_BUTTON = 24;
+ OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25;
+ OVERVIEW_ACTIONS_SELECT_BUTTON = 26;
+ SELECT_MODE_CLOSE_BUTTON = 27;
}
enum TipType {
diff --git a/protos/launcher_trace.proto b/protos/launcher_trace.proto
new file mode 100644
index 0000000..c6f3543
--- /dev/null
+++ b/protos/launcher_trace.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+
+package com.android.launcher3.tracing;
+
+option java_multiple_files = true;
+
+message LauncherTraceProto {
+
+ optional TouchInteractionServiceProto touch_interaction_service = 1;
+}
+
+message TouchInteractionServiceProto {
+
+ optional bool service_connected = 1;
+}
diff --git a/protos/launcher_trace_file.proto b/protos/launcher_trace_file.proto
new file mode 100644
index 0000000..6ce182a
--- /dev/null
+++ b/protos/launcher_trace_file.proto
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+
+import "launcher_trace.proto";
+
+package com.android.launcher3.tracing;
+
+option java_multiple_files = true;
+
+/* represents a file full of launcher trace entries.
+ Encoded, it should start with 0x9 0x4C 0x4E 0x43 0x48 0x52 0x54 0x52 0x43 (.LNCHRTRC), such
+ that they can be easily identified. */
+message LauncherTraceFileProto {
+
+ /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
+ (this is needed because enums have to be 32 bits and there's no nice way to put 64bit
+ constants into .proto files. */
+ enum MagicNumber {
+ INVALID = 0;
+ MAGIC_NUMBER_L = 0x48434E4C; /* LNCH (little-endian ASCII) */
+ MAGIC_NUMBER_H = 0x43525452; /* RTRC (little-endian ASCII) */
+ }
+
+ optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */
+ repeated LauncherTraceEntryProto entry = 2;
+}
+
+/* one launcher trace entry. */
+message LauncherTraceEntryProto {
+ /* required: elapsed realtime in nanos since boot of when this entry was logged */
+ optional fixed64 elapsed_realtime_nanos = 1;
+
+ optional LauncherTraceProto launcher = 3;
+}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index d3cec28..a06a2dd 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -47,10 +47,7 @@
</intent-filter>
</service>
- <!-- STOPSHIP: Change exported to false once all the integration is complete.
- It is set to true so that the activity can be started from command line -->
<activity android:name="com.android.quickstep.RecentsActivity"
- android:exported="true"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
@@ -93,12 +90,12 @@
android:directBootAware="true" />
<activity
- android:name="com.android.quickstep.interaction.BackGestureTutorialActivity"
+ android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait">
<intent-filter>
- <action android:name="com.android.quickstep.action.BACK_GESTURE_TUTORIAL" />
+ <action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml b/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
index fa3a0f8..4fda2a9 100644
--- a/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
+++ b/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
@@ -15,5 +15,5 @@
-->
<vector android:height="24dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="@color/bottom_panel_background" android:pathData="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/>
+ <path android:fillColor="?android:attr/colorAccent" android:pathData="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/>
</vector>
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index d94c665..fe99037 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -24,13 +24,13 @@
<View
android:layout_width="match_parent"
android:layout_height="32dp"
- android:backgroundTint="@color/bottom_panel_background"
+ android:backgroundTint="?android:attr/colorAccent"
android:background="@drawable/bottom_sheet_top_border" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/bottom_panel_background"
+ android:background="?android:attr/colorAccent"
android:orientation="vertical">
<TextView
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index bfbd00e..e7290a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -34,6 +34,7 @@
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.Themes;
import java.util.List;
@@ -109,7 +110,7 @@
NOTIFICATION_CHANNEL_ID)
.setContentTitle(name)
.setOngoing(true)
- .setColor(mLauncher.getColor(R.color.bottom_panel_background))
+ .setColor(Themes.getColorAccent(mLauncher))
.setContentIntent(PendingIntent.getActivity(mLauncher, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT))
.setSmallIcon(R.drawable.hotseat_edu_notification_icon)
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 00e72b1..8926246 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
@@ -191,7 +191,7 @@
|| predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
return;
}
- mLauncher.getDragLayer().addView(this);
+ attachToContainer();
logOnBoardingSeen();
animateOpen();
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index a1c8378..7895bac 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -37,6 +37,7 @@
import android.graphics.Rect;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
@@ -47,6 +48,7 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -143,6 +145,10 @@
public void onStateTransitionEnd(Launcher launcher) {
launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
DiscoveryBounce.showForOverviewIfNeeded(launcher);
+ RecentsView recentsView = launcher.getOverviewPanel();
+ AccessibilityManagerCompat.sendCustomAccessibilityEvent(
+ recentsView.getPageAt(recentsView.getCurrentPage()),
+ AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index b80830a..519939e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -172,7 +172,7 @@
mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
|| upDisplacement < mMotionPauseMinDisplacement
|| upDisplacement > mMotionPauseMaxDisplacement);
- mMotionPauseDetector.addPosition(displacement, event.getEventTime());
+ mMotionPauseDetector.addPosition(event);
return super.onDrag(displacement, event);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 8628db0..799f1ad 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -317,7 +317,7 @@
// home screen elements will appear in the shelf on motion pause.
mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
|| -displacement.y < mMotionPauseMinDisplacement);
- mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime());
+ mMotionPauseDetector.addPosition(ev);
if (mIsHomeScreenVisible) {
// Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 0a79819..3601af2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -428,6 +428,10 @@
// rounding at the end of the animation.
float startRadius = mAppWindowAnimationHelper.getCurrentCornerRadius();
float endRadius = startRect.width() / 6f;
+
+ float startTransformProgress = mTransformParams.getProgress();
+ float endTransformProgress = 1;
+
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
@@ -437,7 +441,8 @@
public void onUpdate(RectF currentRect, float progress) {
homeAnim.setPlayFraction(progress);
- mTransformParams.setProgress(progress)
+ mTransformParams.setProgress(
+ Utilities.mapRange(progress, startTransformProgress, endTransformProgress))
.setCurrentRect(currentRect)
.setTargetAlpha(getWindowAlpha(progress));
if (isFloatingIconView) {
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 3364b66..28e8fb6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
import android.annotation.TargetApi;
@@ -63,6 +64,8 @@
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.tracing.nano.LauncherTraceProto;
+import com.android.launcher3.tracing.nano.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -76,6 +79,7 @@
import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
+import com.android.quickstep.util.ProtoTracer;
import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -85,6 +89,7 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.tracing.ProtoTraceable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -113,7 +118,8 @@
* Service connected by system-UI for handling touch interaction.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin> {
+public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
+ ProtoTraceable<LauncherTraceProto> {
private static final String TAG = "TouchInteractionService";
@@ -275,6 +281,7 @@
mDeviceState = new RecentsAnimationDeviceState(this);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+ ProtoTracer.INSTANCE.get(this).add(this);
sConnected = true;
}
@@ -381,6 +388,13 @@
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
mDeviceState.getSystemUiStateFlags());
mOverviewComponentObserver.onSystemUiStateChanged();
+
+ // Update the tracing state
+ if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) {
+ ProtoTracer.INSTANCE.get(TouchInteractionService.this).start();
+ } else {
+ ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
+ }
}
}
@@ -403,6 +417,8 @@
disposeEventHandlers();
mDeviceState.destroy();
SystemUiProxy.INSTANCE.get(this).setProxy(null);
+ ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
+ ProtoTracer.INSTANCE.get(this).remove(this);
sConnected = false;
super.onDestroy();
@@ -723,6 +739,9 @@
pw.println(" resumed=" + resumed);
pw.println(" mConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
+ pw.println("ProtoTrace:");
+ pw.println(" file="
+ + ProtoTracer.INSTANCE.get(TouchInteractionService.this).getTraceFile());
}
}
@@ -753,7 +772,7 @@
protected boolean shouldNotifyBackGesture() {
return mBackGestureNotificationCounter > 0 &&
- mDeviceState.getGestureBlockedActivityPackage() != null;
+ !mDeviceState.getGestureBlockedActivityPackages().isEmpty();
}
@WorkerThread
@@ -762,8 +781,8 @@
mBackGestureNotificationCounter--;
Utilities.getDevicePrefs(this).edit()
.putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
- sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
- mDeviceState.getGestureBlockedActivityPackage()));
+ mDeviceState.getGestureBlockedActivityPackages().forEach(blockedPackage ->
+ sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(blockedPackage)));
}
}
@@ -781,4 +800,13 @@
public void onPluginDisconnected(OverscrollPlugin overscrollPlugin) {
mOverscrollPlugin = null;
}
+
+ @Override
+ public void writeToProto(LauncherTraceProto proto) {
+ if (proto.touchInteractionService == null) {
+ proto.touchInteractionService = new TouchInteractionServiceProto();
+ }
+ proto.touchInteractionService.serviceConnected = true;
+ proto.touchInteractionService.serviceConnected = true;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index d3765c5..5ad48eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -31,8 +31,8 @@
import com.android.launcher3.R;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -117,9 +117,7 @@
if (pointerIndex == -1) {
break;
}
-
- mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
- ev.getEventTime());
+ mMotionPauseDetector.addPosition(ev, pointerIndex);
}
break;
}
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 3ee3c2d..8e7074d 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
@@ -59,6 +59,7 @@
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -77,6 +78,7 @@
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
private final RecentsAnimationDeviceState mDeviceState;
+ private final NavBarPosition mNavBarPosition;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private RecentsAnimationCallbacks mActiveCallbacks;
@@ -126,13 +128,16 @@
Factory handlerFactory) {
super(base);
mDeviceState = deviceState;
+ mNavBarPosition = mDeviceState.getNavBarPosition();
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
- mMotionPauseDetector = new MotionPauseDetector(base);
+ mMotionPauseDetector = new MotionPauseDetector(base, false,
+ mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
+ ? MotionEvent.AXIS_X : MotionEvent.AXIS_Y);
mMotionPauseMinDisplacement = base.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mOnCompleteCallback = onCompleteCallback;
@@ -172,7 +177,7 @@
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
- mDeviceState.getNavBarPosition().getRotationMode()));
+ mNavBarPosition.getRotationMode()));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -285,7 +290,7 @@
if (mDeviceState.isFullyGesturalNavMode()) {
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
- mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
+ mMotionPauseDetector.addPosition(ev);
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
}
}
@@ -354,9 +359,9 @@
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+ float velocity = mNavBarPosition.isRightEdge()
? velocityX
- : mDeviceState.getNavBarPosition().isLeftEdge()
+ : mNavBarPosition.isLeftEdge()
? -velocityX
: velocityY;
@@ -410,9 +415,9 @@
}
private float getDisplacement(MotionEvent ev) {
- if (mDeviceState.getNavBarPosition().isRightEdge()) {
+ if (mNavBarPosition.isRightEdge()) {
return ev.getX() - mDownPos.x;
- } else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
+ } else if (mNavBarPosition.isLeftEdge()) {
return mDownPos.x - ev.getX();
} else {
return ev.getY() - mDownPos.y;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index d5ed321..b9827ff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -23,8 +23,8 @@
import com.android.launcher3.R;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
-import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.MotionPauseDetector;
/**
* An input consumer that detects swipe up and hold to exit screen pinning mode.
@@ -72,7 +72,7 @@
case MotionEvent.ACTION_MOVE:
float displacement = mTouchDownY - y;
mMotionPauseDetector.setDisallowPause(displacement < mMotionPauseMinDisplacement);
- mMotionPauseDetector.addPosition(y, ev.getEventTime());
+ mMotionPauseDetector.addPosition(ev);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 86e5dfa..5a9c2fe 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -238,9 +238,10 @@
Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale);
mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF));
mCurrentRect.offset(params.mOffsetX, 0);
-
- updateClipRect(params);
}
+
+ updateClipRect(params);
+
return mCurrentRect;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java
new file mode 100644
index 0000000..190763a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 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.quickstep.util;
+
+import static com.android.launcher3.tracing.nano.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_H;
+import static com.android.launcher3.tracing.nano.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_L;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import com.android.launcher3.tracing.nano.LauncherTraceProto;
+import com.android.launcher3.tracing.nano.LauncherTraceEntryProto;
+import com.android.launcher3.tracing.nano.LauncherTraceFileProto;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.shared.tracing.FrameProtoTracer;
+import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.google.protobuf.nano.MessageNano;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Queue;
+
+
+/**
+ * Controller for coordinating winscope proto tracing.
+ */
+public class ProtoTracer implements ProtoTraceParams<MessageNano,
+ LauncherTraceFileProto, LauncherTraceEntryProto, LauncherTraceProto> {
+
+ public static final MainThreadInitializedObject<ProtoTracer> INSTANCE =
+ new MainThreadInitializedObject<>(ProtoTracer::new);
+
+ private static final String TAG = "ProtoTracer";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final Context mContext;
+ private final FrameProtoTracer<MessageNano,
+ LauncherTraceFileProto, LauncherTraceEntryProto, LauncherTraceProto> mProtoTracer;
+
+ public ProtoTracer(Context context) {
+ mContext = context;
+ mProtoTracer = new FrameProtoTracer<>(this);
+ }
+
+ @Override
+ public File getTraceFile() {
+ return new File(mContext.getFilesDir(), "launcher_trace.pb");
+ }
+
+ @Override
+ public LauncherTraceFileProto getEncapsulatingTraceProto() {
+ return new LauncherTraceFileProto();
+ }
+
+ @Override
+ public LauncherTraceEntryProto updateBufferProto(LauncherTraceEntryProto reuseObj,
+ ArrayList<ProtoTraceable<LauncherTraceProto>> traceables) {
+ LauncherTraceEntryProto proto = reuseObj != null
+ ? reuseObj
+ : new LauncherTraceEntryProto();
+ proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
+ proto.launcher = proto.launcher != null ? proto.launcher : new LauncherTraceProto();
+ for (ProtoTraceable t : traceables) {
+ t.writeToProto(proto.launcher);
+ }
+ return proto;
+ }
+
+ @Override
+ public byte[] serializeEncapsulatingProto(LauncherTraceFileProto encapsulatingProto,
+ Queue<LauncherTraceEntryProto> buffer) {
+ encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE;
+ encapsulatingProto.entry = buffer.toArray(new LauncherTraceEntryProto[0]);
+ return MessageNano.toByteArray(encapsulatingProto);
+ }
+
+ @Override
+ public byte[] getProtoBytes(MessageNano proto) {
+ return MessageNano.toByteArray(proto);
+ }
+
+ @Override
+ public int getProtoSize(MessageNano proto) {
+ return proto.getCachedSize();
+ }
+
+ public void start() {
+ mProtoTracer.start();
+ }
+
+ public void stop() {
+ mProtoTracer.stop();
+ }
+
+ public void add(ProtoTraceable<LauncherTraceProto> traceable) {
+ mProtoTracer.add(traceable);
+ }
+
+ public void remove(ProtoTraceable<LauncherTraceProto> traceable) {
+ mProtoTracer.remove(traceable);
+ }
+
+ public void scheduleFrameUpdate() {
+ mProtoTracer.scheduleFrameUpdate();
+ }
+
+ public void update() {
+ mProtoTracer.update();
+ }
+}
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 ef1698e..fe78a84 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
@@ -92,6 +92,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -972,6 +973,10 @@
TaskView runningTask = getRunningTaskView();
if (runningTask != null) {
runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
+ if (!isHidden) {
+ AccessibilityManagerCompat.sendCustomAccessibilityEvent(runningTask,
+ AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
+ }
}
}
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 327bb14..a688f9a 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -13,11 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
- <string name="task_overlay_factory_class" translatable="false"></string>
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <string name="task_overlay_factory_class" translatable="false"/>
- <!-- Activity which blocks home gesture -->
- <string name="gesture_blocking_activity" translatable="false"></string>
+ <!-- Activities which block home gesture -->
+ <string-array name="gesture_blocking_activities" tools:ignore="InconsistentArrays">
+ <item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
+ </string-array>
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
@@ -32,5 +34,5 @@
<integer name="assistant_gesture_min_time_threshold">200</integer>
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
- <string name="wellbeing_provider_pkg" translatable="false"></string>
+ <string name="wellbeing_provider_pkg" translatable="false"/>
</resources>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index e88a8a4..b5e05ee 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -54,6 +54,12 @@
}
@Override
+ public void onStateDisabled(Launcher launcher) {
+ super.onStateDisabled(launcher);
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ }
+
+ @Override
public String getDescription(Launcher launcher) {
AllAppsContainerView appsView = launcher.getAppsView();
return appsView.getDescription();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 4b33d21..abe1592 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -66,6 +66,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
/**
* Manages the state of the system during a swipe up gesture.
@@ -107,7 +109,7 @@
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
- private ComponentName mGestureBlockedActivity;
+ private final List<ComponentName> mGestureBlockedActivities;
public RecentsAnimationDeviceState(Context context) {
final ContentResolver resolver = context.getContentResolver();
@@ -142,9 +144,19 @@
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
// Add any blocked activities
- String blockingActivity = context.getString(R.string.gesture_blocking_activity);
- if (!TextUtils.isEmpty(blockingActivity)) {
- mGestureBlockedActivity = ComponentName.unflattenFromString(blockingActivity);
+ String[] blockingActivities;
+ try {
+ blockingActivities =
+ context.getResources().getStringArray(R.array.gesture_blocking_activities);
+ } catch (Resources.NotFoundException e) {
+ blockingActivities = new String[0];
+ }
+ mGestureBlockedActivities = new ArrayList<>(blockingActivities.length);
+ for (String blockingActivity : blockingActivities) {
+ if (!TextUtils.isEmpty(blockingActivity)) {
+ mGestureBlockedActivities.add(
+ ComponentName.unflattenFromString(blockingActivity));
+ }
}
}
@@ -272,17 +284,16 @@
* @return whether the given running task info matches the gesture-blocked activity.
*/
public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) {
- return runningTaskInfo != null && mGestureBlockedActivity != null
- && mGestureBlockedActivity.equals(runningTaskInfo.topActivity);
+ return runningTaskInfo != null
+ && mGestureBlockedActivities.contains(runningTaskInfo.topActivity);
}
/**
- * @return the package of the gesture-blocked activity or {@code null} if there is none.
+ * @return the packages of gesture-blocked activities.
*/
- public String getGestureBlockedActivityPackage() {
- return (mGestureBlockedActivity != null)
- ? mGestureBlockedActivity.getPackageName()
- : null;
+ public List<String> getGestureBlockedActivityPackages() {
+ return mGestureBlockedActivities.stream().map(ComponentName::getPackageName)
+ .collect(Collectors.toList());
}
/**
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
similarity index 76%
rename from quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
rename to quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 295ab48..8081ad7 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -16,7 +16,10 @@
package com.android.quickstep.interaction;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.View;
import android.view.Window;
@@ -26,10 +29,11 @@
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
+import java.util.List;
import java.util.Optional;
-/** Shows the Back gesture interactive tutorial in full screen mode. */
-public class BackGestureTutorialActivity extends FragmentActivity {
+/** Shows the gesture interactive sandbox in full screen mode. */
+public class GestureSandboxActivity extends FragmentActivity {
Optional<BackGestureTutorialFragment> mFragment = Optional.empty();
@@ -47,6 +51,12 @@
}
@Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ disableSystemGestures();
+ }
+
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
@@ -70,4 +80,14 @@
| View.SYSTEM_UI_FLAG_FULLSCREEN);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
+
+ private void disableSystemGestures() {
+ Display display = getDisplay();
+ if (display != null) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ getWindow().setSystemGestureExclusionRects(
+ List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 801a560..d8b10b6 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -37,8 +37,7 @@
private static final long FORCE_PAUSE_TIMEOUT = 300;
/**
- * After {@link #makePauseHarderToTrigger()}, must
- * move slowly for this long to trigger a pause.
+ * After {@link #mMakePauseHarderToTrigger}, must move slowly for this long to trigger a pause.
*/
private static final long HARDER_TRIGGER_TIMEOUT = 400;
@@ -49,13 +48,10 @@
private final Alarm mForcePauseTimeout;
private final boolean mMakePauseHarderToTrigger;
private final Context mContext;
+ private final VelocityProvider mVelocityProvider;
- private Long mPreviousTime = null;
- private Float mPreviousPosition = null;
private Float mPreviousVelocity = null;
- private Float mFirstPosition = null;
-
private OnMotionPauseListener mOnMotionPauseListener;
private boolean mIsPaused;
// Bias more for the first pause to make it feel extra responsive.
@@ -73,6 +69,13 @@
* @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
*/
public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) {
+ this(context, makePauseHarderToTrigger, MotionEvent.AXIS_Y);
+ }
+
+ /**
+ * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
+ */
+ public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger, int axis) {
mContext = context;
Resources res = context.getResources();
mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
@@ -82,6 +85,7 @@
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
+ mVelocityProvider = new LinearVelocityProvider(axis);
}
/**
@@ -101,28 +105,28 @@
/**
* Computes velocity and acceleration to determine whether the motion is paused.
- * @param position The x or y component of the motion being tracked.
+ * @param ev The motion being tracked.
*
* TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
*/
- public void addPosition(float position, long time) {
- if (mFirstPosition == null) {
- mFirstPosition = position;
- }
+ public void addPosition(MotionEvent ev) {
+ addPosition(ev, 0);
+ }
+
+ /**
+ * Computes velocity and acceleration to determine whether the motion is paused.
+ * @param ev The motion being tracked.
+ * @param pointerIndex Index for the pointer being tracked in the motion event
+ */
+ public void addPosition(MotionEvent ev, int pointerIndex) {
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
: FORCE_PAUSE_TIMEOUT);
- if (mPreviousTime != null && mPreviousPosition != null) {
- long changeInTime = Math.max(1, time - mPreviousTime);
- float changeInPosition = position - mPreviousPosition;
- float velocity = changeInPosition / changeInTime;
- if (mPreviousVelocity != null) {
- checkMotionPaused(velocity, mPreviousVelocity, time);
- }
- mPreviousVelocity = velocity;
+ Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
+ if (newVelocity != null && mPreviousVelocity != null) {
+ checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
}
- mPreviousTime = time;
- mPreviousPosition = position;
+ mPreviousVelocity = newVelocity;
}
private void checkMotionPaused(float velocity, float prevVelocity, long time) {
@@ -178,10 +182,8 @@
}
public void clear() {
- mPreviousTime = null;
- mPreviousPosition = null;
+ mVelocityProvider.clear();
mPreviousVelocity = null;
- mFirstPosition = null;
setOnMotionPauseListener(null);
mIsPaused = mHasEverBeenPaused = false;
mSlowStartTime = 0;
@@ -195,4 +197,55 @@
public interface OnMotionPauseListener {
void onMotionPauseChanged(boolean isPaused);
}
+
+ /**
+ * Interface to abstract out velocity calculations
+ */
+ protected interface VelocityProvider {
+
+ /**
+ * Adds a new motion events, and returns the velocity at this point, or null if
+ * the velocity is not available
+ */
+ Float addMotionEvent(MotionEvent ev, int pointer);
+
+ /**
+ * Clears all stored motion event records
+ */
+ void clear();
+ }
+
+ private static class LinearVelocityProvider implements VelocityProvider {
+
+ private Long mPreviousTime = null;
+ private Float mPreviousPosition = null;
+
+ private final int mAxis;
+
+ LinearVelocityProvider(int axis) {
+ mAxis = axis;
+ }
+
+ @Override
+ public Float addMotionEvent(MotionEvent ev, int pointer) {
+ long time = ev.getEventTime();
+ float position = ev.getAxisValue(mAxis, pointer);
+ Float velocity = null;
+
+ if (mPreviousTime != null && mPreviousPosition != null) {
+ long changeInTime = Math.max(1, time - mPreviousTime);
+ float changeInPosition = position - mPreviousPosition;
+ velocity = changeInPosition / changeInTime;
+ }
+ mPreviousTime = time;
+ mPreviousPosition = position;
+ return velocity;
+ }
+
+ @Override
+ public void clear() {
+ mPreviousTime = null;
+ mPreviousPosition = null;
+ }
+ }
}
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 33502d0..1c18076 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -15,9 +15,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="?attr/allAppsScrimColor"
android:padding="8dp"
android:orientation="vertical"
- android:background="?attr/allAppsScrimColor"
android:gravity="center">
<ImageView
@@ -38,7 +38,7 @@
android:layout_marginBottom="8dp"
android:text="@string/work_apps_paused_title"
android:textAlignment="center"
- android:textSize="24sp" />
+ android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
index f7a529d..04094c4 100644
--- a/res/layout/work_profile_edu.xml
+++ b/res/layout/work_profile_edu.xml
@@ -21,22 +21,22 @@
<View
android:layout_width="match_parent"
- android:backgroundTint="@color/bottom_panel_background"
android:layout_height="32dp"
- android:background="@drawable/bottom_sheet_top_border" />
+ android:background="@drawable/bottom_sheet_top_border"
+ android:backgroundTint="?android:attr/colorAccent" />
<LinearLayout
android:id="@+id/view_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/bottom_panel_background"
+ android:background="?android:attr/colorAccent"
android:orientation="vertical"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingRight="@dimen/bottom_sheet_edu_padding">
<TextView
- style="@style/TextHeadline"
android:id="@+id/content_text"
+ style="@style/TextHeadline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
@@ -57,5 +57,4 @@
android:textAlignment="center"
android:textColor="@android:color/white" />
</LinearLayout>
-
</com.android.launcher3.views.WorkEduView>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 36f8468..194ef2c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -45,5 +45,4 @@
<color name="back_gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
- <color name="bottom_panel_background">#f01A73E8</color>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bde3c31..9d0fb56 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. -->
@@ -330,7 +330,7 @@
<!--- User onboarding title for personal apps -->
<string name="work_profile_edu_personal_apps">Personal apps are private & can\'t be seen by IT</string>
<!--- User onboarding title for work profile apps -->
- <string name="work_profile_edu_work_apps">Work apps are badged and monitored by IT</string>
+ <string name="work_profile_edu_work_apps">Work apps are badged & visible to IT</string>
<!-- Action label to proceed to the next work profile edu section-->
<string name="work_profile_edu_next">Next</string>
<!-- Action label to finish work profile edu-->
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3a275ff..3fc8de2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -482,7 +482,6 @@
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
- mAllAppsController.highlightWorkTabIfNecessary();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index ac0fef8..f618fe1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -66,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;
@@ -127,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) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 4c6e1f3..01893e9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -374,6 +374,8 @@
protected void onPageEndTransition() {
mWasInOverscroll = false;
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+ AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
+ AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
protected int getUnboundedScrollX() {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 4e5b031..fcccc59 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -103,6 +103,8 @@
private final MultiValueAlpha mMultiValueAlpha;
+ Rect mInsets = new Rect();
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -325,6 +327,7 @@
@Override
public void setInsets(Rect insets) {
+ mInsets.set(insets);
DeviceProfile grid = mLauncher.getDeviceProfile();
int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+ grid.cellLayoutPaddingLeftRightPx;
@@ -416,6 +419,7 @@
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
this.addView(mWorkFooterContainer);
+ mWorkFooterContainer.setInsets(mInsets);
mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
}
@@ -553,17 +557,6 @@
return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
}
- public void onScrollUpEnd() {
- highlightWorkTabIfNecessary();
- }
-
- void highlightWorkTabIfNecessary() {
- if (mUsingTabs) {
- ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs))
- .highlightWorkTabIfNecessary();
- }
- }
-
/**
* Adds an update listener to {@param animator} that adds springs to the animation.
*/
@@ -648,9 +641,9 @@
R.layout.work_apps_paused, null);
recyclerView.post(() -> {
int width = recyclerView.getWidth();
- int height = recyclerView.getHeight();
- pausedOverlay.measure(makeMeasureSpec(width, EXACTLY),
- makeMeasureSpec(height, EXACTLY));
+ int height = recyclerView.getHeight() - mWorkFooterContainer.getHeight();
+ pausedOverlay.measure(makeMeasureSpec(recyclerView.getWidth(), EXACTLY),
+ makeMeasureSpec(recyclerView.getHeight(), EXACTLY));
pausedOverlay.layout(0, 0, width, height);
applyPadding();
});
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 93bdac9..6aa3efc 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -248,18 +248,6 @@
private void onProgressAnimationEnd() {
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
- } else if (isAllAppsExpanded()) {
- mAppsView.onScrollUpEnd();
- }
- }
-
- private boolean isAllAppsExpanded() {
- return Float.compare(mProgress, 0f) == 0;
- }
-
- public void highlightWorkTabIfNecessary() {
- if (isAllAppsExpanded()) {
- mAppsView.highlightWorkTabIfNecessary();
}
}
}
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index e8035eb..0f0fc3a 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -35,7 +35,6 @@
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.pm.UserCache;
/**
* Abstract base class of floating view responsible for showing discovery bounce animation
@@ -144,8 +143,7 @@
private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
if (!launcher.isInState(NORMAL)
- || (launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
- && !shouldShowForWorkProfile(launcher))
+ || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
|| AbstractFloatingView.getTopOpenView(launcher) != null
|| launcher.getSystemService(UserManager.class).isDemoUser()
|| Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -170,8 +168,7 @@
|| !launcher.hasBeenResumed()
|| launcher.isForceInvisible()
|| launcher.getDeviceProfile().isVerticalBarLayout()
- || (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
- && !shouldShowForWorkProfile(launcher))
+ || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
|| launcher.getSystemService(UserManager.class).isDemoUser()
|| Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
@@ -213,12 +210,6 @@
}
}
- private static boolean shouldShowForWorkProfile(Launcher launcher) {
- return !launcher.getSharedPrefs().getBoolean(
- PersonalWorkSlidingTabStrip.KEY_SHOWED_PEEK_WORK_TAB, false)
- && UserCache.INSTANCE.get(launcher).hasWorkProfile();
- }
-
private static void incrementShelfBounceCount(Launcher launcher) {
SharedPreferences sharedPrefs = launcher.getSharedPrefs();
int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0);
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 9d0ecd3..25db0e7 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -64,6 +64,14 @@
}
@Override
+ public void setupHeader() {
+ super.setupHeader();
+ if (mWorkTabListener != null && !mUsingTabs) {
+ mLauncher.getStateManager().removeStateListener(mWorkTabListener);
+ }
+ }
+
+ @Override
public void onTabChanged(int pos) {
super.onTabChanged(pos);
if (mUsingTabs) {
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index 6204f31..0e39bbe 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -16,7 +16,6 @@
package com.android.launcher3.allapps;
import android.content.Context;
-import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
@@ -39,11 +38,8 @@
private static final int POSITION_PERSONAL = 0;
private static final int POSITION_WORK = 1;
- public static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab";
-
private final Paint mSelectedIndicatorPaint;
private final Paint mDividerPaint;
- private final SharedPreferences mSharedPreferences;
private int mSelectedIndicatorHeight;
private int mIndicatorLeft = -1;
@@ -72,7 +68,6 @@
mDividerPaint.setStrokeWidth(
getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
- mSharedPreferences = Utilities.getPrefs(context);
mIsRtl = Utilities.isRtl(getResources());
}
@@ -128,25 +123,6 @@
mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
}
- public void highlightWorkTabIfNecessary() {
- if (mSharedPreferences.getBoolean(KEY_SHOWED_PEEK_WORK_TAB, false)) {
- return;
- }
- if (mLastActivePage != POSITION_PERSONAL) {
- return;
- }
- highlightWorkTab();
- mSharedPreferences.edit().putBoolean(KEY_SHOWED_PEEK_WORK_TAB, true).apply();
- }
-
- private void highlightWorkTab() {
- View v = getChildAt(POSITION_WORK);
- v.post(() -> {
- v.setPressed(true);
- v.setPressed(false);
- });
- }
-
@Override
public void setScroll(int currentScroll, int totalScroll) {
float scrollOffset = ((float) currentScroll) / totalScroll;
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 28579c1..db4bef0 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -18,11 +18,14 @@
import android.content.Context;
import android.os.Bundle;
+import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
@@ -37,11 +40,13 @@
return isAccessibilityEnabled(context);
}
- public static void sendCustomAccessibilityEvent(View target, int type, String text) {
+ public static void sendCustomAccessibilityEvent(View target, int type, @Nullable String text) {
if (isObservedEventType(target.getContext(), type)) {
AccessibilityEvent event = AccessibilityEvent.obtain(type);
target.onInitializeAccessibilityEvent(event);
- event.getText().add(text);
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
+ }
getManager(target.getContext()).sendAccessibilityEvent(event);
}
}
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 1b45f10..ddb88dc 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -33,6 +33,7 @@
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
+import static java.util.Optional.ofNullable;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -95,6 +96,7 @@
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.Executors;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -322,12 +324,11 @@
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
if (isEmpty(mFolderName.getText())) {
- FolderNameInfo[] nameInfos =
- (FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
- FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
- if (nameInfos != null) {
- showLabelSuggestion(nameInfos);
- }
+ ofNullable(mInfo)
+ .map(info -> info.suggestedFolderNames)
+ .map(folderNames -> (FolderNameInfo[]) folderNames
+ .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+ .ifPresent(nameInfos -> showLabelSuggestion(nameInfos, false));
}
}
mFolderName.setHint("");
@@ -350,18 +351,11 @@
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 (isEmpty(mInfo.title)) {
- mFolderName.setHint(R.string.folder_hint_text);
- mFolderName.setText("");
- } else {
- mFolderName.setHint(null);
- }
+ mFolderName.setHint(null);
}
sendCustomAccessibilityEvent(
@@ -433,7 +427,7 @@
mInfo = info;
ArrayList<WorkspaceItemInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
- updateItemLocationsInDatabaseBatch();
+ updateItemLocationsInDatabaseBatch(true);
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
if (lp == null) {
@@ -451,11 +445,7 @@
mFolderName.setHint(null);
} else {
mFolderName.setText("");
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mFolderName.setHint("");
- } else {
- mFolderName.setHint(R.string.folder_hint_text);
- }
+ mFolderName.setHint(R.string.folder_hint_text);
}
// In case any children didn't come across during loading, clean up the folder accordingly
mFolderIcon.post(() -> {
@@ -471,11 +461,9 @@
*/
public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
- nameInfos);
if (isEmpty(mFolderName.getText().toString())
&& !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
- showLabelSuggestion(nameInfos);
+ showLabelSuggestion(nameInfos, true);
}
}
}
@@ -484,7 +472,7 @@
* Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
* InputMethodManager suggestions.
*/
- private void showLabelSuggestion(FolderNameInfo[] nameInfos) {
+ private void showLabelSuggestion(FolderNameInfo[] nameInfos, boolean animate) {
if (nameInfos == null) {
return;
}
@@ -494,15 +482,16 @@
nameInfos[0].getLabel())
|| nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
nameInfos[1].getLabel());
- CharSequence firstLabel = nameInfos[0].getLabel();
if (shouldOpen) {
+ CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
if (!isEmpty(firstLabel)) {
mFolderName.setHint("");
mFolderName.setText(firstLabel);
- mInfo.title = firstLabel;
}
- animateOpen(mInfo.contents, 0, true);
+ if (animate) {
+ animateOpen(mInfo.contents, 0, true);
+ }
mFolderName.showKeyboard();
mFolderName.displayCompletions(
asList(nameInfos).subList(1, nameInfos.length).stream()
@@ -991,7 +980,7 @@
// Reordering may have occured, and we need to save the new item locations. We do this once
// at the end to prevent unnecessary database operations.
- updateItemLocationsInDatabaseBatch();
+ updateItemLocationsInDatabaseBatch(false);
// Use the item count to check for multi-page as the folder UI may not have
// been refreshed yet.
if (getItemCount() <= mContent.itemsPerPage()) {
@@ -1001,7 +990,7 @@
}
}
- private void updateItemLocationsInDatabaseBatch() {
+ private void updateItemLocationsInDatabaseBatch(boolean isBind) {
FolderGridOrganizer verifier = new FolderGridOrganizer(
mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
@@ -1017,6 +1006,18 @@
if (!items.isEmpty()) {
mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
}
+ if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind) {
+ Executors.MODEL_EXECUTOR.post(() -> {
+ FolderNameInfo[] nameInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
+ fnp.getSuggestedFolderName(
+ getContext(), mInfo.contents, nameInfos);
+ mInfo.suggestedFolderNames = new Intent().putExtra(
+ FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
+ nameInfos);
+ });
+ }
}
public void notifyDrop() {
@@ -1321,7 +1322,7 @@
// We only need to update the locations if it doesn't get handled in
// #onDropCompleted.
if (d.dragSource != this) {
- updateItemLocationsInDatabaseBatch();
+ updateItemLocationsInDatabaseBatch(false);
}
}
@@ -1362,7 +1363,7 @@
verifier.updateRankAndPos(item, rank);
mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
item.cellY);
- updateItemLocationsInDatabaseBatch();
+ updateItemLocationsInDatabaseBatch(false);
if (mContent.areViewsBound()) {
mContent.createAndAddViewForRank(item, rank);
@@ -1653,25 +1654,8 @@
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);
-
+ Optional<String[]> suggestedLabels = getSuggestedLabels();
+ int accepted_suggestion_index = getAcceptedSuggestionIndex();
boolean hasValidPrimary = suggestedLabels
.map(labels -> labels.length > 0 && !isEmpty(labels[0]))
.orElse(false);
@@ -1700,6 +1684,39 @@
: Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
}
+ private Optional<String[]> getSuggestedLabels() {
+ return ofNullable(mInfo)
+ .map(info -> info.suggestedFolderNames)
+ .map(
+ folderNames ->
+ (FolderNameInfo[])
+ folderNames.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+ .map(
+ folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .filter(Objects::nonNull)
+ .map(FolderNameInfo::getLabel)
+ .filter(Objects::nonNull)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+ }
+
+ private int getAcceptedSuggestionIndex() {
+ String newLabel =
+ checkNotNull(mFolderName.getText().toString(),
+ "Expected valid folder label, but found null");
+
+ return getSuggestedLabels()
+ .map(suggestionsArray ->
+ IntStream.range(0, suggestionsArray.length)
+ .filter(index -> newLabel.equalsIgnoreCase(
+ suggestionsArray[index]))
+ .findFirst()
+ .orElse(-1)
+ ).orElse(-1);
+
+ }
+
private Target.Builder newEditTextTargetBuilder() {
return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
diff --git a/src/com/android/launcher3/folder/FolderNameInfo.java b/src/com/android/launcher3/folder/FolderNameInfo.java
index eb9da90..1287219 100644
--- a/src/com/android/launcher3/folder/FolderNameInfo.java
+++ b/src/com/android/launcher3/folder/FolderNameInfo.java
@@ -80,4 +80,10 @@
public int describeContents() {
return 0;
}
+
+ @Override
+ @NonNull
+ public String toString() {
+ return String.format("%s:%.2f", mLabel, mScore);
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index d5990fa..184dbb9 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -21,9 +21,14 @@
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;
@@ -50,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
@@ -58,9 +65,30 @@
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 static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
+ IntSparseArrayMap<FolderInfo> folderInfos) {
+ FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
+ context.getApplicationContext(), R.string.folder_name_provider_class);
+ fnp.load(appInfos, folderInfos);
+
+ return fnp;
+ }
+
+ private void load(Context context) {
+ LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
+ new FolderNameWorker());
+ }
+
+ private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
+ mAppInfos = appInfos;
+ mFolderInfos = folderInfos;
+ }
+
/**
* Generate and rank the suggested Folder names.
*/
@@ -92,9 +120,8 @@
.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(nameInfos, i.title.toString()));
}
@@ -103,6 +130,15 @@
}
}
+ 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;
@@ -143,4 +179,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/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index def76e8..a429af2 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -25,6 +25,7 @@
import android.annotation.TargetApi;
import android.app.Fragment;
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -55,6 +56,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -72,6 +74,8 @@
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -248,6 +252,16 @@
addInScreenFromBind(folderIcon, info);
}
+ private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
+ WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
+ info.providerName);
+ AppWidgetHostView view = new AppWidgetHostView(mContext);
+ view.setAppWidget(-1, widgetItem.widgetInfo);
+ view.updateAppWidget(null);
+ view.setTag(info);
+ addInScreenFromBind(view, info);
+ }
+
private void dispatchVisibilityAggregated(View view, boolean isVisible) {
// Similar to View.dispatchVisibilityAggregated implementation.
final boolean thisVisible = view.getVisibility() == VISIBLE;
@@ -272,9 +286,9 @@
mContext).getModel();
final WorkspaceItemsInfoFetcher fetcher = new WorkspaceItemsInfoFetcher();
launcherModel.enqueueModelUpdateTask(fetcher);
- ArrayList<ItemInfo> workspaceItems;
+ WorkspaceResult workspaceResult;
try {
- workspaceItems = fetcher.mTask.get(5, TimeUnit.SECONDS);
+ workspaceResult = fetcher.mTask.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Log.d(TAG, "Error fetching workspace items info", e);
return;
@@ -284,9 +298,14 @@
// items
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
- filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceItems,
- currentWorkspaceItems, otherWorkspaceItems);
+ filterCurrentWorkspaceItems(0 /* currentScreenId */,
+ workspaceResult.mWorkspaceItems, currentWorkspaceItems,
+ otherWorkspaceItems);
+ filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
+ currentAppWidgets, otherAppWidgets);
sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
for (ItemInfo itemInfo : currentWorkspaceItems) {
@@ -303,6 +322,17 @@
break;
}
}
+ for (ItemInfo itemInfo : currentAppWidgets) {
+ switch (itemInfo.itemType) {
+ case Favorites.ITEM_TYPE_APPWIDGET:
+ case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+ inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+ workspaceResult.mWidgetsModel);
+ break;
+ default:
+ break;
+ }
+ }
} else {
// Add hotseat icons
for (int i = 0; i < mIdp.numHotseatIcons; i++) {
@@ -349,10 +379,10 @@
}
}
- private static class WorkspaceItemsInfoFetcher implements Callable<ArrayList<ItemInfo>>,
+ private static class WorkspaceItemsInfoFetcher implements Callable<WorkspaceResult>,
LauncherModel.ModelUpdateTask {
- private final FutureTask<ArrayList<ItemInfo>> mTask = new FutureTask<>(this);
+ private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
private LauncherAppState mApp;
private LauncherModel mModel;
@@ -374,14 +404,16 @@
}
@Override
- public ArrayList<ItemInfo> call() throws Exception {
+ public WorkspaceResult call() throws Exception {
if (!mModel.isModelLoaded()) {
Log.d(TAG, "Workspace not loaded, loading now");
mModel.startLoaderForResults(
new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
- return new ArrayList<>();
+ return null;
}
- return mBgDataModel.workspaceItems;
+
+ return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
+ mBgDataModel.widgetsModel);
}
}
@@ -389,4 +421,17 @@
view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
view.layout(0, 0, width, height);
}
+
+ private static class WorkspaceResult {
+ private final ArrayList<ItemInfo> mWorkspaceItems;
+ private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
+ private final WidgetsModel mWidgetsModel;
+
+ private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
+ ArrayList<LauncherAppWidgetInfo> appWidgets, WidgetsModel widgetsModel) {
+ mWorkspaceItems = workspaceItems;
+ mAppWidgets = appWidgets;
+ mWidgetsModel = widgetsModel;
+ }
+ }
}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 67f07b1..a3fdf8d 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -10,6 +10,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
@@ -42,7 +43,7 @@
private static Handler sHandler = null;
private static File sLogsDirectory = null;
- public static final int LOG_DAYS = 4;
+ public static final int LOG_DAYS = FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() ? 10 : 4;
public static void setDir(File logsDir) {
if (ENABLED) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index af802ef..6223a23 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -901,7 +901,8 @@
}
private void loadFolderNames() {
- FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext());
+ FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+ mBgAllAppsList.data, mBgDataModel.folders);
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index 3ef48cd..2fc064c 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -19,6 +19,8 @@
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherSettings;
+import java.util.Objects;
+
/**
* Represents a {@link Package} in the widget tray section.
*/
@@ -48,4 +50,17 @@
public PackageItemInfo clone() {
return new PackageItemInfo(this);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PackageItemInfo that = (PackageItemInfo) o;
+ return Objects.equals(packageName, that.packageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(packageName);
+ }
}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 9334c46..bdba39c 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import android.animation.Animator;
@@ -62,6 +64,7 @@
protected final ObjectAnimator mOpenCloseAnimator;
protected View mContent;
+ private final View mColorScrim;
protected Interpolator mScrollInterpolator;
// range [0, 1], 0=> completely open, 1=> completely closed
@@ -85,11 +88,30 @@
announceAccessibilityChanges();
}
});
+ int scrimColor = getScrimColor(mLauncher);
+ mColorScrim = scrimColor != -1 ? createColorScrim(mLauncher, scrimColor) : null;
+ }
+
+ protected void attachToContainer() {
+ if (mColorScrim != null) {
+ getPopupContainer().addView(mColorScrim);
+ }
+ getPopupContainer().addView(this);
+ }
+
+ /**
+ * Returns a scrim color for a sliding view. if returned value is -1, no scrim is added.
+ */
+ protected int getScrimColor(Context context) {
+ return -1;
}
protected void setTranslationShift(float translationShift) {
mTranslationShift = translationShift;
mContent.setTranslationY(mTranslationShift * mContent.getHeight());
+ if (mColorScrim != null) {
+ mColorScrim.setAlpha(1 - mTranslationShift);
+ }
}
@Override
@@ -127,7 +149,8 @@
/* SingleAxisSwipeDetector.Listener */
@Override
- public void onDragStart(boolean start) { }
+ public void onDragStart(boolean start) {
+ }
@Override
public boolean onDrag(float displacement) {
@@ -185,9 +208,25 @@
protected void onCloseComplete() {
mIsOpen = false;
getPopupContainer().removeView(this);
+ if (mColorScrim != null) {
+ getPopupContainer().removeView(mColorScrim);
+ }
}
protected BaseDragLayer getPopupContainer() {
return mLauncher.getDragLayer();
}
+
+
+ protected static View createColorScrim(Context context, int bgColor) {
+ View view = new View(context);
+ view.forceHasOverlappingRendering(false);
+ view.setBackgroundColor(bgColor);
+
+ BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
+ lp.ignoreInsets = true;
+ view.setLayoutParams(lp);
+
+ return view;
+ }
}
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index b6c81ae..81f8327 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -52,11 +52,15 @@
private static final int WORK_EDU_PERSONAL_APPS = 1;
private static final int WORK_EDU_WORK_APPS = 2;
+ protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
+
+
private Rect mInsets = new Rect();
private View mViewWrapper;
private Button mProceedButton;
private TextView mContentText;
private AllAppsPagedView mAllAppsPagedView;
+
private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
@@ -141,10 +145,15 @@
}
private void show() {
- mLauncher.getDragLayer().addView(this);
+ attachToContainer();
animateOpen();
}
+ @Override
+ protected int getScrimColor(Context context) {
+ return FINAL_SCRIM_BG_COLOR;
+ }
+
private void goToFirstPage() {
if (mAllAppsPagedView != null) {
mAllAppsPagedView.snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 6cae43d..df1a469 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.widget;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
@@ -42,7 +40,6 @@
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.AbstractSlideInView;
-import com.android.launcher3.views.BaseDragLayer;
/**
* Base class for various widgets popup
@@ -55,11 +52,14 @@
/* Touch handling related member variables. */
private Toast mWidgetInstructionToast;
- protected final View mColorScrim;
-
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mColorScrim = createColorScrim(context);
+ }
+
+ protected int getScrimColor(Context context) {
+ WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
+ int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
+ return setColorAlphaBound(colors.getSecondaryColor(), alpha);
}
@Override
@@ -98,16 +98,6 @@
return true;
}
- protected void attachToContainer() {
- getPopupContainer().addView(mColorScrim);
- getPopupContainer().addView(this);
- }
-
- protected void setTranslationShift(float translationShift) {
- super.setTranslationShift(translationShift);
- mColorScrim.setAlpha(1 - mTranslationShift);
- }
-
private boolean beginDraggingWidget(WidgetCell v) {
// Get the widget preview as the drag representation
WidgetImageView image = v.getWidgetView();
@@ -138,7 +128,6 @@
protected void onCloseComplete() {
super.onCloseComplete();
- getPopupContainer().removeView(mColorScrim);
clearNavBarColor();
}
@@ -177,19 +166,4 @@
protected SystemUiController getSystemUiController() {
return mLauncher.getSystemUiController();
}
-
- private static View createColorScrim(Context context) {
- View view = new View(context);
- view.forceHasOverlappingRendering(false);
-
- WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
- int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
- view.setBackgroundColor(setColorAlphaBound(colors.getSecondaryColor(), alpha));
-
- BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
- lp.ignoreInsets = true;
- view.setLayoutParams(lp);
-
- return view;
- }
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 282867a..ae32692 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -6,6 +6,7 @@
import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
@@ -243,4 +244,16 @@
}
}
}
+
+ public WidgetItem getWidgetProviderInfoByProviderName(
+ ComponentName providerName) {
+ ArrayList<WidgetItem> widgetsList = mWidgetsList.get(
+ new PackageItemInfo(providerName.getPackageName()));
+ for (WidgetItem item : widgetsList) {
+ if (item.componentName.equals(providerName)) {
+ return item;
+ }
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 5aa0090..6fe6739 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -73,11 +73,10 @@
mDevice.pressHome();
waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-
waitForLauncherCondition("Personal tab is missing",
- launcher -> launcher.getAppsView().isPersonalTabVisible());
+ launcher -> launcher.getAppsView().isPersonalTabVisible(), 60000);
waitForLauncherCondition("Work tab is missing",
- launcher -> launcher.getAppsView().isWorkTabVisible());
+ launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
}
@Test
@@ -89,7 +88,7 @@
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());
+ launcher -> launcher.getAppsView().getWorkFooterContainer(), 60000);
UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
assertEquals(2, userManager.getUserProfiles().size());
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c5fbd7c..b3b887d 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -69,22 +69,16 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
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;
import java.util.function.Supplier;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -97,16 +91,8 @@
private static final String TAG = "Tapl";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
private static final int GESTURE_STEP_MS = 16;
- private static final SimpleDateFormat DATE_TIME_FORMAT =
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
private static long START_TIME = System.currentTimeMillis();
- 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 + ": (?<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");
@@ -176,11 +162,10 @@
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 Map<String, List<Pattern>> mExpectedEvents;
+ private static LogEventChecker sEventChecker;
+ // True if there is an gesture in progress that needs event verification.
+ private static boolean sCheckingEvents;
- private Date mStartRecordingTime;
private boolean mCheckEventsForSuccessfulGestures = false;
private static Pattern getTouchEventPattern(String prefix, String action) {
@@ -434,10 +419,12 @@
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
- final String eventMismatch = getEventMismatchMessage(false);
-
- if (eventMismatch != null) {
- message = message + ", having produced " + eventMismatch;
+ if (sCheckingEvents) {
+ sCheckingEvents = false;
+ final String eventMismatch = sEventChecker.verify(0);
+ if (eventMismatch != null) {
+ message = message + ", having produced " + eventMismatch;
+ }
}
Assert.fail(formatSystemHealthMessage(message));
@@ -1245,204 +1232,34 @@
return tasks;
}
- // 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 min
- // earlier.
- 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.
- if (DATE_TIME_FORMAT.parse(matcher.group("time"))
- .compareTo(mStartRecordingTime) < 0) {
- continue;
- }
-
- eventsListForSequence(matcher.group("sequence"), events).add(
- matcher.group("event"));
- }
- return events;
- } catch (IOException e) {
- throw new RuntimeException(e);
- } catch (ParseException e) {
- throw new AssertionError(e);
- }
- }
-
- // 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 HashMap<>();
- mStartRecordingTime = new Date();
- log("startRecordingEvents: " + DATE_TIME_FORMAT.format(mStartRecordingTime));
- }
-
- private void stopRecordingEvents() {
- mExpectedEvents = null;
- mStartRecordingTime = null;
- }
-
public Closable eventsCheck() {
+ Assert.assertTrue("Nested event checking", !sCheckingEvents);
if ("com.android.launcher3".equals(getLauncherPackageName())) {
// Not checking specific Launcher3 event sequences.
return () -> {
};
}
-
- // Entering events check block.
- startRecordingEvents();
+ sCheckingEvents = true;
+ if (sEventChecker == null) sEventChecker = new LogEventChecker();
+ sEventChecker.start();
return () -> {
- // Leaving events check block.
- if (mExpectedEvents == null) {
- return; // There was a failure. Noo need to report another one.
- }
-
- if (!mCheckEventsForSuccessfulGestures) {
- stopRecordingEvents();
- return;
- }
-
- final String message = getEventMismatchMessage(true);
- if (message != null) {
- Assert.fail(formatSystemHealthMessage(
- "http://go/tapl : successful gesture produced " + message));
+ if (sCheckingEvents) {
+ sCheckingEvents = false;
+ if (mCheckEventsForSuccessfulGestures) {
+ final String message = sEventChecker.verify(WAIT_TIME_MS);
+ if (message != null) {
+ Assert.fail(formatSystemHealthMessage(
+ "http://go/tapl : successful gesture produced " + message));
+ }
+ } else {
+ sEventChecker.finishNoWait();
+ }
}
};
}
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 {
- 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
- && !receivedEnoughEvents(actual)) {
- SystemClock.sleep(100);
- actual = getEvents();
- }
- }
-
- return getEventMismatchErrorMessage(actual);
- } finally {
- stopRecordingEvents();
- }
- }
-
- // 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(i == position ? "---> " : " ");
- sb.append(events.get(i).toString());
- }
- if (position == events.size()) sb.append("\n | ---> (end)");
+ if (sCheckingEvents) sEventChecker.expectPattern(sequence, expected);
}
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
new file mode 100644
index 0000000..0fc88ee
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -0,0 +1,233 @@
+/*
+ * 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.tapl;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import android.util.Log;
+
+import com.android.launcher3.testing.TestProtocol;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utility class to read log on a background thread.
+ */
+public class LogEventChecker {
+
+ private static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
+ ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / (?<event>.*)");
+
+ private static final String START_PREFIX = "START_READER ";
+ private static final String FINISH_PREFIX = "FINISH_READER ";
+
+ private volatile CountDownLatch mFinished;
+
+ // Map from an event sequence name to an ordered list of expected events in that sequence.
+ private final ListMap<Pattern> mExpectedEvents = new ListMap<>();
+
+ private final ListMap<String> mEvents = new ListMap<>();
+ private final Semaphore mEventsCounter = new Semaphore(0);
+
+ private volatile String mStartCommand;
+ private volatile String mFinishCommand;
+
+ LogEventChecker() {
+ final Thread thread = new Thread(this::onRun, "log-reader-thread");
+ thread.setPriority(Thread.NORM_PRIORITY);
+ thread.start();
+ }
+
+ void start() {
+ if (mFinished != null) {
+ try {
+ mFinished.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ mFinished = null;
+ }
+ mEvents.clear();
+ mExpectedEvents.clear();
+ mEventsCounter.drainPermits();
+ final String id = UUID.randomUUID().toString();
+ mStartCommand = START_PREFIX + id;
+ mFinishCommand = FINISH_PREFIX + id;
+ Log.d(TestProtocol.TAPL_EVENTS_TAG, mStartCommand);
+ }
+
+ private void onRun() {
+ try {
+ // Note that we use Runtime.exec to start the log reading process instead of running
+ // it via UIAutomation, so that we can directly access the "Process" object and
+ // ensure that the instrumentation is not stuck forever.
+ final String cmd = "logcat -s " + TestProtocol.TAPL_EVENTS_TAG;
+
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(
+ Runtime.getRuntime().exec(cmd).getInputStream()))) {
+ for (;;) {
+ // Skip everything before the next start command.
+ for (;;) {
+ final String event = reader.readLine();
+ if (event.contains(TestProtocol.TAPL_EVENTS_TAG)
+ && event.contains(mStartCommand)) {
+ break;
+ }
+ }
+
+ // Store all actual events until the finish command.
+ for (;;) {
+ final String event = reader.readLine();
+ if (event.contains(TestProtocol.TAPL_EVENTS_TAG)) {
+ if (event.contains(mFinishCommand)) {
+ mFinished.countDown();
+ break;
+ } else {
+ final Matcher matcher = EVENT_LOG_ENTRY.matcher(event);
+ if (matcher.find()) {
+ mEvents.add(matcher.group("sequence"), matcher.group("event"));
+ mEventsCounter.release();
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void expectPattern(String sequence, Pattern pattern) {
+ mExpectedEvents.add(sequence, pattern);
+ }
+
+ private void finishSync(long waitForExpectedCountMs) {
+ try {
+ // Wait until Launcher generates the expected number of events.
+ int expectedCount = mExpectedEvents.entrySet()
+ .stream().mapToInt(e -> e.getValue().size()).sum();
+ mEventsCounter.tryAcquire(expectedCount, waitForExpectedCountMs, MILLISECONDS);
+ finishNoWait();
+ mFinished.await();
+ mFinished = null;
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void finishNoWait() {
+ mFinished = new CountDownLatch(1);
+ Log.d(TestProtocol.TAPL_EVENTS_TAG, mFinishCommand);
+ }
+
+ String verify(long waitForExpectedCountMs) {
+ finishSync(waitForExpectedCountMs);
+
+ final StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, List<Pattern>> expectedEvents : mExpectedEvents.entrySet()) {
+ String sequence = expectedEvents.getKey();
+
+ List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
+ final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
+ if (mismatchPosition != -1) {
+ formatSequenceWithMismatch(
+ sb,
+ sequence,
+ expectedEvents.getValue(),
+ actual,
+ mismatchPosition);
+ }
+ }
+ // Check for unexpected event sequences in the actual data.
+ for (String actualNamedSequence : mEvents.keySet()) {
+ if (!mExpectedEvents.containsKey(actualNamedSequence)) {
+ formatSequenceWithMismatch(
+ sb,
+ actualNamedSequence,
+ new ArrayList<>(),
+ mEvents.get(actualNamedSequence),
+ 0);
+ }
+ }
+
+ return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
+ }
+
+ // 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;
+ }
+
+ 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(i == position ? "---> " : " ");
+ sb.append(events.get(i).toString());
+ }
+ if (position == events.size()) sb.append("\n | ---> (end)");
+ }
+
+ private static class ListMap<T> extends HashMap<String, List<T>> {
+
+ void add(String key, T value) {
+ getNonNull(key).add(value);
+ }
+
+ List<T> getNonNull(String key) {
+ List<T> list = get(key);
+ if (list == null) {
+ list = new ArrayList<>();
+ put(key, list);
+ }
+ return list;
+ }
+ }
+}