diff options
11 files changed, 500 insertions, 61 deletions
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index c2ba064ac7b6..39f6d8c46a24 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -179,4 +179,8 @@ <!-- Whether pointer pilfer is required to start back animation. --> <bool name="config_backAnimationRequiresPointerPilfer">true</bool> + + <!-- This is to be overridden to define a list of packages mapped to web links which will be + parsed and utilized for desktop windowing's app-to-web feature. --> + <string name="generic_links_list" translatable="false"/> </resources> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java index fc4710f8f67b..a1ba24ca366f 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java @@ -70,6 +70,10 @@ public class DesktopModeStatus { private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean( "persist.wm.debug.desktop_mode_enforce_device_restrictions", true); + private static final boolean USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS = + SystemProperties.getBoolean( + "persist.wm.debug.use_app_to_web_build_time_generic_links", true); + /** Whether the desktop density override is enabled. */ public static final boolean DESKTOP_DENSITY_OVERRIDE_ENABLED = SystemProperties.getBoolean("persist.wm.debug.desktop_mode_density_enabled", false); @@ -176,6 +180,13 @@ public class DesktopModeStatus { } /** + * Returns {@code true} if the app-to-web feature is using the build-time generic links list. + */ + public static boolean useAppToWebBuildTimeGenericLinks() { + return USE_APP_TO_WEB_BUILD_TIME_GENERIC_LINKS; + } + + /** * Return {@code true} if the override desktop density is enabled. */ private static boolean isDesktopDensityOverrideEnabled() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt new file mode 100644 index 000000000000..56447de6bae1 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParser.kt @@ -0,0 +1,96 @@ +/* + * 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.wm.shell.apptoweb + +import android.content.Context +import android.provider.DeviceConfig +import android.webkit.URLUtil +import com.android.internal.annotations.VisibleForTesting +import com.android.wm.shell.R +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.shared.annotations.ShellMainThread +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useAppToWebBuildTimeGenericLinks + +/** + * Retrieves the build-time or server-side generic links list and parses and stores the + * package-to-url pairs. + */ +class AppToWebGenericLinksParser( + private val context: Context, + @ShellMainThread private val mainExecutor: ShellExecutor +) { + private val genericLinksMap: MutableMap<String, String> = mutableMapOf() + + init { + // If using the server-side generic links list, register a listener + if (!useAppToWebBuildTimeGenericLinks()) { + DeviceConfigListener() + } + + updateGenericLinksMap() + } + + /** Returns the generic link associated with the [packageName] or null if there is none. */ + fun getGenericLink(packageName: String): String? = genericLinksMap[packageName] + + private fun updateGenericLinksMap() { + val genericLinksList = + if (useAppToWebBuildTimeGenericLinks()) { + context.resources.getString(R.string.generic_links_list) + } else { + DeviceConfig.getString(NAMESPACE, FLAG_GENERIC_LINKS, /* defaultValue= */ "") + } ?: return + + parseGenericLinkList(genericLinksList) + } + + private fun parseGenericLinkList(genericLinksList: String) { + val newEntries = + genericLinksList + .split(" ") + .filter { it.contains(':') } + .map { + val (packageName, url) = it.split(':', limit = 2) + return@map packageName to url + } + .filter { URLUtil.isNetworkUrl(it.second) } + + genericLinksMap.clear() + genericLinksMap.putAll(newEntries) + } + + /** + * Listens for changes to the server-side generic links list and updates the package to url map + * if [DesktopModeStatus#useBuildTimeGenericLinkList()] is set to false. + */ + inner class DeviceConfigListener : DeviceConfig.OnPropertiesChangedListener { + init { + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, mainExecutor, this) + } + + override fun onPropertiesChanged(properties: DeviceConfig.Properties) { + if (properties.keyset.contains(FLAG_GENERIC_LINKS)) { + updateGenericLinksMap() + } + } + } + + companion object { + private const val NAMESPACE = DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES + @VisibleForTesting const val FLAG_GENERIC_LINKS = "generic_links_flag" + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 80f6a637ba1c..b8c22c4ea9dd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -35,6 +35,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; +import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; @@ -223,7 +224,8 @@ public abstract class WMShellModule { Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - InteractionJankMonitor interactionJankMonitor) { + InteractionJankMonitor interactionJankMonitor, + AppToWebGenericLinksParser genericLinksParser) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return new DesktopModeWindowDecorViewModel( context, @@ -242,7 +244,8 @@ public abstract class WMShellModule { transitions, desktopTasksController, rootTaskDisplayAreaOrganizer, - interactionJankMonitor); + interactionJankMonitor, + genericLinksParser); } return new CaptionWindowDecorViewModel( context, @@ -259,6 +262,15 @@ public abstract class WMShellModule { transitions); } + @WMSingleton + @Provides + static AppToWebGenericLinksParser provideGenericLinksParser( + Context context, + @ShellMainThread ShellExecutor mainExecutor + ) { + return new AppToWebGenericLinksParser(context, mainExecutor); + } + // // Freeform // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 8312aef110b9..fc639706861e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -87,6 +87,7 @@ import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; @@ -162,6 +163,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener = new DesktopModeKeyguardChangeListener(); private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; + private final AppToWebGenericLinksParser mGenericLinksParser; private final DisplayInsetsController mDisplayInsetsController; private final Region mExclusionRegion = Region.obtain(); private boolean mInImmersiveMode; @@ -198,7 +200,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Transitions transitions, Optional<DesktopTasksController> desktopTasksController, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, - InteractionJankMonitor interactionJankMonitor + InteractionJankMonitor interactionJankMonitor, + AppToWebGenericLinksParser genericLinksParser ) { this( context, @@ -216,6 +219,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { syncQueue, transitions, desktopTasksController, + genericLinksParser, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, @@ -241,6 +245,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopTasksController> desktopTasksController, + AppToWebGenericLinksParser genericLinksParser, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, @@ -266,6 +271,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mGenericLinksParser = genericLinksParser; mInputManager = mContext.getSystemService(InputManager.class); mWindowDecorByTaskId = windowDecorByTaskId; mSysUIPackageName = mContext.getResources().getString( @@ -1155,7 +1161,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mBgExecutor, mMainChoreographer, mSyncQueue, - mRootTaskDisplayAreaOrganizer); + mRootTaskDisplayAreaOrganizer, + mGenericLinksParser); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); final DragPositioningCallback dragPositioningCallback; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 529def7ca3d7..3b62bcfc0e87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -30,6 +30,7 @@ import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLarge import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.WindowConfiguration.WindowingMode; @@ -49,8 +50,8 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Handler; import android.os.Trace; -import android.util.Log; import android.util.Size; +import android.util.Slog; import android.view.Choreographer; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -68,6 +69,7 @@ import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; @@ -133,12 +135,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private CharSequence mAppName; private CapturedLink mCapturedLink; + private Uri mGenericLink; private OpenInBrowserClickListener mOpenInBrowserClickListener; private ExclusionRegionListener mExclusionRegionListener; private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; private final MaximizeMenuFactory mMaximizeMenuFactory; + private final HandleMenuFactory mHandleMenuFactory; + private final AppToWebGenericLinksParser mGenericLinksParser; // Hover state for the maximize menu and button. The menu will remain open as long as either of // these is true. See {@link #onMaximizeHoverStateChanged()}. @@ -161,13 +166,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @ShellBackgroundThread ShellExecutor bgExecutor, Choreographer choreographer, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + AppToWebGenericLinksParser genericLinksParser) { this (context, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, - rootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, + rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new, SurfaceControl.Transaction::new, WindowContainerTransaction::new, SurfaceControl::new, new SurfaceControlViewHostFactory() {}, - DefaultMaximizeMenuFactory.INSTANCE); + DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE); } DesktopModeWindowDecoration( @@ -182,12 +188,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Choreographer choreographer, SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + AppToWebGenericLinksParser genericLinksParser, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory, - MaximizeMenuFactory maximizeMenuFactory) { + MaximizeMenuFactory maximizeMenuFactory, + HandleMenuFactory handleMenuFactory) { super(context, displayController, taskOrganizer, taskInfo, taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier, windowContainerTransactionSupplier, surfaceControlSupplier, @@ -198,7 +206,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mChoreographer = choreographer; mSyncQueue = syncQueue; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; + mGenericLinksParser = genericLinksParser; mMaximizeMenuFactory = maximizeMenuFactory; + mHandleMenuFactory = handleMenuFactory; } /** @@ -425,11 +435,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } void onOpenInBrowserClick() { - if (mOpenInBrowserClickListener == null || mCapturedLink == null) return; - mOpenInBrowserClickListener.onClick(this, mCapturedLink.mUri); + if (mOpenInBrowserClickListener == null || mHandleMenu == null) { + return; + } + mOpenInBrowserClickListener.onClick(this, mHandleMenu.getOpenInBrowserLink()); onCapturedLinkExpired(); } + @Nullable + private Uri getBrowserLink() { + // If the captured link is available and has not expired, return the captured link. + // Otherwise, return the generic link which is set to null if a generic link is unavailable. + if (mCapturedLink != null && !mCapturedLink.mExpired) { + return mCapturedLink.mUri; + } + return mGenericLink; + } + private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { if (!isDragResizable(mTaskInfo)) { if (!mTaskInfo.positionInParent.equals(mPositionInParent)) { @@ -700,7 +722,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } final ComponentName baseActivity = mTaskInfo.baseActivity; if (baseActivity == null) { - Log.e(TAG, "Base activity component not found in task"); + Slog.e(TAG, "Base activity component not found in task"); return; } final PackageManager pm = mContext.getApplicationContext().getPackageManager(); @@ -719,7 +741,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final ApplicationInfo applicationInfo = activityInfo.applicationInfo; mAppName = pm.getApplicationLabel(applicationInfo); } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Base activity's component name cannot be found on the system"); + Slog.e(TAG, "Base activity's component name cannot be found on the system", e); } finally { Trace.endSection(); } @@ -914,7 +936,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ void createHandleMenu(SplitScreenController splitScreenController) { loadAppInfoIfNeeded(); - mHandleMenu = new HandleMenu( + updateGenericLink(); + mHandleMenu = mHandleMenuFactory.create( this, mRelayoutParams.mLayoutResId, mOnCaptionButtonClickListener, @@ -924,7 +947,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mDisplayController, splitScreenController, DesktopModeStatus.canEnterDesktopMode(mContext), - browserLinkAvailable(), + getBrowserLink(), mResult.mCaptionWidth, mResult.mCaptionHeight, mResult.mCaptionX @@ -933,9 +956,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mHandleMenu.show(); } - @VisibleForTesting - boolean browserLinkAvailable() { - return mCapturedLink != null && !mCapturedLink.mExpired; + private void updateGenericLink() { + final ComponentName baseActivity = mTaskInfo.baseActivity; + if (baseActivity == null) { + return; + } + + final String genericLink = + mGenericLinksParser.getGenericLink(baseActivity.getPackageName()); + mGenericLink = genericLink == null ? null : Uri.parse(genericLink); } /** @@ -1219,7 +1248,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @ShellBackgroundThread ShellExecutor bgExecutor, Choreographer choreographer, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, + AppToWebGenericLinksParser genericLinksParser) { return new DesktopModeWindowDecoration( context, displayController, @@ -1231,7 +1261,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin bgExecutor, choreographer, syncQueue, - rootTaskDisplayAreaOrganizer); + rootTaskDisplayAreaOrganizer, + genericLinksParser); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt index e174e83d11c9..32522c6411cb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt @@ -27,6 +27,7 @@ import android.graphics.BlendModeColorFilter import android.graphics.Point import android.graphics.PointF import android.graphics.Rect +import android.net.Uri import android.view.MotionEvent import android.view.SurfaceControl import android.view.View @@ -68,7 +69,7 @@ class HandleMenu( private val displayController: DisplayController, private val splitScreenController: SplitScreenController, private val shouldShowWindowingPill: Boolean, - private val shouldShowBrowserPill: Boolean, + val openInBrowserLink: Uri?, private val captionWidth: Int, private val captionHeight: Int, captionX: Int @@ -106,6 +107,9 @@ class HandleMenu( // those as well. private val globalMenuPosition: Point = Point() + private val shouldShowBrowserPill: Boolean + get() = openInBrowserLink != null + init { updateHandleMenuPillPositions(captionX) } @@ -497,3 +501,57 @@ class HandleMenu( private const val SHOULD_SHOW_MORE_ACTIONS_PILL = false } } + +/** A factory interface to create a [HandleMenu]. */ +interface HandleMenuFactory { + fun create( + parentDecor: DesktopModeWindowDecoration, + layoutResId: Int, + onClickListener: View.OnClickListener?, + onTouchListener: View.OnTouchListener?, + appIconBitmap: Bitmap?, + appName: CharSequence?, + displayController: DisplayController, + splitScreenController: SplitScreenController, + shouldShowWindowingPill: Boolean, + openInBrowserLink: Uri?, + captionWidth: Int, + captionHeight: Int, + captionX: Int + ): HandleMenu +} + +/** A [HandleMenuFactory] implementation that creates a [HandleMenu]. */ +object DefaultHandleMenuFactory : HandleMenuFactory { + override fun create( + parentDecor: DesktopModeWindowDecoration, + layoutResId: Int, + onClickListener: View.OnClickListener?, + onTouchListener: View.OnTouchListener?, + appIconBitmap: Bitmap?, + appName: CharSequence?, + displayController: DisplayController, + splitScreenController: SplitScreenController, + shouldShowWindowingPill: Boolean, + openInBrowserLink: Uri?, + captionWidth: Int, + captionHeight: Int, + captionX: Int + ): HandleMenu { + return HandleMenu( + parentDecor, + layoutResId, + onClickListener, + onTouchListener, + appIconBitmap, + appName, + displayController, + splitScreenController, + shouldShowWindowingPill, + openInBrowserLink, + captionWidth, + captionHeight, + captionX + ) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt new file mode 100644 index 000000000000..053027fc1cf4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apptoweb/AppToWebGenericLinksParserTests.kt @@ -0,0 +1,131 @@ +/* + * 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.wm.shell.apptoweb + +import android.provider.DeviceConfig +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.testing.TestableResources +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.wm.shell.R +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser.Companion.FLAG_GENERIC_LINKS +import junit.framework.TestCase.assertEquals +import junit.framework.TestCase.assertNull +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.quality.Strictness + +/** + * Tests for [AppToWebGenericLinksParser]. + * + * Build/Install/Run: atest WMShellUnitTests:AppToWebGenericLinksParserTests + */ +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +class AppToWebGenericLinksParserTests : ShellTestCase() { + @Mock private lateinit var mockExecutor: ShellExecutor + + private lateinit var genericLinksParser: AppToWebGenericLinksParser + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var resources: TestableResources + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + mockitoSession = + mockitoSession() + .strictness(Strictness.LENIENT) + .spyStatic(DesktopModeStatus::class.java) + .startMocking() + resources = mContext.getOrCreateTestableResources() + resources.addOverride(R.string.generic_links_list, BUILD_TIME_LIST) + DeviceConfig.setProperty( + NAMESPACE, + FLAG_GENERIC_LINKS, + SERVER_SIDE_LIST, + false /* makeDefault */ + ) + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + } + + @Test + fun init_usingBuildTimeList() { + doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() } + genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor) + // Assert build-time list correctly parsed + assertEquals(URL_B, genericLinksParser.getGenericLink(PACKAGE_NAME_1)) + } + + @Test + fun init_usingServerSideList() { + doReturn(false).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() } + genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor) + // Assert server side list correctly parsed + assertEquals(URL_S, genericLinksParser.getGenericLink(PACKAGE_NAME_1)) + } + + @Test + fun init_ignoresMalformedPair() { + doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() } + val packageName2 = "com.google.android.slides" + val url2 = "https://docs.google.com" + resources.addOverride(R.string.generic_links_list, + "$PACKAGE_NAME_1:$URL_B error $packageName2:$url2") + genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor) + // Assert generics links list correctly parsed + assertEquals(URL_B, genericLinksParser.getGenericLink(PACKAGE_NAME_1)) + assertEquals(url2, genericLinksParser.getGenericLink(packageName2)) + } + + + @Test + fun onlySavesValidPackageToUrlMaps() { + doReturn(true).`when` { DesktopModeStatus.useAppToWebBuildTimeGenericLinks() } + resources.addOverride(R.string.generic_links_list, "$PACKAGE_NAME_1:www.yout") + genericLinksParser = AppToWebGenericLinksParser(mContext, mockExecutor) + // Verify map with invalid url not saved + assertNull(genericLinksParser.getGenericLink(PACKAGE_NAME_1)) + } + + companion object { + private const val PACKAGE_NAME_1 = "com.google.android.youtube" + + private const val URL_B = "http://www.youtube.com" + private const val URL_S = "http://www.google.com" + + private const val SERVER_SIDE_LIST = "$PACKAGE_NAME_1:$URL_S" + private const val BUILD_TIME_LIST = "$PACKAGE_NAME_1:$URL_B" + + private const val NAMESPACE = DeviceConfig.NAMESPACE_APP_COMPAT_OVERRIDES + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index aeae0bebb697..01c4f3ad6870 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -64,6 +64,7 @@ import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.DisplayLayout @@ -140,6 +141,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler @Mock private lateinit var mockWindowManager: IWindowManager @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor + @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser private val bgExecutor = TestShellExecutor() private val transactionFactory = Supplier<SurfaceControl.Transaction> { @@ -171,11 +173,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockSyncQueue, mockTransitions, Optional.of(mockDesktopTasksController), + mockGenericLinksParser, mockDesktopModeWindowDecorFactory, mockInputMonitorFactory, transactionFactory, mockRootTaskDisplayAreaOrganizer, - windowDecorByTaskIdSpy, mockInteractionJankMonitor + windowDecorByTaskIdSpy, + mockInteractionJankMonitor ) desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController) whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) @@ -218,7 +222,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { bgExecutor, mockMainChoreographer, mockSyncQueue, - mockRootTaskDisplayAreaOrganizer + mockRootTaskDisplayAreaOrganizer, + mockGenericLinksParser ) verify(decoration).close() } @@ -244,7 +249,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { bgExecutor, mockMainChoreographer, mockSyncQueue, - mockRootTaskDisplayAreaOrganizer + mockRootTaskDisplayAreaOrganizer, + mockGenericLinksParser ) task.setWindowingMode(WINDOWING_MODE_FREEFORM) @@ -261,7 +267,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { bgExecutor, mockMainChoreographer, mockSyncQueue, - mockRootTaskDisplayAreaOrganizer + mockRootTaskDisplayAreaOrganizer, + mockGenericLinksParser ) } @@ -359,7 +366,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { verify(mockDesktopModeWindowDecorFactory, never()) .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any()) + any(), any()) } @Test @@ -381,7 +388,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onTaskOpening(task) verify(mockDesktopModeWindowDecorFactory) .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), - any(), any()) + any(), any(), any()) } finally { mockitoSession.finishMocking() } @@ -399,7 +406,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { verify(mockDesktopModeWindowDecorFactory, never()) .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any()) + any(), any()) } @Test @@ -416,7 +423,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onTaskOpening(task) verify(mockDesktopModeWindowDecorFactory, never()) - .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), + .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any(), any()) } @@ -515,7 +522,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onTaskOpening(task) verify(mockDesktopModeWindowDecorFactory, never()) .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any()) + any(), any()) } finally { mockitoSession.finishMocking() } @@ -540,7 +547,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onTaskOpening(task) verify(mockDesktopModeWindowDecorFactory) .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any()) + any(), any()) } finally { mockitoSession.finishMocking() } @@ -564,7 +571,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { onTaskOpening(task) verify(mockDesktopModeWindowDecorFactory) .create(any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any()) + any(), any()) } finally { mockitoSession.finishMocking() } @@ -702,8 +709,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration { val decoration = mock(DesktopModeWindowDecoration::class.java) whenever( - mockDesktopModeWindowDecorFactory.create( - any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any()) + mockDesktopModeWindowDecorFactory.create(any(), any(), any(), any(), eq(task), any(), + any(), any(), any(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.isFocused).thenReturn(task.isFocused) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 412fef30d4fb..4b069f933d9f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -31,6 +31,7 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; @@ -59,6 +60,7 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; +import android.testing.TestableLooper; import android.view.AttachedSurfaceControl; import android.view.Choreographer; import android.view.Display; @@ -83,6 +85,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; +import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -114,6 +117,7 @@ import java.util.function.Supplier; * atest WMShellUnitTests:DesktopModeWindowDecorationTests */ @SmallTest +@TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner.class) public class DesktopModeWindowDecorationTests extends ShellTestCase { private static final String USE_WINDOW_SHADOWS_SYSPROP_KEY = @@ -123,7 +127,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY = "persist.wm.debug.desktop_use_rounded_corners"; - private static final Uri TEST_URI = Uri.parse("www.google.com"); + private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/"); + private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/"); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @@ -161,6 +166,12 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private Handler mMockHandler; @Mock private DesktopModeWindowDecoration.OpenInBrowserClickListener mMockOpenInBrowserClickListener; + @Mock + private AppToWebGenericLinksParser mMockGenericLinksParser; + @Mock + private HandleMenu mMockHandleMenu; + @Mock + private HandleMenuFactory mMockHandleMenuFactory; @Captor private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener; @Captor @@ -204,6 +215,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final Display defaultDisplay = mock(Display.class); doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY); doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); + doReturn(mMockHandleMenu).when(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(), + any(), any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()); } @After @@ -572,65 +585,123 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { verify(mMockHandler).removeCallbacks(any()); } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* generic link */); + + // Verify handle menu's browser link set as captured link + decor.createHandleMenu(mMockSplitScreenController); + verifyHandleMenuCreated(TEST_URI1); + } + @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) public void capturedLink_postsOnCapturedLinkExpiredRunnable() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, null /* generic link */); final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); - final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); - decor.relayout(taskInfo); - // Assert captured link is set - assertTrue(decor.browserLinkAvailable()); - // Asset runnable posted to set captured link to expired + // Run runnable to set captured link to expired verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong()); runnableArgument.getValue().run(); - assertFalse(decor.browserLinkAvailable()); + + // Verify captured link is no longer valid by verifying link is not set as handle menu + // browser link. + decor.createHandleMenu(mMockSplitScreenController); + verifyHandleMenuCreated(null /* uri */); } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) public void capturedLink_capturedLinkNotResetToSameLink() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); - final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, null /* generic link */); final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); - // Set captured link and run on captured link expired runnable - decor.relayout(taskInfo); + // Run runnable to set captured link to expired verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong()); runnableArgument.getValue().run(); + // Relayout decor with same captured link decor.relayout(taskInfo); - // Assert captured link not set to same value twice - assertFalse(decor.browserLinkAvailable()); + + // Verify handle menu's browser link not set to captured link since link is expired + decor.createHandleMenu(mMockSplitScreenController); + verifyHandleMenuCreated(null /* uri */); } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) - public void capturedLink_capturedLinkExpiresAfterClick() { + public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); - final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); - decor.relayout(taskInfo); - // Assert captured link is set - assertTrue(decor.browserLinkAvailable()); + // Create handle menu before link expires + decor.createHandleMenu(mMockSplitScreenController); + + // Run runnable to set captured link to expired + verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong()); + runnableArgument.getValue().run(); + + // Verify handle menu's browser link is set to captured link since menu was opened before + // captured link expired + decor.createHandleMenu(mMockSplitScreenController); + verifyHandleMenuCreated(TEST_URI1); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void capturedLink_capturedLinkExpiresAfterClick() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + // Simulate menu opening and clicking open in browser button + decor.createHandleMenu(mMockSplitScreenController); decor.onOpenInBrowserClick(); - //Assert Captured link expires after button is clicked - assertFalse(decor.browserLinkAvailable()); + + // Verify handle menu's browser link not set to captured link since link not valid after + // open in browser clicked + decor.createHandleMenu(mMockSplitScreenController); + verifyHandleMenuCreated(null /* uri */); } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) public void capturedLink_openInBrowserListenerCalledOnClick() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); - final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); - - decor.relayout(taskInfo); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + decor.createHandleMenu(mMockSplitScreenController); decor.onOpenInBrowserClick(); verify(mMockOpenInBrowserClickListener).onClick(any(), any()); } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void genericLink_genericLinkUsedWhenCapturedLinkUnavailable() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, null /* captured link */, TEST_URI2 /* generic link */); + + // Verify handle menu's browser link set as generic link no captured link is available + decor.createHandleMenu(mMockSplitScreenController); + verifyHandleMenuCreated(TEST_URI2); + } + + private void verifyHandleMenuCreated(@Nullable Uri uri) { + verify(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(), any(), any(), any(), + any(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt()); + } + private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) { final OnTaskActionClickListener l = (taskId, tag) -> {}; decoration.setOnMaximizeOrRestoreClickListener(l); @@ -657,6 +728,18 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { R.dimen.rounded_corner_radius_bottom, fillValue); } + private DesktopModeWindowDecoration createWindowDecoration( + ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink, + @Nullable Uri genericLink) { + taskInfo.capturedLink = capturedLink; + taskInfo.capturedLinkTimestamp = System.currentTimeMillis(); + final String genericLinkString = genericLink == null ? null : genericLink.toString(); + doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any()); + final DesktopModeWindowDecoration decor = createWindowDecoration(taskInfo); + // Relayout to set captured link + decor.relayout(taskInfo); + return decor; + } private DesktopModeWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo) { @@ -669,14 +752,15 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext, mMockDisplayController, mMockSplitScreenController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer, - mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, + mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, - mMockSurfaceControlViewHostFactory, maximizeMenuFactory); + mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener); + windowDecor.mDecorWindowContext = mContext; return windowDecor; } @@ -692,8 +776,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { "DesktopModeWindowDecorationTests"); taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor", "DesktopModeWindowDecorationTests"); - taskInfo.capturedLink = TEST_URI; - taskInfo.capturedLinkTimestamp = System.currentTimeMillis(); return taskInfo; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt index e548f8f1eb2a..ed43aa3f1301 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt @@ -229,7 +229,7 @@ class HandleMenuTest : ShellTestCase() { val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, onClickListener, onTouchListener, appIcon, appName, displayController, splitScreenController, shouldShowWindowingPill = true, - shouldShowBrowserPill = true, captionWidth = HANDLE_WIDTH, captionHeight = 50, + null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50, captionX = captionX ) handleMenu.show() |