Loading libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,6 @@ android:viewportHeight="960" android:viewportHeight="960" android:viewportWidth="960"> android:viewportWidth="960"> <path <path android:fillColor="@android:color/system_on_tertiary_fixed" android:fillColor="@android:color/system_on_tertiary_container_light" android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620Z" /> android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620Z" /> </vector> </vector> libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -37,7 +37,7 @@ android:layout_marginStart="2dp" android:layout_marginStart="2dp" android:lineHeight="20dp" android:lineHeight="20dp" android:maxWidth="150dp" android:maxWidth="150dp" android:textColor="@android:color/system_on_tertiary_fixed" android:textColor="@android:color/system_on_tertiary_container_light" android:textFontWeight="500" android:textFontWeight="500" android:textSize="14sp" /> android:textSize="14sp" /> </LinearLayout> </LinearLayout> No newline at end of file libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +48 −8 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.res.Resources import android.graphics.Point import android.graphics.Point import android.os.SystemProperties import android.os.SystemProperties import android.util.Slog import android.util.Slog import androidx.core.content.withStyledAttributes import com.android.window.flags.Flags import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.R import com.android.wm.shell.desktopmode.CaptionState import com.android.wm.shell.desktopmode.CaptionState Loading @@ -32,8 +33,11 @@ import com.android.wm.shell.shared.annotations.ShellBackgroundThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.Theme import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.EducationViewConfig import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.EducationViewConfig import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainCoroutineDispatcher import kotlinx.coroutines.MainCoroutineDispatcher Loading Loading @@ -70,6 +74,7 @@ class AppHandleEducationController( @ShellMainThread private val applicationCoroutineScope: CoroutineScope, @ShellMainThread private val applicationCoroutineScope: CoroutineScope, @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher, @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher, ) { ) { private val decorThemeUtil = DecorThemeUtil(context) private lateinit var openHandleMenuCallback: (Int) -> Unit private lateinit var openHandleMenuCallback: (Int) -> Unit private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit Loading Loading @@ -97,7 +102,9 @@ class AppHandleEducationController( } } .flowOn(backgroundDispatcher) .flowOn(backgroundDispatcher) .collectLatest { captionState -> .collectLatest { captionState -> showEducation(captionState) val tooltipColorScheme = tooltipColorScheme(captionState) showEducation(captionState, tooltipColorScheme) // After showing first tooltip, mark education as viewed // After showing first tooltip, mark education as viewed appHandleEducationDatastoreRepository.updateEducationViewedTimestampMillis(true) appHandleEducationDatastoreRepository.updateEducationViewedTimestampMillis(true) } } Loading @@ -123,7 +130,7 @@ class AppHandleEducationController( if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block() if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block() } } private fun showEducation(captionState: CaptionState) { private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) { val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds val tooltipGlobalCoordinates = val tooltipGlobalCoordinates = Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) Loading @@ -132,14 +139,17 @@ class AppHandleEducationController( val appHandleTooltipConfig = val appHandleTooltipConfig = EducationViewConfig( EducationViewConfig( tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipText = getString(R.string.windowing_app_handle_education_tooltip), tooltipText = getString(R.string.windowing_app_handle_education_tooltip), arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, onEducationClickAction = { onEducationClickAction = { launchWithExceptionHandling { showWindowingImageButtonTooltip() } launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } openHandleMenuCallback(captionState.runningTaskInfo.taskId) openHandleMenuCallback(captionState.runningTaskInfo.taskId) }, }, onDismissAction = { launchWithExceptionHandling { showWindowingImageButtonTooltip() } }, onDismissAction = { launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } }, ) ) windowingEducationViewController.showEducationTooltip( windowingEducationViewController.showEducationTooltip( Loading @@ -147,7 +157,7 @@ class AppHandleEducationController( } } /** Show tooltip that points to windowing image button in app handle menu */ /** Show tooltip that points to windowing image button in app handle menu */ private suspend fun showWindowingImageButtonTooltip() { private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) { val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) val appHandleMenuWidth = val appHandleMenuWidth = Loading Loading @@ -188,18 +198,21 @@ class AppHandleEducationController( val windowingImageButtonTooltipConfig = val windowingImageButtonTooltipConfig = EducationViewConfig( EducationViewConfig( tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipText = tooltipText = getString(R.string.windowing_desktop_mode_image_button_education_tooltip), getString(R.string.windowing_desktop_mode_image_button_education_tooltip), arrowDirection = arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, onEducationClickAction = { onEducationClickAction = { launchWithExceptionHandling { showExitWindowingTooltip() } launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } toDesktopModeCallback( toDesktopModeCallback( captionState.runningTaskInfo.taskId, captionState.runningTaskInfo.taskId, DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) }, }, onDismissAction = { launchWithExceptionHandling { showExitWindowingTooltip() } }, onDismissAction = { launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } }, ) ) windowingEducationViewController.showEducationTooltip( windowingEducationViewController.showEducationTooltip( Loading @@ -209,7 +222,7 @@ class AppHandleEducationController( } } /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ private suspend fun showExitWindowingTooltip() { private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) { windowDecorCaptionHandleRepository.captionStateFlow windowDecorCaptionHandleRepository.captionStateFlow // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered // desktop mode. // desktop mode. Loading Loading @@ -238,6 +251,7 @@ class AppHandleEducationController( val exitWindowingTooltipConfig = val exitWindowingTooltipConfig = EducationViewConfig( EducationViewConfig( tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip), tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip), arrowDirection = arrowDirection = Loading @@ -254,6 +268,32 @@ class AppHandleEducationController( } } } } private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme { context.withStyledAttributes( set = null, attrs = intArrayOf( com.android.internal.R.attr.materialColorOnTertiaryFixed, com.android.internal.R.attr.materialColorTertiaryFixed, com.android.internal.R.attr.materialColorTertiaryFixedDim), defStyleAttr = 0, defStyleRes = 0) { val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0) val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0) val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0) val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo val tooltipContainerColor = if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) { tertiaryFixed } else { tertiaryFixedDim } return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed) } return TooltipColorScheme(0, 0, 0) } /** /** * Setup callbacks for app handle education tooltips. * Setup callbacks for app handle education tooltips. * * Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt +32 −0 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.windowdecor.education package com.android.wm.shell.windowdecor.education import android.annotation.ColorInt import android.annotation.DimenRes import android.annotation.DimenRes import android.annotation.LayoutRes import android.annotation.LayoutRes import android.content.Context import android.content.Context Loading @@ -32,6 +33,7 @@ import android.widget.LinearLayout import android.widget.TextView import android.widget.TextView import android.window.DisplayAreaInfo import android.window.DisplayAreaInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import androidx.core.graphics.drawable.DrawableCompat import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce import androidx.dynamicanimation.animation.SpringForce import com.android.wm.shell.R import com.android.wm.shell.R Loading Loading @@ -120,6 +122,7 @@ class DesktopWindowingEducationTooltipController( hideEducationTooltip() hideEducationTooltip() tooltipViewConfig.onEducationClickAction() tooltipViewConfig.onEducationClickAction() } } setTooltipColorScheme(tooltipViewConfig.tooltipColorScheme) } } val tooltipDimens = tooltipDimens(tooltipView = tooltipView, tooltipViewConfig.arrowDirection) val tooltipDimens = tooltipDimens(tooltipView = tooltipView, tooltipViewConfig.arrowDirection) Loading Loading @@ -189,6 +192,21 @@ class DesktopWindowingEducationTooltipController( view = tooltipView) view = tooltipView) } } private fun View.setTooltipColorScheme(tooltipColorScheme: TooltipColorScheme) { requireViewById<LinearLayout>(R.id.tooltip_container).apply { background.setTint(tooltipColorScheme.container) } requireViewById<ImageView>(R.id.arrow_icon).apply { val wrappedDrawable = DrawableCompat.wrap(this.drawable) DrawableCompat.setTint(wrappedDrawable, tooltipColorScheme.container) } requireViewById<TextView>(R.id.tooltip_text).apply { setTextColor(tooltipColorScheme.text) } requireViewById<ImageView>(R.id.tooltip_icon).apply { val wrappedDrawable = DrawableCompat.wrap(this.drawable) DrawableCompat.setTint(wrappedDrawable, tooltipColorScheme.icon) } } private fun tooltipViewGlobalCoordinates( private fun tooltipViewGlobalCoordinates( tooltipViewGlobalCoordinates: Point, tooltipViewGlobalCoordinates: Point, arrowDirection: TooltipArrowDirection, arrowDirection: TooltipArrowDirection, Loading Loading @@ -255,6 +273,7 @@ class DesktopWindowingEducationTooltipController( */ */ data class EducationViewConfig( data class EducationViewConfig( @LayoutRes val tooltipViewLayout: Int, @LayoutRes val tooltipViewLayout: Int, val tooltipColorScheme: TooltipColorScheme, val tooltipViewGlobalCoordinates: Point, val tooltipViewGlobalCoordinates: Point, val tooltipText: String, val tooltipText: String, val arrowDirection: TooltipArrowDirection, val arrowDirection: TooltipArrowDirection, Loading @@ -262,6 +281,19 @@ class DesktopWindowingEducationTooltipController( val onDismissAction: () -> Unit, val onDismissAction: () -> Unit, ) ) /** * Color scheme of education view: * * @property container Color of the container of the tooltip. * @property text Text color of the [TextView] of education tooltip. * @property icon Color to be filled in tooltip's icon. */ data class TooltipColorScheme( @ColorInt val container: Int, @ColorInt val text: Int, @ColorInt val icon: Int, ) /** Direction of arrow of the tooltip */ /** Direction of arrow of the tooltip */ enum class TooltipArrowDirection { enum class TooltipArrowDirection { UP, UP, Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt +33 −1 Original line number Original line Diff line number Diff line Loading @@ -30,12 +30,15 @@ import android.view.View import android.view.WindowManager import android.view.WindowManager import android.widget.TextView import android.widget.TextView import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.wm.shell.R import com.android.wm.shell.R import com.android.wm.shell.ShellTestCase import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipArrowDirection import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipArrowDirection import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Before import org.junit.Test import org.junit.Test Loading Loading @@ -240,14 +243,42 @@ class DesktopWindowingEducationTooltipControllerTest : ShellTestCase() { /* fromRotation= */ ROTATION_90, /* fromRotation= */ ROTATION_90, /* toRotation= */ ROTATION_180, /* toRotation= */ ROTATION_180, /* newDisplayAreaInfo= */ null, /* newDisplayAreaInfo= */ null, WindowContainerTransaction()) WindowContainerTransaction(), ) verify(mockPopupWindow, times(1)).releaseView() verify(mockPopupWindow, times(1)).releaseView() verify(mockDisplayController, atLeastOnce()).removeDisplayChangingController(any()) verify(mockDisplayController, atLeastOnce()).removeDisplayChangingController(any()) } } @Test fun showEducationTooltip_setTooltipColorScheme_correctColorsAreSet() { val tooltipColorScheme = TooltipColorScheme( container = Color.Red.toArgb(), text = Color.Blue.toArgb(), icon = Color.Green.toArgb()) val tooltipViewConfig = createTooltipConfig(tooltipColorScheme = tooltipColorScheme) tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123) verify(mockViewContainerFactory, times(1)) .create( windowManagerWrapper = any(), taskId = anyInt(), x = anyInt(), y = anyInt(), width = anyInt(), height = anyInt(), flags = anyInt(), view = tooltipViewArgumentCaptor.capture()) val tooltipTextView = tooltipViewArgumentCaptor.lastValue.findViewById<TextView>(R.id.tooltip_text) assertThat(tooltipTextView.textColors.defaultColor).isEqualTo(Color.Blue.toArgb()) } private fun createTooltipConfig( private fun createTooltipConfig( @LayoutRes tooltipViewLayout: Int = R.layout.desktop_windowing_education_top_arrow_tooltip, @LayoutRes tooltipViewLayout: Int = R.layout.desktop_windowing_education_top_arrow_tooltip, tooltipColorScheme: TooltipColorScheme = TooltipColorScheme( container = Color.Red.toArgb(), text = Color.Red.toArgb(), icon = Color.Red.toArgb()), tooltipViewGlobalCoordinates: Point = Point(0, 0), tooltipViewGlobalCoordinates: Point = Point(0, 0), tooltipText: String = "This is a tooltip", tooltipText: String = "This is a tooltip", arrowDirection: TooltipArrowDirection = TooltipArrowDirection.UP, arrowDirection: TooltipArrowDirection = TooltipArrowDirection.UP, Loading @@ -256,6 +287,7 @@ class DesktopWindowingEducationTooltipControllerTest : ShellTestCase() { ) = ) = DesktopWindowingEducationTooltipController.EducationViewConfig( DesktopWindowingEducationTooltipController.EducationViewConfig( tooltipViewLayout = tooltipViewLayout, tooltipViewLayout = tooltipViewLayout, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipViewGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipViewGlobalCoordinates, tooltipText = tooltipText, tooltipText = tooltipText, arrowDirection = arrowDirection, arrowDirection = arrowDirection, Loading Loading
libs/WindowManager/Shell/res/drawable/app_handle_education_tooltip_icon.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,6 @@ android:viewportHeight="960" android:viewportHeight="960" android:viewportWidth="960"> android:viewportWidth="960"> <path <path android:fillColor="@android:color/system_on_tertiary_fixed" android:fillColor="@android:color/system_on_tertiary_container_light" android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620Z" /> android:pathData="M419,880Q391,880 366.5,868Q342,856 325,834L107,557L126,537Q146,516 174,512Q202,508 226,523L300,568L300,240Q300,223 311.5,211.5Q323,200 340,200Q357,200 369,211.5Q381,223 381,240L381,712L284,652L388,785Q394,792 402,796Q410,800 419,800L640,800Q673,800 696.5,776.5Q720,753 720,720L720,560Q720,543 708.5,531.5Q697,520 680,520L461,520L461,440L680,440Q730,440 765,475Q800,510 800,560L800,720Q800,786 753,833Q706,880 640,880L419,880ZM167,340Q154,318 147,292.5Q140,267 140,240Q140,157 198.5,98.5Q257,40 340,40Q423,40 481.5,98.5Q540,157 540,240Q540,267 533,292.5Q526,318 513,340L444,300Q452,286 456,271.5Q460,257 460,240Q460,190 425,155Q390,120 340,120Q290,120 255,155Q220,190 220,240Q220,257 224,271.5Q228,286 236,300L167,340ZM502,620L502,620L502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620Q502,620 502,620Q502,620 502,620L502,620L502,620Z" /> </vector> </vector>
libs/WindowManager/Shell/res/layout/desktop_windowing_education_tooltip_container.xml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -37,7 +37,7 @@ android:layout_marginStart="2dp" android:layout_marginStart="2dp" android:lineHeight="20dp" android:lineHeight="20dp" android:maxWidth="150dp" android:maxWidth="150dp" android:textColor="@android:color/system_on_tertiary_fixed" android:textColor="@android:color/system_on_tertiary_container_light" android:textFontWeight="500" android:textFontWeight="500" android:textSize="14sp" /> android:textSize="14sp" /> </LinearLayout> </LinearLayout> No newline at end of file
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +48 −8 Original line number Original line Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.res.Resources import android.graphics.Point import android.graphics.Point import android.os.SystemProperties import android.os.SystemProperties import android.util.Slog import android.util.Slog import androidx.core.content.withStyledAttributes import com.android.window.flags.Flags import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.R import com.android.wm.shell.desktopmode.CaptionState import com.android.wm.shell.desktopmode.CaptionState Loading @@ -32,8 +33,11 @@ import com.android.wm.shell.shared.annotations.ShellBackgroundThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.canEnterDesktopMode import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.Theme import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.EducationViewConfig import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.EducationViewConfig import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainCoroutineDispatcher import kotlinx.coroutines.MainCoroutineDispatcher Loading Loading @@ -70,6 +74,7 @@ class AppHandleEducationController( @ShellMainThread private val applicationCoroutineScope: CoroutineScope, @ShellMainThread private val applicationCoroutineScope: CoroutineScope, @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher, @ShellBackgroundThread private val backgroundDispatcher: MainCoroutineDispatcher, ) { ) { private val decorThemeUtil = DecorThemeUtil(context) private lateinit var openHandleMenuCallback: (Int) -> Unit private lateinit var openHandleMenuCallback: (Int) -> Unit private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit private lateinit var toDesktopModeCallback: (Int, DesktopModeTransitionSource) -> Unit Loading Loading @@ -97,7 +102,9 @@ class AppHandleEducationController( } } .flowOn(backgroundDispatcher) .flowOn(backgroundDispatcher) .collectLatest { captionState -> .collectLatest { captionState -> showEducation(captionState) val tooltipColorScheme = tooltipColorScheme(captionState) showEducation(captionState, tooltipColorScheme) // After showing first tooltip, mark education as viewed // After showing first tooltip, mark education as viewed appHandleEducationDatastoreRepository.updateEducationViewedTimestampMillis(true) appHandleEducationDatastoreRepository.updateEducationViewedTimestampMillis(true) } } Loading @@ -123,7 +130,7 @@ class AppHandleEducationController( if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block() if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block() } } private fun showEducation(captionState: CaptionState) { private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) { val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds val tooltipGlobalCoordinates = val tooltipGlobalCoordinates = Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom) Loading @@ -132,14 +139,17 @@ class AppHandleEducationController( val appHandleTooltipConfig = val appHandleTooltipConfig = EducationViewConfig( EducationViewConfig( tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, tooltipViewLayout = R.layout.desktop_windowing_education_top_arrow_tooltip, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipText = getString(R.string.windowing_app_handle_education_tooltip), tooltipText = getString(R.string.windowing_app_handle_education_tooltip), arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP, onEducationClickAction = { onEducationClickAction = { launchWithExceptionHandling { showWindowingImageButtonTooltip() } launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } openHandleMenuCallback(captionState.runningTaskInfo.taskId) openHandleMenuCallback(captionState.runningTaskInfo.taskId) }, }, onDismissAction = { launchWithExceptionHandling { showWindowingImageButtonTooltip() } }, onDismissAction = { launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) } }, ) ) windowingEducationViewController.showEducationTooltip( windowingEducationViewController.showEducationTooltip( Loading @@ -147,7 +157,7 @@ class AppHandleEducationController( } } /** Show tooltip that points to windowing image button in app handle menu */ /** Show tooltip that points to windowing image button in app handle menu */ private suspend fun showWindowingImageButtonTooltip() { private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) { val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height) val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height) val appHandleMenuWidth = val appHandleMenuWidth = Loading Loading @@ -188,18 +198,21 @@ class AppHandleEducationController( val windowingImageButtonTooltipConfig = val windowingImageButtonTooltipConfig = EducationViewConfig( EducationViewConfig( tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipText = tooltipText = getString(R.string.windowing_desktop_mode_image_button_education_tooltip), getString(R.string.windowing_desktop_mode_image_button_education_tooltip), arrowDirection = arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT, onEducationClickAction = { onEducationClickAction = { launchWithExceptionHandling { showExitWindowingTooltip() } launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } toDesktopModeCallback( toDesktopModeCallback( captionState.runningTaskInfo.taskId, captionState.runningTaskInfo.taskId, DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON) }, }, onDismissAction = { launchWithExceptionHandling { showExitWindowingTooltip() } }, onDismissAction = { launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) } }, ) ) windowingEducationViewController.showEducationTooltip( windowingEducationViewController.showEducationTooltip( Loading @@ -209,7 +222,7 @@ class AppHandleEducationController( } } /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ /** Show tooltip that points to app chip button and educates user on how to exit desktop mode */ private suspend fun showExitWindowingTooltip() { private suspend fun showExitWindowingTooltip(tooltipColorScheme: TooltipColorScheme) { windowDecorCaptionHandleRepository.captionStateFlow windowDecorCaptionHandleRepository.captionStateFlow // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered // After the previous tooltip was dismissed, wait for 400 ms and see if the user entered // desktop mode. // desktop mode. Loading Loading @@ -238,6 +251,7 @@ class AppHandleEducationController( val exitWindowingTooltipConfig = val exitWindowingTooltipConfig = EducationViewConfig( EducationViewConfig( tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipGlobalCoordinates, tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip), tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip), arrowDirection = arrowDirection = Loading @@ -254,6 +268,32 @@ class AppHandleEducationController( } } } } private fun tooltipColorScheme(captionState: CaptionState): TooltipColorScheme { context.withStyledAttributes( set = null, attrs = intArrayOf( com.android.internal.R.attr.materialColorOnTertiaryFixed, com.android.internal.R.attr.materialColorTertiaryFixed, com.android.internal.R.attr.materialColorTertiaryFixedDim), defStyleAttr = 0, defStyleRes = 0) { val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0) val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0) val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0) val taskInfo = (captionState as CaptionState.AppHandle).runningTaskInfo val tooltipContainerColor = if (decorThemeUtil.getAppTheme(taskInfo) == Theme.LIGHT) { tertiaryFixed } else { tertiaryFixedDim } return TooltipColorScheme(tooltipContainerColor, onTertiaryFixed, onTertiaryFixed) } return TooltipColorScheme(0, 0, 0) } /** /** * Setup callbacks for app handle education tooltips. * Setup callbacks for app handle education tooltips. * * Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipController.kt +32 −0 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.wm.shell.windowdecor.education package com.android.wm.shell.windowdecor.education import android.annotation.ColorInt import android.annotation.DimenRes import android.annotation.DimenRes import android.annotation.LayoutRes import android.annotation.LayoutRes import android.content.Context import android.content.Context Loading @@ -32,6 +33,7 @@ import android.widget.LinearLayout import android.widget.TextView import android.widget.TextView import android.window.DisplayAreaInfo import android.window.DisplayAreaInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import androidx.core.graphics.drawable.DrawableCompat import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce import androidx.dynamicanimation.animation.SpringForce import com.android.wm.shell.R import com.android.wm.shell.R Loading Loading @@ -120,6 +122,7 @@ class DesktopWindowingEducationTooltipController( hideEducationTooltip() hideEducationTooltip() tooltipViewConfig.onEducationClickAction() tooltipViewConfig.onEducationClickAction() } } setTooltipColorScheme(tooltipViewConfig.tooltipColorScheme) } } val tooltipDimens = tooltipDimens(tooltipView = tooltipView, tooltipViewConfig.arrowDirection) val tooltipDimens = tooltipDimens(tooltipView = tooltipView, tooltipViewConfig.arrowDirection) Loading Loading @@ -189,6 +192,21 @@ class DesktopWindowingEducationTooltipController( view = tooltipView) view = tooltipView) } } private fun View.setTooltipColorScheme(tooltipColorScheme: TooltipColorScheme) { requireViewById<LinearLayout>(R.id.tooltip_container).apply { background.setTint(tooltipColorScheme.container) } requireViewById<ImageView>(R.id.arrow_icon).apply { val wrappedDrawable = DrawableCompat.wrap(this.drawable) DrawableCompat.setTint(wrappedDrawable, tooltipColorScheme.container) } requireViewById<TextView>(R.id.tooltip_text).apply { setTextColor(tooltipColorScheme.text) } requireViewById<ImageView>(R.id.tooltip_icon).apply { val wrappedDrawable = DrawableCompat.wrap(this.drawable) DrawableCompat.setTint(wrappedDrawable, tooltipColorScheme.icon) } } private fun tooltipViewGlobalCoordinates( private fun tooltipViewGlobalCoordinates( tooltipViewGlobalCoordinates: Point, tooltipViewGlobalCoordinates: Point, arrowDirection: TooltipArrowDirection, arrowDirection: TooltipArrowDirection, Loading Loading @@ -255,6 +273,7 @@ class DesktopWindowingEducationTooltipController( */ */ data class EducationViewConfig( data class EducationViewConfig( @LayoutRes val tooltipViewLayout: Int, @LayoutRes val tooltipViewLayout: Int, val tooltipColorScheme: TooltipColorScheme, val tooltipViewGlobalCoordinates: Point, val tooltipViewGlobalCoordinates: Point, val tooltipText: String, val tooltipText: String, val arrowDirection: TooltipArrowDirection, val arrowDirection: TooltipArrowDirection, Loading @@ -262,6 +281,19 @@ class DesktopWindowingEducationTooltipController( val onDismissAction: () -> Unit, val onDismissAction: () -> Unit, ) ) /** * Color scheme of education view: * * @property container Color of the container of the tooltip. * @property text Text color of the [TextView] of education tooltip. * @property icon Color to be filled in tooltip's icon. */ data class TooltipColorScheme( @ColorInt val container: Int, @ColorInt val text: Int, @ColorInt val icon: Int, ) /** Direction of arrow of the tooltip */ /** Direction of arrow of the tooltip */ enum class TooltipArrowDirection { enum class TooltipArrowDirection { UP, UP, Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/education/DesktopWindowingEducationTooltipControllerTest.kt +33 −1 Original line number Original line Diff line number Diff line Loading @@ -30,12 +30,15 @@ import android.view.View import android.view.WindowManager import android.view.WindowManager import android.widget.TextView import android.widget.TextView import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.wm.shell.R import com.android.wm.shell.R import com.android.wm.shell.ShellTestCase import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipArrowDirection import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipArrowDirection import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Before import org.junit.Test import org.junit.Test Loading Loading @@ -240,14 +243,42 @@ class DesktopWindowingEducationTooltipControllerTest : ShellTestCase() { /* fromRotation= */ ROTATION_90, /* fromRotation= */ ROTATION_90, /* toRotation= */ ROTATION_180, /* toRotation= */ ROTATION_180, /* newDisplayAreaInfo= */ null, /* newDisplayAreaInfo= */ null, WindowContainerTransaction()) WindowContainerTransaction(), ) verify(mockPopupWindow, times(1)).releaseView() verify(mockPopupWindow, times(1)).releaseView() verify(mockDisplayController, atLeastOnce()).removeDisplayChangingController(any()) verify(mockDisplayController, atLeastOnce()).removeDisplayChangingController(any()) } } @Test fun showEducationTooltip_setTooltipColorScheme_correctColorsAreSet() { val tooltipColorScheme = TooltipColorScheme( container = Color.Red.toArgb(), text = Color.Blue.toArgb(), icon = Color.Green.toArgb()) val tooltipViewConfig = createTooltipConfig(tooltipColorScheme = tooltipColorScheme) tooltipController.showEducationTooltip(tooltipViewConfig = tooltipViewConfig, taskId = 123) verify(mockViewContainerFactory, times(1)) .create( windowManagerWrapper = any(), taskId = anyInt(), x = anyInt(), y = anyInt(), width = anyInt(), height = anyInt(), flags = anyInt(), view = tooltipViewArgumentCaptor.capture()) val tooltipTextView = tooltipViewArgumentCaptor.lastValue.findViewById<TextView>(R.id.tooltip_text) assertThat(tooltipTextView.textColors.defaultColor).isEqualTo(Color.Blue.toArgb()) } private fun createTooltipConfig( private fun createTooltipConfig( @LayoutRes tooltipViewLayout: Int = R.layout.desktop_windowing_education_top_arrow_tooltip, @LayoutRes tooltipViewLayout: Int = R.layout.desktop_windowing_education_top_arrow_tooltip, tooltipColorScheme: TooltipColorScheme = TooltipColorScheme( container = Color.Red.toArgb(), text = Color.Red.toArgb(), icon = Color.Red.toArgb()), tooltipViewGlobalCoordinates: Point = Point(0, 0), tooltipViewGlobalCoordinates: Point = Point(0, 0), tooltipText: String = "This is a tooltip", tooltipText: String = "This is a tooltip", arrowDirection: TooltipArrowDirection = TooltipArrowDirection.UP, arrowDirection: TooltipArrowDirection = TooltipArrowDirection.UP, Loading @@ -256,6 +287,7 @@ class DesktopWindowingEducationTooltipControllerTest : ShellTestCase() { ) = ) = DesktopWindowingEducationTooltipController.EducationViewConfig( DesktopWindowingEducationTooltipController.EducationViewConfig( tooltipViewLayout = tooltipViewLayout, tooltipViewLayout = tooltipViewLayout, tooltipColorScheme = tooltipColorScheme, tooltipViewGlobalCoordinates = tooltipViewGlobalCoordinates, tooltipViewGlobalCoordinates = tooltipViewGlobalCoordinates, tooltipText = tooltipText, tooltipText = tooltipText, arrowDirection = arrowDirection, arrowDirection = arrowDirection, Loading