diff --git a/Android.bp b/Android.bp index cdada0a87f..f45394a998 100644 --- a/Android.bp +++ b/Android.bp @@ -17,7 +17,7 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } -min_launcher3_sdk_version = "26" +min_launcher3_sdk_version = "30" // Common source files used to build launcher (java and kotlin) // All sources are split so they can be reused in many other libraries/apps in other folders @@ -69,22 +69,6 @@ filegroup { ], } -filegroup { - name: "launcher-ext_tests", - srcs: [ - "ext_tests/**/*.java", - "ext_tests/**/*.kt", - ], -} - -filegroup { - name: "launcher-quickstep-ext_tests", - srcs: [ - "quickstep/ext_tests/**/*.java", - "quickstep/ext_tests/**/*.kt", - ], -} - // Proguard files for Launcher3 filegroup { name: "launcher-proguard-rules", @@ -186,7 +170,7 @@ android_library { sdk_version: "current", min_sdk_version: min_launcher3_sdk_version, lint: { - baseline_filename: "lint-baseline2.xml", + baseline_filename: "lint-baseline.xml", }, } @@ -212,7 +196,7 @@ android_library { min_sdk_version: min_launcher3_sdk_version, manifest: "AndroidManifest-common.xml", lint: { - baseline_filename: "lint-baseline2.xml", + baseline_filename: "lint-baseline.xml", }, } @@ -229,11 +213,8 @@ android_app { ":launcher-src", ":launcher-src_shortcuts_overrides", ":launcher-src_ui_overrides", - ":launcher-ext_tests", - ], - resource_dirs: [ - "ext_tests/res", ], + optimize: { proguard_flags_files: ["proguard.flags"], // Proguard is disable for testing. Derivarive prjects to keep proguard enabled diff --git a/ext_tests/res/values/overrides.xml b/ext_tests/res/values/overrides.xml deleted file mode 100644 index 3f071d4219..0000000000 --- a/ext_tests/res/values/overrides.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - com.android.launcher3.testing.DebugTestInformationHandler - - diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java deleted file mode 100644 index 6e7a82ae5c..0000000000 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.testing; - -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; - -import android.app.Activity; -import android.app.Application; -import android.content.Context; -import android.os.Binder; -import android.os.Bundle; -import android.system.Os; - -import androidx.annotation.Keep; -import androidx.annotation.Nullable; - -import com.android.launcher3.BubbleTextView; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel; -import com.android.launcher3.ShortcutAndWidgetContainer; -import com.android.launcher3.icons.ClockDrawableWrapper; -import com.android.launcher3.testing.shared.TestProtocol; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Class to handle requests from tests, including debug ones. - */ -public class DebugTestInformationHandler extends TestInformationHandler { - private static Collection sEvents; - private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks; - private static final Map sActivities = - Collections.synchronizedMap(new WeakHashMap<>()); - private static int sActivitiesCreatedCount = 0; - - public DebugTestInformationHandler(Context context) { - init(context); - if (sActivityLifecycleCallbacks == null) { - sActivityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - sActivities.put(activity, true); - ++sActivitiesCreatedCount; - } - - @Override - public void onActivityStarted(Activity activity) { - } - - @Override - public void onActivityResumed(Activity activity) { - } - - @Override - public void onActivityPaused(Activity activity) { - } - - @Override - public void onActivityStopped(Activity activity) { - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { - } - - @Override - public void onActivityDestroyed(Activity activity) { - } - }; - ((Application) context.getApplicationContext()) - .registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks); - } - } - - private static void runGcAndFinalizersSync() { - Runtime.getRuntime().gc(); - Runtime.getRuntime().runFinalization(); - - final CountDownLatch fence = new CountDownLatch(1); - createFinalizationObserver(fence); - try { - do { - Runtime.getRuntime().gc(); - Runtime.getRuntime().runFinalization(); - } while (!fence.await(100, TimeUnit.MILLISECONDS)); - } catch (InterruptedException ex) { - throw new RuntimeException(ex); - } - } - - // Create the observer in the scope of a method to minimize the chance that - // it remains live in a DEX/machine register at the point of the fence guard. - // This must be kept to avoid R8 inlining it. - @Keep - private static void createFinalizationObserver(CountDownLatch fence) { - new Object() { - @Override - protected void finalize() throws Throwable { - try { - fence.countDown(); - } finally { - super.finalize(); - } - } - }; - } - - @Override - public Bundle call(String method, String arg, @Nullable Bundle extras) { - final Bundle response = new Bundle(); - switch (method) { - case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: { - return getLauncherUIProperty(Bundle::putInt, - l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags()); - } - - case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING: - TestProtocol.sDebugTracing = true; - ClockDrawableWrapper.sRunningInTest = true; - return response; - - case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING: - TestProtocol.sDebugTracing = false; - ClockDrawableWrapper.sRunningInTest = false; - return response; - - case TestProtocol.REQUEST_PID: { - response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid()); - return response; - } - - case TestProtocol.REQUEST_FORCE_GC: { - runGcAndFinalizersSync(); - return response; - } - - case TestProtocol.REQUEST_START_EVENT_LOGGING: { - sEvents = new ArrayList<>(); - TestLogging.setEventConsumer( - (sequence, event) -> { - final Collection events = sEvents; - if (events != null) { - synchronized (events) { - events.add(sequence + '/' + event); - } - } - }); - return response; - } - - case TestProtocol.REQUEST_STOP_EVENT_LOGGING: { - TestLogging.setEventConsumer(null); - sEvents = null; - return response; - } - - case TestProtocol.REQUEST_GET_TEST_EVENTS: { - if (sEvents == null) { - // sEvents can be null if Launcher died and restarted after - // REQUEST_START_EVENT_LOGGING. - return response; - } - - synchronized (sEvents) { - response.putStringArrayList( - TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents)); - } - return response; - } - - case TestProtocol.REQUEST_REINITIALIZE_DATA: { - final long identity = Binder.clearCallingIdentity(); - try { - MODEL_EXECUTOR.execute(() -> { - LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); - model.getModelDbController().createEmptyDB(); - MAIN_EXECUTOR.execute(model::forceReload); - }); - return response; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - case TestProtocol.REQUEST_CLEAR_DATA: { - final long identity = Binder.clearCallingIdentity(); - try { - MODEL_EXECUTOR.execute(() -> { - LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); - model.getModelDbController().createEmptyDB(); - model.getModelDbController().clearEmptyDbFlag(); - MAIN_EXECUTOR.execute(model::forceReload); - }); - return response; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: { - return getLauncherUIProperty(Bundle::putStringArrayList, l -> { - ShortcutAndWidgetContainer hotseatIconsContainer = - l.getHotseat().getShortcutsAndWidgets(); - ArrayList hotseatIconNames = new ArrayList<>(); - - for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) { - // Use unchecked cast to catch changes in hotseat layout - BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i); - hotseatIconNames.add((String) icon.getText()); - } - - return hotseatIconNames; - }); - } - - case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: { - response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount); - return response; - } - - case TestProtocol.REQUEST_GET_ACTIVITIES: { - response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD, - sActivities.keySet().stream().map( - a -> a.getClass().getSimpleName() + " (" - + (a.isDestroyed() ? "destroyed" : "current") + ")") - .toArray(String[]::new)); - return response; - } - - case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED: - return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new); - - default: - return super.call(method, arg, extras); - } - } -} diff --git a/lint-baseline.xml b/lint-baseline.xml index fe005cadf0..2ee9531298 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -3,13 +3,13 @@ + message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)" + errorLine1=' <corners android:radius="@android:dimen/system_app_widget_background_radius" />' + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml" + line="20" + column="14"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lint-baseline2.xml b/lint-baseline2.xml deleted file mode 100644 index 84f1b15d54..0000000000 --- a/lint-baseline2.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java deleted file mode 100644 index 0b17a7b7d7..0000000000 --- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2022 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; - -import android.content.Context; -import android.os.Bundle; - -import androidx.annotation.Nullable; - -import com.android.launcher3.testing.DebugTestInformationHandler; -import com.android.launcher3.testing.shared.TestProtocol; - -/** - * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds. - */ -public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler { - - private final DebugTestInformationHandler mDebugTestInformationHandler; - - public DebugQuickstepTestInformationHandler(Context context) { - super(context); - mDebugTestInformationHandler = new DebugTestInformationHandler(context); - } - - @Override - public Bundle call(String method, String arg, @Nullable Bundle extras) { - Bundle response = new Bundle(); - if (TestProtocol.REQUEST_RECREATE_TASKBAR.equals(method)) { - // Allow null-pointer to catch illegal states. - runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar()); - return response; - } - response = super.call(method, arg, extras); - if (response != null) return response; - return mDebugTestInformationHandler.call(method, arg, extras); - } -} - diff --git a/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml b/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml new file mode 100644 index 0000000000..961f5aa4b2 --- /dev/null +++ b/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml index dde9cac05a..8cd856035c 100644 --- a/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml +++ b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml @@ -18,6 +18,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" - android:background="@drawable/keyboard_quick_switch_task_view_background" + android:background="@drawable/keyboard_quick_switch_thumbnail_background" android:clipToOutline="true" android:importantForAccessibility="no"/> diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java index 0bcf2d11f0..bed85d7bd9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java @@ -97,7 +97,11 @@ public final class KeyboardQuickSwitchController implements private void openQuickSwitchView(int currentFocusedIndex) { if (mQuickSwitchViewController != null) { - return; + if (!mQuickSwitchViewController.isCloseAnimationRunning()) { + return; + } + // Allow the KQS to be reopened during the close animation to make it more responsive + closeQuickSwitchView(false); } TaskbarOverlayContext overlayContext = mControllers.taskbarOverlayController.requestWindow(); diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java index 5b407f0b5d..48fc7d18fb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; @@ -174,21 +175,23 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout { return; } if (updateFunction == null) { - applyThumbnail(thumbnailView, task.thumbnail); + applyThumbnail(thumbnailView, task.colorBackground, task.thumbnail); return; } - updateFunction.updateThumbnailInBackground( - task, thumbnailData -> applyThumbnail(thumbnailView, thumbnailData)); + updateFunction.updateThumbnailInBackground(task, thumbnailData -> + applyThumbnail(thumbnailView, task.colorBackground, thumbnailData)); } private void applyThumbnail( @NonNull ImageView thumbnailView, - ThumbnailData thumbnailData) { + @ColorInt int backgroundColor, + @Nullable ThumbnailData thumbnailData) { Bitmap bm = thumbnailData == null ? null : thumbnailData.thumbnail; if (thumbnailView.getVisibility() != VISIBLE) { thumbnailView.setVisibility(VISIBLE); } + thumbnailView.getBackground().setTint(bm == null ? backgroundColor : Color.TRANSPARENT); thumbnailView.setImageDrawable(new BlurredBitmapDrawable(bm, THUMBNAIL_BLUR_RADIUS)); } diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java index 847088d784..25a97d4429 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java @@ -94,8 +94,12 @@ public class KeyboardQuickSwitchViewController { mViewCallbacks); } + boolean isCloseAnimationRunning() { + return mCloseAnimation != null; + } + protected void closeQuickSwitchView(boolean animate) { - if (mCloseAnimation != null) { + if (isCloseAnimationRunning()) { // Let currently-running animation finish. if (!animate) { mCloseAnimation.end(); @@ -130,7 +134,7 @@ public class KeyboardQuickSwitchViewController { } private int launchTaskAt(int index) { - if (mCloseAnimation != null) { + if (isCloseAnimationRunning()) { // Ignore taps on task views and alt key unpresses while the close animation is running. return -1; } @@ -138,7 +142,7 @@ public class KeyboardQuickSwitchViewController { // views have been added in the KeyboardQuickSwitchView. GroupTask task = mControllerCallbacks.getTaskAt(index); if (task == null) { - return Math.max(0, index); + return mOnDesktop ? 1 : Math.max(0, index); } if (mControllerCallbacks.isTaskRunning(task)) { // Ignore attempts to run the selected task if it is already running. @@ -186,7 +190,7 @@ public class KeyboardQuickSwitchViewController { pw.println(prefix + "KeyboardQuickSwitchViewController:"); pw.println(prefix + "\thasFocus=" + mKeyboardQuickSwitchView.hasFocus()); - pw.println(prefix + "\tcloseAnimationRunning=" + (mCloseAnimation != null)); + pw.println(prefix + "\tisCloseAnimationRunning=" + isCloseAnimationRunning()); pw.println(prefix + "\tmCurrentFocusIndex=" + mCurrentFocusIndex); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index aedbe6cfbd..26212c1934 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -37,14 +37,12 @@ import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN; -import static com.android.launcher3.taskbar.TaskbarDragLayerController.TASKBAR_REAPPEAR_DELAY_MS; import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName; import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING; import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; @@ -82,7 +80,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.R; -import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.apppairs.AppPairIcon; import com.android.launcher3.config.FeatureFlags; @@ -1432,23 +1429,6 @@ public class TaskbarActivityContext extends BaseTaskbarContext { }); } - public void hideTaskbarWhenFolding() { - AnimatedFloat alphaAnim = mControllers.taskbarDragLayerController.getTaskbarAlpha(); - alphaAnim.cancelAnimation(); - alphaAnim.updateValue(0); - ObjectAnimator animator = alphaAnim.animateToValue(1).setDuration(0); - animator.setStartDelay(TASKBAR_REAPPEAR_DELAY_MS); - animator.start(); - } - - public void cancelHideTaskbarWhenFolding() { - mControllers.taskbarDragLayerController.getTaskbarAlpha().cancelAnimation(); - } - - public void resetHideTaskbarWhenUnfolding() { - mControllers.taskbarDragLayerController.getTaskbarAlpha().updateValue(1); - } - protected boolean isUserSetupComplete() { return mIsUserSetupComplete; } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index e48c20dd61..ff890fb18e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -44,12 +44,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa private static final boolean DEBUG = SystemProperties.getBoolean( "persist.debug.draw_taskbar_debug_ui", false); - // Delay to reset the task bar alpha back to 1 after fading it for transition from unfold to - // fold. Normally this is not needed since the new task bar is recreated after fading, but in - // case something goes wrong this provides a fallback mechanism to make sure the task bar is - // visible after the transition finishes. - public static final long TASKBAR_REAPPEAR_DELAY_MS = 2000; - private final TaskbarActivityContext mActivity; private final TaskbarDragLayer mTaskbarDragLayer; private final int mFolderMargin; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java index ecbc7e7fdc..e4f9ba5260 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java @@ -29,7 +29,6 @@ import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING; import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange; import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR; @@ -43,7 +42,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.res.Configuration; -import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; @@ -111,7 +109,6 @@ public class TaskbarManager { private final Context mContext; private final @Nullable Context mNavigationBarPanelContext; - private final DeviceStateManager mDeviceStateManager; private WindowManager mWindowManager; private FrameLayout mTaskbarRootLayout; private boolean mAddedWindow; @@ -180,8 +177,7 @@ public class TaskbarManager { } }; - private final UnfoldTransitionProgressProvider.TransitionProgressListener - mUnfoldTransitionProgressListener = + UnfoldTransitionProgressProvider.TransitionProgressListener mUnfoldTransitionProgressListener = new UnfoldTransitionProgressProvider.TransitionProgressListener() { @Override public void onTransitionStarted() { @@ -210,9 +206,6 @@ public class TaskbarManager { } }; - private final DeviceStateManager.FoldStateListener mFoldStateListener; - private Boolean mFolded; - @SuppressLint("WrongConstant") public TaskbarManager( TouchInteractionService service, AllAppsActionManager allAppsActionManager) { @@ -240,29 +233,6 @@ public class TaskbarManager { } }; } - // Temporary solution to mitigate the visual jump from folding the device. Currently, the - // screen turns on much earlier than we receive the onConfigurationChanged callback or - // receiving the correct device profile. While the ideal the solution is to align turning - // the screen on after onConfigurationChanged (by either delaying turning on the screen or - // figuring out what is causing the delay in getting onConfigurationChanged callback), one - // easy temporary mitigation is to dimming the bar so that the visual jump isn't as glaring. - mFoldStateListener = new DeviceStateManager.FoldStateListener(mContext, folded -> { - boolean firstTime = mFolded == null; - if (mTaskbarActivityContext == null) { - return; - } - if (!firstTime && mFolded.booleanValue() != folded) { - mTaskbarActivityContext.cancelHideTaskbarWhenFolding(); - } - mFolded = folded; - if (folded && !firstTime) { - mTaskbarActivityContext.hideTaskbarWhenFolding(); - } else { - mTaskbarActivityContext.resetHideTaskbarWhenUnfolding(); - } - }); - mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class); - mDeviceStateManager.registerCallback(MAIN_EXECUTOR, mFoldStateListener); mNavButtonController = new TaskbarNavButtonController(service, SystemUiProxy.INSTANCE.get(mContext), new Handler(), AssistUtils.newInstance(mContext)); @@ -619,7 +589,6 @@ public class TaskbarManager { Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy()."); mContext.unregisterComponentCallbacks(mComponentCallbacks); mContext.unregisterReceiver(mShutdownReceiver); - mDeviceStateManager.unregisterCallback(mFoldStateListener); } public @Nullable TaskbarActivityContext getCurrentActivityContext() { diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index 077ca60aae..e4d8e92374 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -22,6 +22,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS; import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage; import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess; +import static com.android.launcher3.testing.shared.TestProtocol.UPDATE_OVERVIEW_TARGETS_RUNNING_LATE; import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT; import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS; import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT; @@ -44,6 +45,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.RemoteException; +import android.util.Log; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -59,6 +61,7 @@ import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule; import com.android.launcher3.util.rule.FailureWatcher; import com.android.launcher3.util.rule.SamplerRule; import com.android.launcher3.util.rule.ScreenRecordRule; @@ -105,6 +108,9 @@ public class FallbackRecentsTest { @Rule public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(); + @Rule + public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule(); + public FallbackRecentsTest() throws RemoteException { Instrumentation instrumentation = getInstrumentation(); Context context = instrumentation.getContext(); @@ -129,6 +135,13 @@ public class FallbackRecentsTest { getLauncherCommand(mOtherLauncherActivity)); updateHandler.mChangeCounter .await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS); + Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE, + "AFTER AWAIT: mObserver home intent package name=" + + updateHandler.mObserver.getHomeIntent() + .getComponent().getPackageName()); + Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE, + "AFTER AWAIT: mOtherLauncherActivity package name=" + + mOtherLauncherActivity.packageName); try { base.evaluate(); } finally { @@ -340,12 +353,25 @@ public class FallbackRecentsTest { mRads = new RecentsAnimationDeviceState(ctx); mObserver = new OverviewComponentObserver(ctx, mRads); mChangeCounter = new CountDownLatch(1); + Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE, + "OverviewUpdateHandler(Constructor): mObserver home intent package name=" + + mObserver.getHomeIntent().getComponent().getPackageName()); + Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE, + "OverviewUpdateHandler(Constructor): mOtherLauncherActivity package name=" + + mOtherLauncherActivity.packageName); if (mObserver.getHomeIntent().getComponent() .getPackageName().equals(mOtherLauncherActivity.packageName)) { // Home already same mChangeCounter.countDown(); } else { - mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown()); + mObserver.setOverviewChangeListener(b -> { + Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE, + "OverviewChangeListener(Callback): isHomeAndOverviewSame=" + b); + Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE, + "OverviewChangeListener(Callback): mObserver home intent package name=" + + mObserver.getHomeIntent().getComponent().getPackageName()); + mChangeCounter.countDown(); + }); } } diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 12cdd67515..d5c9b9fa89 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -23,39 +23,56 @@ import static com.android.launcher3.config.FeatureFlags.enableAppPairs; import static com.android.launcher3.config.FeatureFlags.enableSplitContextually; import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.app.Activity; +import android.app.Application; import android.content.Context; import android.content.res.Resources; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.os.Binder; import android.os.Bundle; +import android.system.Os; import android.view.WindowInsets; +import androidx.annotation.Keep; import androidx.annotation.Nullable; import androidx.core.view.WindowInsetsCompat; +import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherState; import com.android.launcher3.R; +import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Workspace; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.icons.ClockDrawableWrapper; import com.android.launcher3.testing.shared.HotseatCellCenterRequest; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest; +import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.widget.picker.WidgetsFullSheet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Supplier; @@ -69,6 +86,12 @@ public class TestInformationHandler implements ResourceBasedOverride { context, R.string.test_information_handler_class); } + private static Collection sEvents; + private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks; + private static final Set sActivities = + Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>())); + private static int sActivitiesCreatedCount = 0; + protected Context mContext; protected DeviceProfile mDeviceProfile; protected LauncherAppState mLauncherAppState; @@ -78,6 +101,17 @@ public class TestInformationHandler implements ResourceBasedOverride { mDeviceProfile = InvariantDeviceProfile.INSTANCE. get(context).getDeviceProfile(context); mLauncherAppState = LauncherAppState.getInstanceNoCreate(); + if (sActivityLifecycleCallbacks == null) { + sActivityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() { + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + sActivities.add(activity); + ++sActivitiesCreatedCount; + } + }; + ((Application) context.getApplicationContext()) + .registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks); + } } /** @@ -309,6 +343,127 @@ public class TestInformationHandler implements ResourceBasedOverride { return response; } + case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: { + return getLauncherUIProperty(Bundle::putInt, + l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags()); + } + + case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING: + TestProtocol.sDebugTracing = true; + ClockDrawableWrapper.sRunningInTest = true; + return response; + + case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING: + TestProtocol.sDebugTracing = false; + ClockDrawableWrapper.sRunningInTest = false; + return response; + + case TestProtocol.REQUEST_PID: { + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid()); + return response; + } + + case TestProtocol.REQUEST_FORCE_GC: { + runGcAndFinalizersSync(); + return response; + } + + case TestProtocol.REQUEST_START_EVENT_LOGGING: { + sEvents = new ArrayList<>(); + TestLogging.setEventConsumer( + (sequence, event) -> { + final Collection events = sEvents; + if (events != null) { + synchronized (events) { + events.add(sequence + '/' + event); + } + } + }); + return response; + } + + case TestProtocol.REQUEST_STOP_EVENT_LOGGING: { + TestLogging.setEventConsumer(null); + sEvents = null; + return response; + } + + case TestProtocol.REQUEST_GET_TEST_EVENTS: { + if (sEvents == null) { + // sEvents can be null if Launcher died and restarted after + // REQUEST_START_EVENT_LOGGING. + return response; + } + + synchronized (sEvents) { + response.putStringArrayList( + TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents)); + } + return response; + } + + case TestProtocol.REQUEST_REINITIALIZE_DATA: { + final long identity = Binder.clearCallingIdentity(); + try { + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + MAIN_EXECUTOR.execute(model::forceReload); + }); + return response; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + case TestProtocol.REQUEST_CLEAR_DATA: { + final long identity = Binder.clearCallingIdentity(); + try { + MODEL_EXECUTOR.execute(() -> { + LauncherModel model = LauncherAppState.getInstance(mContext).getModel(); + model.getModelDbController().createEmptyDB(); + model.getModelDbController().clearEmptyDbFlag(); + MAIN_EXECUTOR.execute(model::forceReload); + }); + return response; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: { + return getLauncherUIProperty(Bundle::putStringArrayList, l -> { + ShortcutAndWidgetContainer hotseatIconsContainer = + l.getHotseat().getShortcutsAndWidgets(); + ArrayList hotseatIconNames = new ArrayList<>(); + + for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) { + // Use unchecked cast to catch changes in hotseat layout + BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i); + hotseatIconNames.add((String) icon.getText()); + } + + return hotseatIconNames; + }); + } + + case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: { + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount); + return response; + } + + case TestProtocol.REQUEST_GET_ACTIVITIES: { + response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD, + sActivities.stream().map( + a -> a.getClass().getSimpleName() + " (" + + (a.isDestroyed() ? "destroyed" : "current") + ")") + .toArray(String[]::new)); + return response; + } + + case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED: + return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new); + default: return null; } @@ -387,4 +542,38 @@ public class TestInformationHandler implements ResourceBasedOverride { */ void set(Bundle b, String key, T value); } + + + private static void runGcAndFinalizersSync() { + Runtime.getRuntime().gc(); + Runtime.getRuntime().runFinalization(); + + final CountDownLatch fence = new CountDownLatch(1); + createFinalizationObserver(fence); + try { + do { + Runtime.getRuntime().gc(); + Runtime.getRuntime().runFinalization(); + } while (!fence.await(100, TimeUnit.MILLISECONDS)); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + } + + // Create the observer in the scope of a method to minimize the chance that + // it remains live in a DEX/machine register at the point of the fence guard. + // This must be kept to avoid R8 inlining it. + @Keep + private static void createFinalizationObserver(CountDownLatch fence) { + new Object() { + @Override + protected void finalize() throws Throwable { + try { + fence.countDown(); + } finally { + super.finalize(); + } + } + }; + } } diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java index e0fafcc8d2..fea0330a16 100644 --- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java +++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java @@ -180,6 +180,7 @@ public final class TestProtocol { public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471"; public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890"; public static final String ACTIVITY_NOT_RESUMED_AFTER_BACK = "b/322823209"; + public static final String UPDATE_OVERVIEW_TARGETS_RUNNING_LATE = "b/321775748"; public static final String REQUEST_EMULATE_DISPLAY = "emulate-display"; public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display"; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 972be80c7b..d8635f9071 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -66,6 +66,7 @@ import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.TestUtil; import com.android.launcher3.util.Wait; +import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule; import com.android.launcher3.util.rule.FailureWatcher; import com.android.launcher3.util.rule.SamplerRule; import com.android.launcher3.util.rule.ScreenRecordRule; @@ -219,6 +220,9 @@ public abstract class AbstractLauncherUiTest { @Rule public SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + @Rule + public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule(); + public static void initialize(AbstractLauncherUiTest test) throws Exception { test.reinitializeLauncherData(); test.mDevice.pressHome(); diff --git a/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java b/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java new file mode 100644 index 0000000000..702988c05b --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 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.util.rule; + +import android.content.ContentResolver; +import android.provider.Settings; +import android.util.Log; +import android.view.ViewConfiguration; + +import androidx.test.InstrumentationRegistry; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class ExtendedLongPressTimeoutRule implements TestRule { + + private static final String TAG = "ExtendedLongPressTimeoutRule"; + + private static final float LONG_PRESS_TIMEOUT_MULTIPLIER = 10f; + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + ContentResolver contentResolver = InstrumentationRegistry.getInstrumentation() + .getContext() + .getContentResolver(); + int prevLongPressTimeout = Settings.Secure.getInt( + contentResolver, + Settings.Secure.LONG_PRESS_TIMEOUT, + ViewConfiguration.getLongPressTimeout()); + int newLongPressTimeout = + (int) (prevLongPressTimeout * LONG_PRESS_TIMEOUT_MULTIPLIER); + + try { + Log.d(TAG, "In try-block: Setting long press timeout from " + + prevLongPressTimeout + "ms to " + newLongPressTimeout + "ms"); + Settings.Secure.putInt( + contentResolver, + Settings.Secure.LONG_PRESS_TIMEOUT, + (int) (prevLongPressTimeout * LONG_PRESS_TIMEOUT_MULTIPLIER)); + + base.evaluate(); + } catch (Exception e) { + Log.e(TAG, "Error", e); + throw e; + } finally { + Log.d(TAG, "In finally-block: resetting long press timeout to " + + prevLongPressTimeout + "ms"); + Settings.Secure.putInt( + contentResolver, + Settings.Secure.LONG_PRESS_TIMEOUT, + prevLongPressTimeout); + } + } + }; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 0e523c3dae..c7d37548b4 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1919,17 +1919,21 @@ public final class LauncherInstrumentation { } private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, - float x, float y, int source) { + float x, float y, int source, int toolType) { return MotionEvent.obtain(downTime, eventTime, action, 1, - new MotionEvent.PointerProperties[]{getPointerProperties(0)}, + new MotionEvent.PointerProperties[]{getPointerProperties(0, toolType)}, new MotionEvent.PointerCoords[]{getPointerCoords(x, y)}, 0, 0, 1.0f, 1.0f, 0, 0, source, 0); } private static MotionEvent.PointerProperties getPointerProperties(int pointerId) { + return getPointerProperties(pointerId, Configurator.getInstance().getToolType()); + } + + private static MotionEvent.PointerProperties getPointerProperties(int pointerId, int toolType) { MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); properties.id = pointerId; - properties.toolType = Configurator.getInstance().getToolType(); + properties.toolType = toolType; return properties; } @@ -1975,6 +1979,19 @@ public final class LauncherInstrumentation { public void sendPointer(long downTime, long currentTime, int action, Point point, GestureScope gestureScope, int source, boolean isRightClick) { + sendPointer( + downTime, + currentTime, + action, + point, + gestureScope, + source, + isRightClick, + Configurator.getInstance().getToolType()); + } + + public void sendPointer(long downTime, long currentTime, int action, Point point, + GestureScope gestureScope, int source, boolean isRightClick, int toolType) { final boolean hasTIS = hasTIS(); int pointerCount = mPointerCount; @@ -2009,13 +2026,13 @@ public final class LauncherInstrumentation { ? getTrackpadMotionEvent( downTime, currentTime, action, point.x, point.y, pointerCount, mTrackpadGestureType) - : getMotionEvent(downTime, currentTime, action, point.x, point.y, source); + : getMotionEvent(downTime, currentTime, action, point.x, point.y, source, toolType); if (action == MotionEvent.ACTION_BUTTON_PRESS || action == MotionEvent.ACTION_BUTTON_RELEASE) { event.setActionButton(MotionEvent.BUTTON_PRIMARY); } if (isRightClick) { - event.setButtonState(event.getButtonState() & MotionEvent.BUTTON_SECONDARY); + event.setButtonState(event.getButtonState() | MotionEvent.BUTTON_SECONDARY); } injectEvent(event); } @@ -2114,15 +2131,19 @@ public final class LauncherInstrumentation { @NonNull final UiObject2 target, @NonNull String resName, Pattern longClickEvent) { final Point targetCenter = target.getVisibleCenter(); final long downTime = SystemClock.uptimeMillis(); + // Use stylus secondary button press to prevent using the exteded long press timeout rule + // unnecessarily sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter, - GestureScope.DONT_EXPECT_PILFER); + GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN, + /* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS); try { expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent); final UiObject2 result = waitForLauncherObject(resName); return result; } finally { sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter, - GestureScope.DONT_EXPECT_PILFER); + GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN, + /* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS); } }