From 51fbe4e5b10da6c6d01cb62cc34ba891245fa8e8 Mon Sep 17 00:00:00 2001 From: Kristen Halper Date: Thu, 18 Aug 2022 10:25:16 -0700 Subject: [PATCH] Update dependencies (#102) * Update gradle version * Update readme * Update dependencies * Update CompanionPane sample * Update ComposeGallery sample * Update Diary sample * Update DragAndDrop sample * Update DualView sample * Update DyAdd sample * Update ExtendedCanvas sample * Update ListDetail sample * Update NavRail sample * Update SourceEditor sample * Update TwoPage sample * Update VideoPlusChat sample * Fix readme table format --- CompanionPane/build.gradle | 5 +- .../samples/companionpane/LayoutTest.kt | 107 ++++-------------- .../samples/companionpane/TopAppBarTest.kt | 39 +++---- CompanionPane/src/main/AndroidManifest.xml | 3 +- .../display/samples/companionpane/HomePage.kt | 10 +- .../companionpane/ui/view/LandscapeLayouts.kt | 11 +- .../companionpane/ui/view/PortraitLayouts.kt | 10 +- ComposeGallery/build.gradle | 1 + .../composegallery/PaneSynchronizationTest.kt | 68 ++--------- .../samples/composegallery/TopAppBarTest.kt | 36 +++--- ComposeGallery/src/main/AndroidManifest.xml | 3 +- .../composegallery/ui/view/DetailPane.kt | 8 +- .../composegallery/ui/view/ListPane.kt | 4 + Diary/build.gradle | 1 + Diary/src/main/AndroidManifest.xml | 3 +- .../samples/diary/ui/pages/CalendarPage.kt | 50 ++++---- .../samples/diary/ui/pages/DiaryPage.kt | 49 ++++---- DragAndDrop/build.gradle | 1 + .../samples/draganddrop/DragAndDropTest.kt | 8 -- DragAndDrop/src/main/AndroidManifest.xml | 3 +- .../samples/draganddrop/ui/view/DragPane.kt | 10 +- .../samples/draganddrop/ui/view/DropPane.kt | 17 +-- .../samples/draganddrop/ui/view/HomePage.kt | 3 +- DualView/build.gradle | 3 + .../display/samples/dualview/MapImageTest.kt | 9 -- .../samples/dualview/RestaurantListTest.kt | 21 +--- .../display/samples/dualview/TopAppBarTest.kt | 36 +++--- DualView/src/main/AndroidManifest.xml | 3 +- .../samples/dualview/ui/view/MapView.kt | 15 ++- .../dualview/ui/view/RestaurantView.kt | 21 ++-- DyAdd/build.gradle | 1 + DyAdd/src/main/AndroidManifest.xml | 3 +- .../dyadd/ui/pages/components/Answer.kt | 10 +- .../dyadd/ui/pages/components/ButtonGrids.kt | 39 ++----- .../dyadd/ui/pages/components/Buttons.kt | 3 +- .../dyadd/ui/pages/components/History.kt | 9 +- ExtendedCanvas/build.gradle | 10 +- .../extendedcanvas/ExtendedCanvasTest.kt | 13 --- ExtendedCanvas/src/main/AndroidManifest.xml | 3 +- .../samples/extendedcanvas/HomePage.kt | 22 ++-- ListDetail/build.gradle | 3 + .../samples/listdetail/ListDetailTest.kt | 14 --- .../samples/listdetail/TopAppBarTest.kt | 49 +++----- ListDetail/src/main/AndroidManifest.xml | 3 +- .../samples/listdetail/ui/view/DetailView.kt | 16 ++- .../samples/listdetail/ui/view/ListView.kt | 19 ++-- NavigationRail/build.gradle | 3 + .../samples/navigationrail/DetailTest.kt | 32 +++--- .../samples/navigationrail/GalleryTest.kt | 15 +-- .../navigationrail/NavComponentTest.kt | 23 ++-- .../navigationrail/PaneSynchronizationTest.kt | 43 ++----- NavigationRail/src/main/AndroidManifest.xml | 3 +- .../ui/components/GalleryNavWithSelector.kt | 16 +-- .../navigationrail/ui/view/GalleryView.kt | 26 +++-- README.md | 14 +-- SourceEditorCompose/build.gradle | 1 + .../src/main/AndroidManifest.xml | 3 +- .../ui/pages/EditorPage.kt | 45 ++++---- .../ui/pages/PreviewPage.kt | 50 ++++---- TwoPage/build.gradle | 3 + .../samples/twopage/PageContentTest.kt | 12 +- .../display/samples/twopage/PageSwipeTest.kt | 49 ++------ TwoPage/src/main/AndroidManifest.xml | 3 +- .../samples/twopage/ui/view/HomePage.kt | 20 +++- VideoPlusChat/build.gradle | 1 + VideoPlusChat/src/main/AndroidManifest.xml | 3 +- .../ui/views/ChatPage.kt | 2 + .../ui/views/VideoPage.kt | 24 +--- dependencies.gradle | 30 ++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 70 files changed, 472 insertions(+), 726 deletions(-) diff --git a/CompanionPane/build.gradle b/CompanionPane/build.gradle index ebc2aac..9cd004b 100644 --- a/CompanionPane/build.gradle +++ b/CompanionPane/build.gradle @@ -36,6 +36,7 @@ android { excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] } } + namespace 'com.microsoft.device.display.samples.companionpane' } dependencies { @@ -53,6 +54,8 @@ dependencies { implementation microsoftDependencies.twoPaneLayout implementation microsoftDependencies.windowState + implementation googleDependencies.material + androidTestImplementation testDependencies.androidxTestCore androidTestImplementation testDependencies.androidxTestRules androidTestImplementation testDependencies.androidxTestRunner @@ -62,5 +65,5 @@ dependencies { androidTestImplementation testDependencies.uiAutomator androidTestImplementation microsoftDependencies.composeTesting - implementation googleDependencies.material + debugImplementation testDependencies.composeUITestManifest } \ No newline at end of file diff --git a/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/LayoutTest.kt b/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/LayoutTest.kt index 48725ec..661a309 100644 --- a/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/LayoutTest.kt +++ b/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/LayoutTest.kt @@ -10,49 +10,34 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithTag -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.uiautomator.UiDevice -import androidx.window.testing.layout.WindowLayoutInfoPublisherRule -import com.microsoft.device.display.samples.companionpane.ui.theme.CompanionPaneAppTheme +import com.microsoft.device.dualscreen.testing.compose.foldableRuleChain import com.microsoft.device.dualscreen.testing.compose.getString -import com.microsoft.device.dualscreen.testing.compose.simulateHorizontalFoldingFeature -import com.microsoft.device.dualscreen.testing.compose.simulateVerticalFoldingFeature -import com.microsoft.device.dualscreen.testing.filters.DeviceOrientation -import com.microsoft.device.dualscreen.windowstate.WindowMode -import com.microsoft.device.dualscreen.windowstate.rememberWindowState +import com.microsoft.device.dualscreen.testing.filters.DualScreenTest +import com.microsoft.device.dualscreen.testing.filters.SingleScreenTest +import com.microsoft.device.dualscreen.testing.rules.FoldableTestRule +import com.microsoft.device.dualscreen.testing.runner.FoldableJUnit4ClassRunner import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain import org.junit.rules.TestRule +import org.junit.runner.RunWith +@RunWith(FoldableJUnit4ClassRunner::class) class LayoutTest { private val composeTestRule = createAndroidComposeRule() - private val publisherRule = WindowLayoutInfoPublisherRule() - private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + private val foldableTestRule = FoldableTestRule() - @get: Rule - val testRule: TestRule - - init { - testRule = RuleChain.outerRule(publisherRule).around(composeTestRule) - RuleChain.outerRule(composeTestRule) - } + @get:Rule + val testRule: TestRule = foldableRuleChain(composeTestRule, foldableTestRule) /** * Tests that the single portrait layout appears when one pane is shown and the device is in * the portrait orientation */ @Test - @DeviceOrientation(orientation = UiAutomation.ROTATION_FREEZE_0) + @SingleScreenTest(orientation = UiAutomation.ROTATION_FREEZE_0) fun app_testSinglePortraitLayout() { - composeTestRule.setContent { - CompanionPaneAppTheme { - CompanionPaneAppContent(WindowMode.SINGLE_PORTRAIT) - } - } - // Check that single portrait layout is shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.single_port)) + composeTestRule.onNodeWithTag(getString(R.string.single_port)) .assertIsDisplayed() } @@ -61,16 +46,10 @@ class LayoutTest { * the landscape orientation */ @Test - @DeviceOrientation(orientation = UiAutomation.ROTATION_FREEZE_90) + @SingleScreenTest(orientation = UiAutomation.ROTATION_FREEZE_90) fun app_testSingleLandscapeLayout() { - composeTestRule.setContent { - CompanionPaneAppTheme { - CompanionPaneAppContent(WindowMode.SINGLE_LANDSCAPE) - } - } - // Check that single landscape layout is shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.single_land)) + composeTestRule.onNodeWithTag(getString(R.string.single_land)) .assertIsDisplayed() } @@ -79,20 +58,12 @@ class LayoutTest { */ @ExperimentalTestApi @Test + @DualScreenTest(orientation = UiAutomation.ROTATION_FREEZE_0) fun app_testDualPortraitLayout() { - composeTestRule.setContent { - CompanionPaneAppTheme { - CompanionPaneAppContent(WindowMode.DUAL_PORTRAIT) - } - } - - // Simulate vertical foldingFeature - publisherRule.simulateVerticalFoldingFeature(composeTestRule) - // Check that dual portrait panes are shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_port_pane1)) + composeTestRule.onNodeWithTag(getString(R.string.dual_port_pane1)) .assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_port_pane2)) + composeTestRule.onNodeWithTag(getString(R.string.dual_port_pane2)) .assertIsDisplayed() } @@ -101,50 +72,12 @@ class LayoutTest { */ @ExperimentalTestApi @Test + @DualScreenTest(orientation = UiAutomation.ROTATION_FREEZE_90) fun app_testDualLandscapeLayout() { - composeTestRule.setContent { - CompanionPaneAppTheme { - CompanionPaneAppContent(WindowMode.DUAL_LANDSCAPE) - } - } - - // Simulate horizontal foldingFeature - publisherRule.simulateHorizontalFoldingFeature(composeTestRule) - // Check that dual landscape panes are shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_land_pane1)) + composeTestRule.onNodeWithTag(getString(R.string.dual_land_pane1)) .assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_land_pane2)) - .assertIsDisplayed() - } - - /** - * Test that app responds correctly when fold orientation switches from horizontal to vertical - */ - @Test - fun app_testDualLayoutRespondsToFoldOrientationChange() { - composeTestRule.setContent { - CompanionPaneAppTheme { - CompanionPaneApp(composeTestRule.activity.rememberWindowState()) - } - } - - // Simulate horizontal foldingFeature - publisherRule.simulateHorizontalFoldingFeature(composeTestRule) - - // Check that dual landscape panes are shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_land_pane1)) - .assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_land_pane2)) - .assertIsDisplayed() - - // Simulate vertical foldingFeature - publisherRule.simulateVerticalFoldingFeature(composeTestRule) - - // Check that dual portrait panes are shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_port_pane1)) - .assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.dual_port_pane2)) + composeTestRule.onNodeWithTag(getString(R.string.dual_land_pane2)) .assertIsDisplayed() } } diff --git a/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/TopAppBarTest.kt b/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/TopAppBarTest.kt index b2da80d..054d539 100644 --- a/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/TopAppBarTest.kt +++ b/CompanionPane/src/androidTest/java/com/microsoft/device/display/samples/companionpane/TopAppBarTest.kt @@ -8,28 +8,17 @@ package com.microsoft.device.display.samples.companionpane import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.hasParent import androidx.compose.ui.test.hasTestTag -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag -import androidx.window.testing.layout.WindowLayoutInfoPublisherRule import com.microsoft.device.display.samples.companionpane.ui.theme.CompanionPaneAppTheme import com.microsoft.device.dualscreen.testing.compose.getString import com.microsoft.device.dualscreen.windowstate.WindowMode import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.rules.TestRule class TopAppBarTest { - private val composeTestRule = createAndroidComposeRule() - private val publisherRule = WindowLayoutInfoPublisherRule() - @get: Rule - val testRule: TestRule - - init { - testRule = RuleChain.outerRule(publisherRule).around(composeTestRule) - RuleChain.outerRule(composeTestRule) - } + val composeTestRule = createComposeRule() /** * Tests that the app title shows in the pane 1 top bar when in single portrait mode @@ -45,9 +34,9 @@ class TopAppBarTest { // Check that app title appears in top bar composeTestRule.onNode( hasParent( - hasTestTag(composeTestRule.getString(R.string.top_bar)) + hasTestTag(getString(R.string.top_bar)) ) - ).assertTextEquals(composeTestRule.getString(R.string.app_name)) + ).assertTextEquals(getString(R.string.app_name)) } /** @@ -64,9 +53,9 @@ class TopAppBarTest { // Check that app title appears in top bar composeTestRule.onNode( hasParent( - hasTestTag(composeTestRule.getString(R.string.top_bar)) + hasTestTag(getString(R.string.top_bar)) ) - ).assertTextEquals(composeTestRule.getString(R.string.app_name)) + ).assertTextEquals(getString(R.string.app_name)) } /** @@ -83,9 +72,9 @@ class TopAppBarTest { // Check that app title appears in top bar composeTestRule.onNode( hasParent( - hasTestTag(composeTestRule.getString(R.string.top_bar)) + hasTestTag(getString(R.string.top_bar)) ) - ).assertTextEquals(composeTestRule.getString(R.string.app_name)) + ).assertTextEquals(getString(R.string.app_name)) } /** @@ -102,9 +91,9 @@ class TopAppBarTest { // Check that app title appears in top bar composeTestRule.onNode( hasParent( - hasTestTag(composeTestRule.getString(R.string.top_bar)) + hasTestTag(getString(R.string.top_bar)) ) - ).assertTextEquals(composeTestRule.getString(R.string.app_name)) + ).assertTextEquals(getString(R.string.app_name)) } /** @@ -119,7 +108,7 @@ class TopAppBarTest { } // Check that top bar doesn't exist - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.top_bar)) + composeTestRule.onNodeWithTag(getString(R.string.top_bar)) .assertDoesNotExist() } @@ -135,7 +124,7 @@ class TopAppBarTest { } // Check that top bar doesn't exist - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.top_bar)) + composeTestRule.onNodeWithTag(getString(R.string.top_bar)) .assertDoesNotExist() } @@ -153,7 +142,7 @@ class TopAppBarTest { // Check that app title appears blank in top bar composeTestRule.onNode( hasParent( - hasTestTag(composeTestRule.getString(R.string.top_bar)) + hasTestTag(getString(R.string.top_bar)) ) ).assertTextEquals("") } @@ -170,7 +159,7 @@ class TopAppBarTest { } // Check that top bar doesn't exist - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.top_bar)) + composeTestRule.onNodeWithTag(getString(R.string.top_bar)) .assertDoesNotExist() } } diff --git a/CompanionPane/src/main/AndroidManifest.xml b/CompanionPane/src/main/AndroidManifest.xml index ac8176a..cf729e3 100644 --- a/CompanionPane/src/main/AndroidManifest.xml +++ b/CompanionPane/src/main/AndroidManifest.xml @@ -4,8 +4,7 @@ ~ Licensed under the MIT License. --> - + PortraitLayout() - WindowMode.SINGLE_LANDSCAPE -> LandscapeLayout(sliderState) - WindowMode.DUAL_PORTRAIT -> DualPortraitPane1() - WindowMode.DUAL_LANDSCAPE -> DualLandscapePane1() + WindowMode.SINGLE_PORTRAIT -> PortraitLayout(it) + WindowMode.SINGLE_LANDSCAPE -> LandscapeLayout(sliderState, it) + WindowMode.DUAL_PORTRAIT -> DualPortraitPane1(it) + WindowMode.DUAL_LANDSCAPE -> DualLandscapePane1(it) } } } @@ -78,7 +78,7 @@ fun Pane2(windowMode: WindowMode, sliderState: SliderState = rememberSliderState Scaffold( topBar = { CompanionPaneTopBar() } ) { - DualPortraitPane2(sliderState) + DualPortraitPane2(sliderState, it) } } WindowMode.DUAL_LANDSCAPE -> DualLandscapePane2(sliderState) diff --git a/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/LandscapeLayouts.kt b/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/LandscapeLayouts.kt index 040f56c..31de90c 100644 --- a/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/LandscapeLayouts.kt +++ b/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/LandscapeLayouts.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -39,10 +40,11 @@ import com.microsoft.device.display.samples.companionpane.ui.components.Vignette private val shortSlideWidth = 200.dp @Composable -fun DualLandscapePane1() { +fun DualLandscapePane1(paddingValues: PaddingValues) { Box( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .padding(bottom = 20.dp) .clipToBounds() .testTag(stringResource(R.string.dual_land_pane1)), @@ -53,11 +55,12 @@ fun DualLandscapePane1() { } @Composable -fun DualLandscapePane2(sliderState: SliderState) { +fun DualLandscapePane2(sliderState: SliderState, paddingValues: PaddingValues = PaddingValues()) { Surface { Column( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .padding(start = 5.dp, end = 5.dp, bottom = 5.dp) .testTag(stringResource(R.string.dual_land_pane2)), verticalArrangement = Arrangement.SpaceEvenly, @@ -67,6 +70,7 @@ fun DualLandscapePane2(sliderState: SliderState) { horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier .fillMaxWidth() + .padding(paddingValues) .horizontalScroll(rememberScrollState()) ) { Column(verticalArrangement = Arrangement.SpaceEvenly) { @@ -106,10 +110,11 @@ fun DualLandscapePane2(sliderState: SliderState) { } @Composable -fun LandscapeLayout(sliderState: SliderState) { +fun LandscapeLayout(sliderState: SliderState, paddingValues: PaddingValues) { Column( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .padding(top = 20.dp, start = 20.dp, end = 10.dp, bottom = 10.dp) .verticalScroll(rememberScrollState()) .testTag(stringResource(R.string.single_land)), diff --git a/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/PortraitLayouts.kt b/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/PortraitLayouts.kt index 816fe35..f78248d 100644 --- a/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/PortraitLayouts.kt +++ b/CompanionPane/src/main/java/com/microsoft/device/display/samples/companionpane/ui/view/PortraitLayouts.kt @@ -7,6 +7,7 @@ package com.microsoft.device.display.samples.companionpane.ui.view import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height @@ -33,10 +34,11 @@ import com.microsoft.device.display.samples.companionpane.ui.components.Vignette private val longSliderWidth = 350.dp @Composable -fun DualPortraitPane1() { +fun DualPortraitPane1(paddingValues: PaddingValues) { Column( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .testTag(stringResource(R.string.dual_port_pane1)), verticalArrangement = Arrangement.SpaceEvenly ) { @@ -46,10 +48,11 @@ fun DualPortraitPane1() { } @Composable -fun DualPortraitPane2(sliderState: SliderState) { +fun DualPortraitPane2(sliderState: SliderState, paddingValues: PaddingValues) { Column( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .testTag(stringResource(R.string.dual_port_pane2)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(80.dp, Alignment.CenterVertically) @@ -73,10 +76,11 @@ fun DualPortraitPane2(sliderState: SliderState) { } @Composable -fun PortraitLayout() { +fun PortraitLayout(paddingValues: PaddingValues) { Column( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .testTag(stringResource(R.string.single_port)), verticalArrangement = Arrangement.spacedBy(15.dp), horizontalAlignment = Alignment.CenterHorizontally diff --git a/ComposeGallery/build.gradle b/ComposeGallery/build.gradle index a63b496..3171cbb 100644 --- a/ComposeGallery/build.gradle +++ b/ComposeGallery/build.gradle @@ -36,6 +36,7 @@ android { excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] } } + namespace 'com.microsoft.device.display.samples.composegallery' } dependencies { diff --git a/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/PaneSynchronizationTest.kt b/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/PaneSynchronizationTest.kt index 1657ffe..399eb3d 100644 --- a/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/PaneSynchronizationTest.kt +++ b/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/PaneSynchronizationTest.kt @@ -16,32 +16,26 @@ import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToIndex -import androidx.window.testing.layout.WindowLayoutInfoPublisherRule import com.microsoft.device.display.samples.composegallery.models.DataProvider -import com.microsoft.device.display.samples.composegallery.ui.ComposeGalleryTheme -import com.microsoft.device.display.samples.composegallery.ui.view.ComposeGalleryApp +import com.microsoft.device.dualscreen.testing.compose.foldableRuleChain import com.microsoft.device.dualscreen.testing.compose.getString -import com.microsoft.device.dualscreen.testing.compose.simulateVerticalFoldingFeature import com.microsoft.device.dualscreen.testing.filters.MockFoldingFeature +import com.microsoft.device.dualscreen.testing.rules.FoldableTestRule +import com.microsoft.device.dualscreen.testing.runner.FoldableJUnit4ClassRunner import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain import org.junit.rules.TestRule +import org.junit.runner.RunWith +@RunWith(FoldableJUnit4ClassRunner::class) class PaneSynchronizationTest { private val composeTestRule = createAndroidComposeRule() - private val publisherRule = WindowLayoutInfoPublisherRule() + private val foldableTestRule = FoldableTestRule() - @get: Rule - val testRule: TestRule - - init { - testRule = RuleChain.outerRule(publisherRule).around(composeTestRule) - RuleChain.outerRule(composeTestRule) - } + @get:Rule + val testRule: TestRule = foldableRuleChain(composeTestRule, foldableTestRule) /** * Test that clicking an item in the list pane updates the image shown in the detail pane @@ -50,12 +44,6 @@ class PaneSynchronizationTest { @Test @MockFoldingFeature(orientation = MockFoldingFeature.FoldingFeatureOrientation.VERTICAL) fun app_verticalFold_testListItemClickUpdatesDetailPane() { - composeTestRule.setContent { - ComposeGalleryTheme { - ComposeGalleryApp() - } - } - // Scroll to end of list val index = 7 composeTestRule.onNode( @@ -80,40 +68,6 @@ class PaneSynchronizationTest { ).assertIsDisplayed() } - /** - * Test that a selection made when no fold is present (not a large screen device) is remembered when a vertical - * fold is introduced - */ - @Test - fun app_testSelectionPersistenceAfterVerticalFold() { - composeTestRule.setContent { - ComposeGalleryTheme { - ComposeGalleryApp() - } - } - - // Click on third surface duo entry - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.duo3_content_des)) - .performClick() - - // Check that detail view of third surface duo is displayed - composeTestRule.onNodeWithText(composeTestRule.getString(R.string.duo3_id)) - .assertIsDisplayed() - - // REVISIT: return to list view so state isn't saved for other tests - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_list)) - .performClick() - - // Simulate a vertical foldFeature so two panes are visible - publisherRule.simulateVerticalFoldingFeature(composeTestRule) - - // Check that third surface duo image is still displayed - composeTestRule.onNode( - hasContentDescription(composeTestRule.getString(R.string.duo3_content_des)) - and hasAnySibling(hasText(composeTestRule.getString(R.string.duo3_id))) - ).assertIsDisplayed() - } - /** * Test that one pane is continuously shown on a non-large screen device, even when a horizontal fold is * introduced @@ -121,12 +75,6 @@ class PaneSynchronizationTest { @Test @MockFoldingFeature(orientation = MockFoldingFeature.FoldingFeatureOrientation.HORIZONTAL) fun app_testOnePaneShowsWithHorizontalFold() { - composeTestRule.setContent { - ComposeGalleryTheme { - ComposeGalleryApp() - } - } - // Check that the list view is displayed composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.gallery_list)) .assertIsDisplayed() diff --git a/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/TopAppBarTest.kt b/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/TopAppBarTest.kt index a51863e..3483f3a 100644 --- a/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/TopAppBarTest.kt +++ b/ComposeGallery/src/androidTest/java/com/microsoft/device/display/samples/composegallery/TopAppBarTest.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasParent import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick @@ -29,7 +29,7 @@ import org.junit.Test */ class TopAppBarTest { @get: Rule - val composeTestRule = createAndroidComposeRule() + val composeTestRule = createComposeRule() private val models = DataProvider.imageModels private val selectedImageIndex = 0 @@ -48,7 +48,7 @@ class TopAppBarTest { } // isDualMode is true, icon should be hidden - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_detail)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_detail)) .assertDoesNotExist() } @@ -64,7 +64,7 @@ class TopAppBarTest { } // isDualMode is false, icon should show - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_detail)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_detail)) .assertIsDisplayed() } @@ -80,7 +80,7 @@ class TopAppBarTest { } // isDualMode is true, icon should be hidden - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_list)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_list)) .assertDoesNotExist() } @@ -96,7 +96,7 @@ class TopAppBarTest { } // isDualMode is false, icon should show - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_list)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_list)) .assertIsDisplayed() } @@ -112,8 +112,8 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.top_app_bar))) - and hasText(composeTestRule.getString(R.string.app_name)) + hasParent(hasTestTag(getString(R.string.top_app_bar))) + and hasText(getString(R.string.app_name)) ).assertIsDisplayed() } @@ -129,8 +129,8 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.top_app_bar))) - and hasText(composeTestRule.getString(R.string.app_name)) + hasParent(hasTestTag(getString(R.string.top_app_bar))) + and hasText(getString(R.string.app_name)) ).assertIsDisplayed() } @@ -146,8 +146,8 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.top_app_bar))) - and hasText(composeTestRule.getString(R.string.app_name)) + hasParent(hasTestTag(getString(R.string.top_app_bar))) + and hasText(getString(R.string.app_name)) ).assertIsDisplayed() } @@ -163,7 +163,7 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.top_app_bar))) + hasParent(hasTestTag(getString(R.string.top_app_bar))) and hasText("") ).assertExists() } @@ -180,23 +180,23 @@ class TopAppBarTest { } // Check that list pane is currently displayed - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.gallery_list)) + composeTestRule.onNodeWithTag(getString(R.string.gallery_list)) .assertIsDisplayed() // Click on picture/detail icon - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_detail)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_detail)) .performClick() // Check that list pane is no longer displayed - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.gallery_list)) + composeTestRule.onNodeWithTag(getString(R.string.gallery_list)) .assertDoesNotExist() // Click on list icon - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_list)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_list)) .performClick() // Check that list pane is displayed again - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.gallery_list)) + composeTestRule.onNodeWithTag(getString(R.string.gallery_list)) .assertIsDisplayed() } } diff --git a/ComposeGallery/src/main/AndroidManifest.xml b/ComposeGallery/src/main/AndroidManifest.xml index 8b6d5b0..646096b 100644 --- a/ComposeGallery/src/main/AndroidManifest.xml +++ b/ComposeGallery/src/main/AndroidManifest.xml @@ -4,8 +4,7 @@ ~ Licensed under the MIT License. --> - + , selectedIndex: Int) { ) } ) { - GalleryItemDetail(models = models, selectedIndex = selectedIndex) + GalleryItemDetail(models = models, selectedIndex = selectedIndex, paddingValues = it) } } @@ -54,7 +56,7 @@ private fun TwoPaneScope.DetailActions() { } @Composable -private fun GalleryItemDetail(models: List, selectedIndex: Int) { +private fun GalleryItemDetail(models: List, selectedIndex: Int, paddingValues: PaddingValues) { val selectedImageModel = models[selectedIndex] Crossfade( @@ -62,7 +64,7 @@ private fun GalleryItemDetail(models: List, selectedIndex: Int) { animationSpec = tween(600) ) { Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize().padding(paddingValues), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.CenterVertically) ) { diff --git a/ComposeGallery/src/main/java/com/microsoft/device/display/samples/composegallery/ui/view/ListPane.kt b/ComposeGallery/src/main/java/com/microsoft/device/display/samples/composegallery/ui/view/ListPane.kt index 4e84619..3e72695 100644 --- a/ComposeGallery/src/main/java/com/microsoft/device/display/samples/composegallery/ui/view/ListPane.kt +++ b/ComposeGallery/src/main/java/com/microsoft/device/display/samples/composegallery/ui/view/ListPane.kt @@ -7,6 +7,7 @@ package com.microsoft.device.display.samples.composegallery.ui.view import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight @@ -56,6 +57,7 @@ fun TwoPaneScope.ListPane( models = models, selectedImageIndex = selectedImageIndex, updateImageIndex = updateImageIndex, + paddingValues = it ) } } @@ -76,10 +78,12 @@ private fun TwoPaneScope.GalleryList( models: List, selectedImageIndex: Int, updateImageIndex: (Int) -> Unit, + paddingValues: PaddingValues ) { LazyColumn( modifier = Modifier .fillMaxSize() + .padding(paddingValues) .testTag(stringResource(R.string.gallery_list)), ) { itemsIndexed(models) { index, item -> diff --git a/Diary/build.gradle b/Diary/build.gradle index 0f65f38..7d90f15 100644 --- a/Diary/build.gradle +++ b/Diary/build.gradle @@ -43,6 +43,7 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.microsoft.device.display.samples.diary' } dependencies { diff --git a/Diary/src/main/AndroidManifest.xml b/Diary/src/main/AndroidManifest.xml index acd7588..2e123e1 100644 --- a/Diary/src/main/AndroidManifest.xml +++ b/Diary/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/CalendarPage.kt b/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/CalendarPage.kt index ea88148..8902365 100644 --- a/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/CalendarPage.kt +++ b/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/CalendarPage.kt @@ -42,31 +42,10 @@ fun TwoPaneScope.CalendarPage( updateDate: (LocalDate) -> Unit, updateContent: () -> Unit ) { - val twoPaneScope = this Scaffold( - topBar = { - TopAppBar( - title = { - Text(text = stringResource(id = R.string.app_name)) - }, - backgroundColor = MaterialTheme.colors.primaryVariant, - actions = { - if (twoPaneScope.isSinglePane) { - IconButton( - onClick = { twoPaneScope.navigateToPane2() } - ) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = stringResource(R.string.edit_diary) - ) - } - } - } - ) - } - ) { - - Column { + topBar = { CalendarTopBar() } + ) { paddingValues -> + Column(modifier = Modifier.padding(paddingValues)) { AndroidView( { CalendarView(it) @@ -87,14 +66,12 @@ fun TwoPaneScope.CalendarPage( } ) - Spacer( modifier = Modifier .background(color = Color.Gray) .height(1.dp) .fillMaxWidth() ) - Text( modifier = Modifier .fillMaxWidth() @@ -110,10 +87,9 @@ fun TwoPaneScope.CalendarPage( ) ) ) - Text( + modifier = Modifier.padding(10.dp), text = content, - Modifier.padding(10.dp), style = TextStyle( fontSize = 18.sp, textAlign = TextAlign.Left, @@ -122,3 +98,21 @@ fun TwoPaneScope.CalendarPage( } } } + +@Composable +fun TwoPaneScope.CalendarTopBar() { + TopAppBar( + title = { Text(text = stringResource(id = R.string.app_name)) }, + backgroundColor = MaterialTheme.colors.primaryVariant, + actions = { + if (this@CalendarTopBar.isSinglePane) { + IconButton(onClick = { this@CalendarTopBar.navigateToPane2() }) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = stringResource(R.string.edit_diary) + ) + } + } + } + ) +} diff --git a/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/DiaryPage.kt b/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/DiaryPage.kt index c106db2..c3b2414 100644 --- a/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/DiaryPage.kt +++ b/Diary/src/main/java/com/microsoft/device/display/samples/diary/ui/pages/DiaryPage.kt @@ -35,36 +35,13 @@ fun TwoPaneScope.DiaryPage( selectedDate: LocalDate, updateContent: () -> Unit ) { - val twoPaneScope = this val context = LocalContext.current val rootDataDir: File = context.applicationContext.dataDir Scaffold( - topBar = { - TopAppBar( - title = { - if (isSinglePane) { - Text(text = stringResource(R.string.app_name)) - } - }, - backgroundColor = MaterialTheme.colors.primaryVariant, - actions = { - if (twoPaneScope.isSinglePane) { - IconButton( - onClick = { twoPaneScope.navigateToPane1() } - ) { - Icon( - imageVector = Icons.Default.DateRange, - contentDescription = stringResource(R.string.date_picker) - ) - } - } - } - ) - } + topBar = { DiaryTopBar() } ) { - Column { - + Column(modifier = Modifier.padding(it)) { TextField( value = text, placeholder = { Text(stringResource(R.string.diary_placeholder)) }, @@ -74,7 +51,6 @@ fun TwoPaneScope.DiaryPage( .fillMaxSize() .weight(0.9f) ) - Button( modifier = Modifier .width(150.dp) @@ -90,3 +66,24 @@ fun TwoPaneScope.DiaryPage( } } } + +@Composable +fun TwoPaneScope.DiaryTopBar() { + TopAppBar( + title = { + if (isSinglePane) + Text(text = stringResource(R.string.app_name)) + }, + backgroundColor = MaterialTheme.colors.primaryVariant, + actions = { + if (this@DiaryTopBar.isSinglePane) { + IconButton(onClick = { this@DiaryTopBar.navigateToPane1() }) { + Icon( + imageVector = Icons.Default.DateRange, + contentDescription = stringResource(R.string.date_picker) + ) + } + } + } + ) +} diff --git a/DragAndDrop/build.gradle b/DragAndDrop/build.gradle index e9d90c3..cb1bd12 100644 --- a/DragAndDrop/build.gradle +++ b/DragAndDrop/build.gradle @@ -31,6 +31,7 @@ android { excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] } } + namespace 'com.microsoft.device.display.samples.draganddrop' } dependencies { diff --git a/DragAndDrop/src/androidTest/java/com/microsoft/device/display/samples/draganddrop/DragAndDropTest.kt b/DragAndDrop/src/androidTest/java/com/microsoft/device/display/samples/draganddrop/DragAndDropTest.kt index 7f3e313..b2e1a55 100644 --- a/DragAndDrop/src/androidTest/java/com/microsoft/device/display/samples/draganddrop/DragAndDropTest.kt +++ b/DragAndDrop/src/androidTest/java/com/microsoft/device/display/samples/draganddrop/DragAndDropTest.kt @@ -11,8 +11,6 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.window.testing.layout.WindowLayoutInfoPublisherRule -import com.microsoft.device.display.samples.draganddrop.ui.theme.DragAndDropSamplesTheme -import com.microsoft.device.display.samples.draganddrop.ui.view.DragAndDropApp import com.microsoft.device.dualscreen.testing.compose.getString import com.microsoft.device.dualscreen.testing.compose.simulateHorizontalFoldingFeature import org.junit.Rule @@ -38,12 +36,6 @@ class DragAndDropTest { */ @Test fun app_showsBothPanes_before_and_after_horizontalFold() { - composeTestRule.setContent { - DragAndDropSamplesTheme { - DragAndDropApp() - } - } - // Assert the drag pane is now shown composeTestRule.onNodeWithTag( composeTestRule.getString(R.string.drag_pane) diff --git a/DragAndDrop/src/main/AndroidManifest.xml b/DragAndDrop/src/main/AndroidManifest.xml index ad1bb30..30b0a62 100644 --- a/DragAndDrop/src/main/AndroidManifest.xml +++ b/DragAndDrop/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + Unit ) { Scaffold( - topBar = { - TopAppBar(backgroundColor = colors.primary) {} - }, - floatingActionButton = { - ResetFloatingActionButton(updateDragText, updateDragImage) - } + topBar = { TopAppBar(backgroundColor = colors.primary) {} }, + floatingActionButton = { ResetFloatingActionButton(updateDragText, updateDragImage) } ) { - DropPane(dragText, updateDragText, dragImage, updateDragImage) + DropPane(dragText, updateDragText, dragImage, updateDragImage, paddingValues = it) } } @@ -65,7 +62,8 @@ fun DropPane( updateDragText: (String?) -> Unit, dragImage: Painter?, updateDragImage: (Painter?) -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + paddingValues: PaddingValues = PaddingValues() ) { var isDroppingText by remember { mutableStateOf(false) } var isDroppingImage by remember { mutableStateOf(false) } @@ -73,6 +71,7 @@ fun DropPane( DropContainer( modifier = modifier + .padding(paddingValues) .testTag(stringResource(R.string.drop_pane)), onDrag = { inBounds, isDragging -> if (!inBounds || !isDragging) { @@ -112,6 +111,7 @@ fun DropPaneContent(dragText: String?, isDroppingText: Boolean, dragImage: Paint @Composable fun RowScope.DropImageBox(dragImage: Painter?, isDroppingImage: Boolean) { val boxColor = if (isDroppingImage) mediumGray else lightGray + Box( modifier = Modifier .weight(1f) @@ -148,6 +148,7 @@ fun DropImagePlaceholder() { @Composable fun RowScope.DropTextBox(text: String?, isDroppingText: Boolean) { val boxColor = if (isDroppingText) mediumGray else lightGray + Box( modifier = Modifier .weight(1f) diff --git a/DragAndDrop/src/main/java/com/microsoft/device/display/samples/draganddrop/ui/view/HomePage.kt b/DragAndDrop/src/main/java/com/microsoft/device/display/samples/draganddrop/ui/view/HomePage.kt index d45ffac..44595b4 100644 --- a/DragAndDrop/src/main/java/com/microsoft/device/display/samples/draganddrop/ui/view/HomePage.kt +++ b/DragAndDrop/src/main/java/com/microsoft/device/display/samples/draganddrop/ui/view/HomePage.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.material.FloatingActionButton import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme @@ -91,7 +92,7 @@ fun DragAndDropSinglePane( ResetFloatingActionButton(updateDragText, updateDragImage) } ) { - Column { + Column(modifier = Modifier.padding(it)) { DragPane(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.height(10.dp)) DropPane(dragText, updateDragText, dragImage, updateDragImage, Modifier.weight(1f)) diff --git a/DualView/build.gradle b/DualView/build.gradle index 93c431a..6ebc083 100644 --- a/DualView/build.gradle +++ b/DualView/build.gradle @@ -36,6 +36,7 @@ android { excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] } } + namespace 'com.microsoft.device.display.samples.dualview' } dependencies { @@ -63,4 +64,6 @@ dependencies { androidTestImplementation testDependencies.composeJunit androidTestImplementation testDependencies.uiAutomator androidTestImplementation microsoftDependencies.composeTesting + + debugImplementation testDependencies.composeUITestManifest } \ No newline at end of file diff --git a/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/MapImageTest.kt b/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/MapImageTest.kt index 491c57a..e835d23 100644 --- a/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/MapImageTest.kt +++ b/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/MapImageTest.kt @@ -17,11 +17,8 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToIndex import androidx.window.testing.layout.WindowLayoutInfoPublisherRule import com.microsoft.device.display.samples.dualview.models.restaurants -import com.microsoft.device.display.samples.dualview.ui.theme.DualViewAppTheme -import com.microsoft.device.display.samples.dualview.ui.view.DualViewApp import com.microsoft.device.dualscreen.testing.compose.getString import com.microsoft.device.dualscreen.testing.compose.simulateHorizontalFoldingFeature -import com.microsoft.device.dualscreen.windowstate.WindowState import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain @@ -47,12 +44,6 @@ class MapImageTest { @ExperimentalTestApi @Test fun app_horizontalFold_mapUpdatesAfterRestaurantClick() { - composeTestRule.setContent { - DualViewAppTheme { - DualViewApp(WindowState(hasFold = true, foldIsHorizontal = true, foldIsSeparating = true)) - } - } - // Simulate horizontal foldFeature publisherRule.simulateHorizontalFoldingFeature(composeTestRule) diff --git a/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/RestaurantListTest.kt b/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/RestaurantListTest.kt index 702d901..120cdeb 100644 --- a/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/RestaurantListTest.kt +++ b/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/RestaurantListTest.kt @@ -18,12 +18,11 @@ import androidx.compose.ui.test.assert import androidx.compose.ui.test.hasAnyChild import androidx.compose.ui.test.hasScrollToIndexAction import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToIndex import androidx.compose.ui.text.TextStyle -import androidx.window.testing.layout.WindowLayoutInfoPublisherRule import com.microsoft.device.display.samples.dualview.models.restaurants import com.microsoft.device.display.samples.dualview.ui.theme.DualViewAppTheme import com.microsoft.device.display.samples.dualview.ui.theme.selectedBody1 @@ -35,20 +34,10 @@ import com.microsoft.device.dualscreen.testing.compose.getString import com.microsoft.device.dualscreen.twopanelayout.twopanelayout.TwoPaneScopeTest import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain -import org.junit.rules.TestRule class RestaurantListTest { - private val composeTestRule = createAndroidComposeRule() - private val publisherRule = WindowLayoutInfoPublisherRule() - @get: Rule - val testRule: TestRule - - init { - testRule = RuleChain.outerRule(publisherRule).around(composeTestRule) - RuleChain.outerRule(composeTestRule) - } + val composeTestRule = createComposeRule() /** * Tests that clicking on each restaurant list item updates the item's text style @@ -91,7 +80,7 @@ class RestaurantListTest { // Assert that the details for the first restaurant are horizontally scrollable composeTestRule.onNode( - matcher = hasAnyChild(hasText(composeTestRule.getString(R.string.pestle_rock))) + matcher = hasAnyChild(hasText(getString(R.string.pestle_rock))) and SemanticsMatcher.keyIsDefined(SemanticsProperties.HorizontalScrollAxisRange), useUnmergedTree = true ).assertExists() @@ -108,7 +97,7 @@ class RestaurantListTest { // Assert that the details for the first restaurant are not horizontally scrollable composeTestRule.onNode( - matcher = hasAnyChild(hasText(composeTestRule.getString(R.string.pestle_rock))) + matcher = hasAnyChild(hasText(getString(R.string.pestle_rock))) and SemanticsMatcher.keyIsDefined(SemanticsProperties.HorizontalScrollAxisRange), useUnmergedTree = true ).assertDoesNotExist() @@ -127,7 +116,7 @@ class RestaurantListTest { composeTestRule.onNode(hasScrollToIndexAction()).performScrollToIndex(index) // Get semantics node for current restaurant - val currentRestaurantTitle = composeTestRule.getString(restaurants[index].title) + val currentRestaurantTitle = getString(restaurants[index].title) val currentRestaurant = composeTestRule.onNodeWithContentDescription(currentRestaurantTitle) // Check that the unselected text style is being used diff --git a/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/TopAppBarTest.kt b/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/TopAppBarTest.kt index 645b423..5707174 100644 --- a/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/TopAppBarTest.kt +++ b/DualView/src/androidTest/java/com/microsoft/device/display/samples/dualview/TopAppBarTest.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasParent import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick @@ -25,7 +25,7 @@ import org.junit.Test class TopAppBarTest { @get: Rule - val composeTestRule = createAndroidComposeRule() + val composeTestRule = createComposeRule() /** * Tests that the map icon shows in the restaurant top bar when single screen @@ -38,7 +38,7 @@ class TopAppBarTest { } } - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_map)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_map)) .assertIsDisplayed() } @@ -53,7 +53,7 @@ class TopAppBarTest { } } - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_map)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_map)) .assertDoesNotExist() } @@ -68,7 +68,7 @@ class TopAppBarTest { } } - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_rest)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_rest)) .assertIsDisplayed() } @@ -83,7 +83,7 @@ class TopAppBarTest { } } - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_rest)) + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_rest)) .assertDoesNotExist() } @@ -99,8 +99,8 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.restaurant_top_bar))) - and hasText(composeTestRule.getString(R.string.app_name)) + hasParent(hasTestTag(getString(R.string.restaurant_top_bar))) + and hasText(getString(R.string.app_name)) ).assertExists() } @@ -116,8 +116,8 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.restaurant_top_bar))) - and hasText(composeTestRule.getString(R.string.app_name)) + hasParent(hasTestTag(getString(R.string.restaurant_top_bar))) + and hasText(getString(R.string.app_name)) ).assertExists() } @@ -133,8 +133,8 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.map_top_bar))) - and hasText(composeTestRule.getString(R.string.app_name)) + hasParent(hasTestTag(getString(R.string.map_top_bar))) + and hasText(getString(R.string.app_name)) ).assertExists() } @@ -150,7 +150,7 @@ class TopAppBarTest { } composeTestRule.onNode( - hasParent(hasTestTag(composeTestRule.getString(R.string.map_top_bar))) + hasParent(hasTestTag(getString(R.string.map_top_bar))) and hasText("") ).assertExists() } @@ -167,18 +167,18 @@ class TopAppBarTest { } // Assert restaurant view is shown first - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.restaurant_top_bar)).assertExists() + composeTestRule.onNodeWithTag(getString(R.string.restaurant_top_bar)).assertExists() // Click map icon to switch views - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_map)).performClick() + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_map)).performClick() // Assert map view is now shown - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.map_top_bar)).assertExists() + composeTestRule.onNodeWithTag(getString(R.string.map_top_bar)).assertExists() // Click restaurant icon to switch views - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.switch_to_rest)).performClick() + composeTestRule.onNodeWithContentDescription(getString(R.string.switch_to_rest)).performClick() // Assert restaurant view is shown again - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.restaurant_top_bar)).assertExists() + composeTestRule.onNodeWithTag(getString(R.string.restaurant_top_bar)).assertExists() } } diff --git a/DualView/src/main/AndroidManifest.xml b/DualView/src/main/AndroidManifest.xml index 502f114..40a06a0 100644 --- a/DualView/src/main/AndroidManifest.xml +++ b/DualView/src/main/AndroidManifest.xml @@ -5,8 +5,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> nonSelection) { selectedMapId = restaurants[selectedIndex].mapImageResourceId selectedTitleId = restaurants[selectedIndex].description } + Box( modifier = Modifier + .padding(paddingValues) .clipToBounds() .testTag( stringResource(R.string.map_image) diff --git a/DualView/src/main/java/com/microsoft/device/display/samples/dualview/ui/view/RestaurantView.kt b/DualView/src/main/java/com/microsoft/device/display/samples/dualview/ui/view/RestaurantView.kt index e15bbb4..8fcb497 100644 --- a/DualView/src/main/java/com/microsoft/device/display/samples/dualview/ui/view/RestaurantView.kt +++ b/DualView/src/main/java/com/microsoft/device/display/samples/dualview/ui/view/RestaurantView.kt @@ -69,17 +69,15 @@ fun TwoPaneScope.RestaurantViewWithTopBar( Scaffold( topBar = { RestaurantTopBar() } ) { - RestaurantView(viewWidth, selectedIndex, updateSelectedIndex) + RestaurantView(viewWidth, selectedIndex, updateSelectedIndex, it) } } @Composable fun TwoPaneScope.RestaurantTopBar() { - val twoPaneScope = this - TopAppBar( modifier = Modifier.testTag(stringResource(R.string.restaurant_top_bar)), - actions = { if (twoPaneScope.isSinglePane) twoPaneScope.RestaurantActionButton() }, + actions = { if (this@RestaurantTopBar.isSinglePane) this@RestaurantTopBar.RestaurantActionButton() }, title = { Text( text = stringResource(R.string.app_name), @@ -106,18 +104,23 @@ private fun TwoPaneScope.RestaurantActionButton() { } @Composable -fun TwoPaneScope.RestaurantView(viewWidth: Int, selectedIndex: Int, updateSelectedIndex: (Int) -> Unit) { - val twoPaneScope = this - +fun TwoPaneScope.RestaurantView( + viewWidth: Int, + selectedIndex: Int, + updateSelectedIndex: (Int) -> Unit, + paddingValues: PaddingValues +) { Column( - modifier = Modifier.padding(top = outlinePadding.dp, start = outlinePadding.dp, end = outlinePadding.dp), + modifier = Modifier + .padding(paddingValues) + .padding(top = outlinePadding.dp, start = outlinePadding.dp, end = outlinePadding.dp), verticalArrangement = Arrangement.spacedBy(15.dp) ) { Text( text = stringResource(R.string.list_title), style = typography.subtitle1 ) - twoPaneScope.RestaurantListView(viewWidth, selectedIndex, updateSelectedIndex) + this@RestaurantView.RestaurantListView(viewWidth, selectedIndex, updateSelectedIndex) } } diff --git a/DyAdd/build.gradle b/DyAdd/build.gradle index d275c82..cfdb435 100644 --- a/DyAdd/build.gradle +++ b/DyAdd/build.gradle @@ -46,6 +46,7 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.microsoft.device.display.samples.dyadd' } dependencies { diff --git a/DyAdd/src/main/AndroidManifest.xml b/DyAdd/src/main/AndroidManifest.xml index af99efd..d6dfb92 100644 --- a/DyAdd/src/main/AndroidManifest.xml +++ b/DyAdd/src/main/AndroidManifest.xml @@ -5,8 +5,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> if (index == list.size - 1) { @@ -75,18 +61,11 @@ fun NumericGrid(modifier: Modifier = Modifier) { ) } -@OptIn(ExperimentalFoundationApi::class) @Composable fun BasicCalculationGrid() { LazyVerticalGrid( - cells = GridCells.Fixed(2), - // content padding - contentPadding = PaddingValues( - start = 6.dp, - top = 16.dp, - end = 12.dp, - bottom = 16.dp - ), + columns = GridCells.Fixed(2), + contentPadding = PaddingValues(start = 6.dp, top = 16.dp, end = 12.dp, bottom = 16.dp), content = { item { EquationButton(eq = Equation.DIV, color = MaterialTheme.colors.secondary) } item { ActionButton(ac = Action.CLR, color = MaterialTheme.colors.secondaryVariant) } diff --git a/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/Buttons.kt b/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/Buttons.kt index dc0a86e..b5d4d6c 100644 --- a/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/Buttons.kt +++ b/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/Buttons.kt @@ -68,10 +68,10 @@ fun ButtonLayout( onClick: () -> Unit ) { Button( - onClick = { onClick() }, modifier = Modifier .padding(5.dp) .size(90.dp), + onClick = { onClick() }, colors = ButtonDefaults.buttonColors(backgroundColor = color), shape = MaterialTheme.shapes.large ) { @@ -88,6 +88,7 @@ fun ButtonText(content: String) { val button = MaterialTheme.typography.button var textStyle by remember { mutableStateOf(button) } var readyToDraw by remember { mutableStateOf(false) } + Text( text = content, style = textStyle, diff --git a/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/History.kt b/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/History.kt index 0d676ef..b6eeca4 100644 --- a/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/History.kt +++ b/DyAdd/src/main/java/com/microsoft/device/display/samples/dyadd/ui/pages/components/History.kt @@ -29,14 +29,9 @@ import kotlinx.coroutines.launch fun History(addRecordToTop: Boolean, modifier: Modifier = Modifier) { val listState = rememberLazyListState() val coroutineScope = rememberCoroutineScope() + LazyColumn( - modifier = modifier - .padding( - start = 20.dp, - end = 20.dp, - bottom = 5.dp, - top = 15.dp - ), + modifier = modifier.padding(start = 20.dp, end = 20.dp, bottom = 5.dp, top = 15.dp), state = listState, ) { items(historyModel.records.size) { index -> diff --git a/ExtendedCanvas/build.gradle b/ExtendedCanvas/build.gradle index cb431cf..6271f6e 100644 --- a/ExtendedCanvas/build.gradle +++ b/ExtendedCanvas/build.gradle @@ -29,10 +29,14 @@ android { kotlinCompilerExtensionVersion composeVersion } packagingOptions { - exclude "META-INF/licenses/**" - exclude "META-INF/AL2.0" - exclude "META-INF/LGPL2.1" + jniLibs { + excludes += ['META-INF/licenses/**'] + } + resources { + excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] + } } + namespace 'com.microsoft.device.display.samples.extendedcanvas' } dependencies { diff --git a/ExtendedCanvas/src/androidTest/java/com/microsoft/device/display/samples/extendedcanvas/ExtendedCanvasTest.kt b/ExtendedCanvas/src/androidTest/java/com/microsoft/device/display/samples/extendedcanvas/ExtendedCanvasTest.kt index 09c8bfa..a9b3452 100644 --- a/ExtendedCanvas/src/androidTest/java/com/microsoft/device/display/samples/extendedcanvas/ExtendedCanvasTest.kt +++ b/ExtendedCanvas/src/androidTest/java/com/microsoft/device/display/samples/extendedcanvas/ExtendedCanvasTest.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeLeft -import com.microsoft.device.display.samples.extendedcanvas.ui.ExtendedCanvasAppsTheme import com.microsoft.device.dualscreen.testing.compose.getString import org.junit.Rule import org.junit.Test @@ -29,12 +28,6 @@ class ExtendedCanvasTest { */ @Test fun topBar_shows() { - composeTestRule.setContent { - ExtendedCanvasAppsTheme { - ExtendedCanvasApp() - } - } - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.top_bar)) .assertIsDisplayed() } @@ -44,12 +37,6 @@ class ExtendedCanvasTest { */ @Test fun mapView_testImageDrags() { - composeTestRule.setContent { - ExtendedCanvasAppsTheme { - ExtendedCanvasApp() - } - } - // Get node for the map image val mapImageNode = composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.map_image)) diff --git a/ExtendedCanvas/src/main/AndroidManifest.xml b/ExtendedCanvas/src/main/AndroidManifest.xml index c68026a..4fc4f01 100644 --- a/ExtendedCanvas/src/main/AndroidManifest.xml +++ b/ExtendedCanvas/src/main/AndroidManifest.xml @@ -4,8 +4,7 @@ ~ Licensed under the MIT License. --> - + () + val composeTestRule = createComposeRule() /** * Tests that app title shows in the top bar of the list view @@ -34,9 +34,7 @@ class TopAppBarTest { } } - composeTestRule.onNode( - hasText(composeTestRule.getString(R.string.app_name)) - ).assertExists() + composeTestRule.onNode(hasText(getString(R.string.app_name))).assertExists() } /** @@ -51,9 +49,7 @@ class TopAppBarTest { } // Assert the back button is not shown - composeTestRule.onNodeWithContentDescription( - composeTestRule.getString(R.string.back_to_list) - ).assertDoesNotExist() + composeTestRule.onNodeWithContentDescription(getString(R.string.back_to_list)).assertDoesNotExist() } /** @@ -68,9 +64,7 @@ class TopAppBarTest { } // Assert the back button is shown - composeTestRule.onNodeWithContentDescription( - composeTestRule.getString(R.string.back_to_list) - ).assertExists() + composeTestRule.onNodeWithContentDescription(getString(R.string.back_to_list)).assertExists() } /** @@ -86,44 +80,29 @@ class TopAppBarTest { } // Assert the list view is shown first - composeTestRule.onNodeWithTag( - composeTestRule.getString(R.string.list_view) - ).assertExists() + composeTestRule.onNodeWithTag(getString(R.string.list_view)).assertExists() // Assert the back button is not shown yet - composeTestRule.onNodeWithContentDescription( - composeTestRule.getString(R.string.back_to_list) - ).assertDoesNotExist() + composeTestRule.onNodeWithContentDescription(getString(R.string.back_to_list)).assertDoesNotExist() // Click the first image from the list val index = 0 - composeTestRule.onNodeWithContentDescription( - index.toString() - ).performClick() + composeTestRule.onNodeWithContentDescription(index.toString()).performClick() // Assert the correct detail image is shown - composeTestRule.onNodeWithContentDescription( - composeTestRule.getString(R.string.image_tag) + index.toString() - ).assertExists() + composeTestRule.onNodeWithContentDescription(getString(R.string.image_tag) + index.toString()) + .assertExists() // Assert the back button is now shown - composeTestRule.onNodeWithContentDescription( - composeTestRule.getString(R.string.back_to_list) - ).assertExists() + composeTestRule.onNodeWithContentDescription(getString(R.string.back_to_list)).assertExists() // Click the back button to go back to the list view - composeTestRule.onNodeWithContentDescription( - composeTestRule.getString(R.string.back_to_list) - ).performClick() + composeTestRule.onNodeWithContentDescription(getString(R.string.back_to_list)).performClick() // Assert the detail view is not shown - composeTestRule.onNodeWithTag( - composeTestRule.getString(R.string.detail_view) - ).assertDoesNotExist() + composeTestRule.onNodeWithTag(getString(R.string.detail_view)).assertDoesNotExist() // Assert the list view is now shown - composeTestRule.onNodeWithTag( - composeTestRule.getString(R.string.list_view) - ).assertExists() + composeTestRule.onNodeWithTag(getString(R.string.list_view)).assertExists() } } diff --git a/ListDetail/src/main/AndroidManifest.xml b/ListDetail/src/main/AndroidManifest.xml index 608359a..100a1bd 100644 --- a/ListDetail/src/main/AndroidManifest.xml +++ b/ListDetail/src/main/AndroidManifest.xml @@ -4,8 +4,7 @@ ~ Licensed under the MIT License. --> - + Unit) { Scaffold( topBar = { ListViewTopBar() }, - content = { ListView(selectedIndex, updateSelectedIndex) } - ) + ) { + ListView(selectedIndex, updateSelectedIndex, it) + } } @Composable @@ -64,19 +66,14 @@ fun ListViewTopBar() { } @Composable -fun TwoPaneScope.ListView(selectedIndex: Int, updateSelectedIndex: (Int) -> Unit) { +fun TwoPaneScope.ListView(selectedIndex: Int, updateSelectedIndex: (Int) -> Unit, paddingValues: PaddingValues) { val subImageList = images.chunked(3) - val twoPaneScope = this Box( modifier = Modifier .fillMaxSize() - .padding( - top = verticalPadding, - bottom = verticalPadding, - start = horizontalPadding, - end = horizontalPadding - ) + .padding(paddingValues) + .padding(horizontal = horizontalPadding, vertical = verticalPadding) .testTag(stringResource(R.string.list_view)) ) { LazyColumn( @@ -103,7 +100,7 @@ fun TwoPaneScope.ListView(selectedIndex: Int, updateSelectedIndex: (Int) -> Unit selected = (listIndex == selectedIndex), onClick = { updateSelectedIndex(listIndex) - twoPaneScope.navigateToPane2() + this@ListView.navigateToPane2() } ) ) diff --git a/NavigationRail/build.gradle b/NavigationRail/build.gradle index a67129d..2f08a4f 100644 --- a/NavigationRail/build.gradle +++ b/NavigationRail/build.gradle @@ -33,6 +33,7 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.microsoft.device.display.samples.navigationrail' } dependencies { @@ -59,4 +60,6 @@ dependencies { androidTestImplementation testDependencies.composeJunit androidTestImplementation testDependencies.uiAutomator androidTestImplementation microsoftDependencies.composeTesting + + debugImplementation testDependencies.composeUITestManifest } \ No newline at end of file diff --git a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/DetailTest.kt b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/DetailTest.kt index 39b1bbe..1bc9f4f 100644 --- a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/DetailTest.kt +++ b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/DetailTest.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasAnyChild import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText @@ -51,7 +51,7 @@ import org.junit.Test @ExperimentalAnimationApi class DetailTest { @get: Rule - val composeTestRule = createAndroidComposeRule() + val composeTestRule = createComposeRule() /** * Tests that the back button appears in pane 2 in single screen mode @@ -63,7 +63,7 @@ class DetailTest { } // Assert that back button is visible - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.back)).assertIsDisplayed() + composeTestRule.onNodeWithContentDescription(getString(R.string.back)).assertIsDisplayed() } /** @@ -76,7 +76,7 @@ class DetailTest { } // Assert that back button is visible - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.back)).assertIsDisplayed() + composeTestRule.onNodeWithContentDescription(getString(R.string.back)).assertIsDisplayed() } /** @@ -89,7 +89,7 @@ class DetailTest { } // Assert that back button does not exist - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.back)).assertDoesNotExist() + composeTestRule.onNodeWithContentDescription(getString(R.string.back)).assertDoesNotExist() } /** @@ -105,24 +105,24 @@ class DetailTest { } // Assert "plants" title is visible - composeTestRule.onNodeWithText("plants").assertIsDisplayed() + composeTestRule.onNodeWithText(getString(R.string.plants).lowercase()).assertIsDisplayed() // Click on first plant item val firstEntryDescription = - composeTestRule.getString(R.string.image_description, plantList[0].name, plantList[0].id) + getString(R.string.image_description, plantList[0].name, plantList[0].id) composeTestRule.onNodeWithContentDescription(firstEntryDescription).performClick() // Assert that back button is visible - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.back)).assertIsDisplayed() + composeTestRule.onNodeWithContentDescription(getString(R.string.back)).assertIsDisplayed() // Assert that "plants" title is no longer visible - composeTestRule.onNodeWithText("plants").assertDoesNotExist() + composeTestRule.onNodeWithText(getString(R.string.plants).lowercase()).assertDoesNotExist() // Click back button - composeTestRule.onNodeWithContentDescription(composeTestRule.getString(R.string.back)).performClick() + composeTestRule.onNodeWithContentDescription(getString(R.string.back)).performClick() // Assert that "plants" title is visible again - composeTestRule.onNodeWithText("plants").assertIsDisplayed() + composeTestRule.onNodeWithText(getString(R.string.plants).lowercase()).assertIsDisplayed() } /** @@ -135,26 +135,26 @@ class DetailTest { } // Assert drawer is collapsed on start - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.content_drawer)) + composeTestRule.onNodeWithTag(getString(R.string.content_drawer)) .assertDrawerStateEquals(DrawerState.Collapsed) // Swipe content drawer up - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.content_drawer)).performTouchInput { + composeTestRule.onNodeWithTag(getString(R.string.content_drawer)).performTouchInput { val start = this.topCenter val end = Offset(start.x, start.y - 200) swipe(start, end, 200) } // Assert drawer is now expanded - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.content_drawer)) + composeTestRule.onNodeWithTag(getString(R.string.content_drawer)) .assertDrawerStateEquals(DrawerState.Expanded) // Swipe content drawer back down - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.content_drawer)) + composeTestRule.onNodeWithTag(getString(R.string.content_drawer)) .performTouchInput { swipeDown() } // Assert drawer is collapsed again - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.content_drawer)) + composeTestRule.onNodeWithTag(getString(R.string.content_drawer)) .assertDrawerStateEquals(DrawerState.Collapsed) } diff --git a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/GalleryTest.kt b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/GalleryTest.kt index abe359f..e3d5ad9 100644 --- a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/GalleryTest.kt +++ b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/GalleryTest.kt @@ -7,6 +7,7 @@ package com.microsoft.device.display.samples.navigationrail import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -15,7 +16,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.semantics.SemanticsProperties.VerticalScrollAxisRange import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performScrollTo @@ -38,7 +39,7 @@ import org.junit.Test @ExperimentalAnimationApi class GalleryTest { @get: Rule - val composeTestRule = createAndroidComposeRule() + val composeTestRule = createComposeRule() /** * Tests that the content of the gallery view is vertically scrollable @@ -52,15 +53,15 @@ class GalleryTest { galleryList = DataProvider.plantList, currentImageId = id, onImageSelected = { newId -> id = newId }, - horizontalPadding = 10.dp + horizontalPadding = 10.dp, + paddingValues = PaddingValues() ) } } // Assert that last plant is not visible at the start val lastEntry = DataProvider.plantList.last() - val lastEntryDescription = - composeTestRule.getString(R.string.image_description, lastEntry.name, lastEntry.id) + val lastEntryDescription = getString(R.string.image_description, lastEntry.name, lastEntry.id) composeTestRule.onNodeWithContentDescription(lastEntryDescription).assertDoesNotExist() // Assert that gallery has vertical scroll action, then scroll to the end of the gallery @@ -83,10 +84,10 @@ class GalleryTest { } // Assert "plants" title is visible in gallery view - composeTestRule.onNodeWithText("plants").assertIsDisplayed() + composeTestRule.onNodeWithText(getString(R.string.plants).lowercase()).assertIsDisplayed() // Assert that placeholder view isn't shown on start up (only one pane) - val placeholderText = composeTestRule.activity.getString(R.string.placeholder_msg, "plants") + val placeholderText = getString(R.string.placeholder_msg, getString(R.string.plants).lowercase()) composeTestRule.onNodeWithText(placeholderText).assertDoesNotExist() } } diff --git a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/NavComponentTest.kt b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/NavComponentTest.kt index 53455cf..b127314 100644 --- a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/NavComponentTest.kt +++ b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/NavComponentTest.kt @@ -11,7 +11,7 @@ import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasClickAction import androidx.compose.ui.test.hasText -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.compose.ui.unit.ExperimentalUnitApi @@ -29,9 +29,8 @@ import org.junit.Test @ExperimentalMaterialApi @ExperimentalAnimationApi class NavComponentTest { - @get: Rule - val composeTestRule = createAndroidComposeRule() + val composeTestRule = createComposeRule() /** * Tests that clicking each icon in the bottom navigation bar switches the gallery destination @@ -73,8 +72,8 @@ class NavComponentTest { } // Assert that nav rail, not bottom nav, is displayed - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.nav_rail)).assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.bottom_nav)).assertDoesNotExist() + composeTestRule.onNodeWithTag(getString(R.string.nav_rail)).assertIsDisplayed() + composeTestRule.onNodeWithTag(getString(R.string.bottom_nav)).assertDoesNotExist() } /** @@ -89,8 +88,8 @@ class NavComponentTest { } // Assert that nav rail, not bottom nav, is displayed - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.nav_rail)).assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.bottom_nav)).assertDoesNotExist() + composeTestRule.onNodeWithTag(getString(R.string.nav_rail)).assertIsDisplayed() + composeTestRule.onNodeWithTag(getString(R.string.bottom_nav)).assertDoesNotExist() } /** @@ -105,8 +104,8 @@ class NavComponentTest { } // Assert that nav rail, not bottom nav, is displayed - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.nav_rail)).assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.bottom_nav)).assertDoesNotExist() + composeTestRule.onNodeWithTag(getString(R.string.nav_rail)).assertIsDisplayed() + composeTestRule.onNodeWithTag(getString(R.string.bottom_nav)).assertDoesNotExist() } /** @@ -121,8 +120,8 @@ class NavComponentTest { } // Assert that bottom nav, not nav rail, is displayed - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.bottom_nav)).assertIsDisplayed() - composeTestRule.onNodeWithTag(composeTestRule.getString(R.string.nav_rail)).assertDoesNotExist() + composeTestRule.onNodeWithTag(getString(R.string.bottom_nav)).assertIsDisplayed() + composeTestRule.onNodeWithTag(getString(R.string.nav_rail)).assertDoesNotExist() } /** @@ -132,7 +131,7 @@ class NavComponentTest { private fun clickEachNavIcon() { for (destination in GallerySections.values()) { // Click on nav icon - composeTestRule.onNode(hasText(composeTestRule.getString(destination.title)) and hasClickAction()) + composeTestRule.onNode(hasText(getString(destination.title)) and hasClickAction()) .performClick() // Assert that new destination route is shown in the top bar diff --git a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/PaneSynchronizationTest.kt b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/PaneSynchronizationTest.kt index 6bdf5c7..6aba9c8 100644 --- a/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/PaneSynchronizationTest.kt +++ b/NavigationRail/src/androidTest/java/com/microsoft/device/display/samples/navigationrail/PaneSynchronizationTest.kt @@ -24,49 +24,36 @@ import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeUp import androidx.compose.ui.unit.ExperimentalUnitApi -import androidx.window.testing.layout.WindowLayoutInfoPublisherRule import com.microsoft.device.display.samples.navigationrail.models.DataProvider -import com.microsoft.device.display.samples.navigationrail.ui.theme.NavigationRailAppTheme import com.microsoft.device.display.samples.navigationrail.ui.view.GallerySections -import com.microsoft.device.display.samples.navigationrail.ui.view.NavigationRailApp +import com.microsoft.device.dualscreen.testing.compose.foldableRuleChain import com.microsoft.device.dualscreen.testing.compose.getString -import com.microsoft.device.dualscreen.testing.compose.simulateVerticalFoldingFeature -import com.microsoft.device.dualscreen.windowstate.WindowState +import com.microsoft.device.dualscreen.testing.filters.MockFoldingFeature +import com.microsoft.device.dualscreen.testing.rules.FoldableTestRule +import com.microsoft.device.dualscreen.testing.runner.FoldableJUnit4ClassRunner import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain import org.junit.rules.TestRule +import org.junit.runner.RunWith @ExperimentalFoundationApi @ExperimentalUnitApi @ExperimentalMaterialApi @ExperimentalAnimationApi +@RunWith(FoldableJUnit4ClassRunner::class) class PaneSynchronizationTest { private val composeTestRule = createAndroidComposeRule() - private val publisherRule = WindowLayoutInfoPublisherRule() + private val foldableTestRule = FoldableTestRule() - @get: Rule - val testRule: TestRule - - init { - testRule = RuleChain.outerRule(publisherRule).around(composeTestRule) - RuleChain.outerRule(composeTestRule) - } + @get:Rule + val testRule: TestRule = foldableRuleChain(composeTestRule, foldableTestRule) /** * Tests that the correct placeholder views appear when a gallery is first opened and no items are selected */ @Test + @MockFoldingFeature(orientation = MockFoldingFeature.FoldingFeatureOrientation.VERTICAL) fun app_verticalFold_testPlaceholderViewAppearsOnStart() { - composeTestRule.setContent { - NavigationRailAppTheme { - NavigationRailApp(WindowState(hasFold = true, foldIsSeparating = true)) - } - } - - // Simulate a vertical foldFeature - publisherRule.simulateVerticalFoldingFeature(composeTestRule) - for (gallery in GallerySections.values()) { // Click on gallery composeTestRule.onNode(hasText(gallery.route, ignoreCase = true) and hasClickAction()).performClick() @@ -82,16 +69,8 @@ class PaneSynchronizationTest { * for the clicked item when a vertical fold is present */ @Test + @MockFoldingFeature(orientation = MockFoldingFeature.FoldingFeatureOrientation.VERTICAL) fun app_verticalFold_galleryClickUpdatesSelection() { - composeTestRule.setContent { - NavigationRailAppTheme { - NavigationRailApp(WindowState(hasFold = true, foldIsSeparating = true)) - } - } - - // Simulate a vertical foldFeature - publisherRule.simulateVerticalFoldingFeature(composeTestRule) - GallerySections.values().forEachIndexed { i, gallery -> // Click on gallery composeTestRule.onNode(hasText(gallery.route, ignoreCase = true) and hasClickAction()) diff --git a/NavigationRail/src/main/AndroidManifest.xml b/NavigationRail/src/main/AndroidManifest.xml index bd78e6b..f3216ac 100644 --- a/NavigationRail/src/main/AndroidManifest.xml +++ b/NavigationRail/src/main/AndroidManifest.xml @@ -4,8 +4,7 @@ ~ Licensed under the MIT License. --> - + Unit, updateRoute: (String) -> Unit, ) { - val twoPaneNavScope = this - NavigationRail( modifier = Modifier.testTag(stringResource(id = R.string.nav_rail)), backgroundColor = MaterialTheme.colors.primary, ) { Spacer(Modifier.height(NAV_RAIL_TOP_SPACING)) val currentDestination = - if (twoPaneNavScope.isSinglePane) + if (this@GalleryNavRail.isSinglePane) navController.currentBackStackEntryAsState().value?.destination?.route else - twoPaneNavScope.currentPane1Destination + this@GalleryNavRail.currentPane1Destination galleries.forEach { gallery -> NavRailItemWithSelector( icon = { @@ -57,7 +55,7 @@ fun TwoPaneNavScope.GalleryNavRail( label = { NavItemLabel(stringResource(gallery.title)) }, selected = isNavItemSelected(currentDestination, gallery.route), onClick = { - twoPaneNavScope.navItemOnClick(navController, gallery.route, updateImageId, updateRoute) + this@GalleryNavRail.navItemOnClick(navController, gallery.route, updateImageId, updateRoute) }, selectedContentColor = MaterialTheme.colors.primary, unselectedContentColor = MaterialTheme.colors.onPrimary @@ -74,17 +72,15 @@ fun TwoPaneNavScope.GalleryBottomNav( updateImageId: (Int?) -> Unit, updateRoute: (String) -> Unit, ) { - val twoPaneNavScope = this - BottomNavigation( modifier = Modifier.testTag(stringResource(id = R.string.bottom_nav)), backgroundColor = MaterialTheme.colors.primary, ) { val currentDestination = - if (twoPaneNavScope.isSinglePane) + if (this@GalleryBottomNav.isSinglePane) navController.currentBackStackEntryAsState().value?.destination?.route else - twoPaneNavScope.currentPane1Destination + this@GalleryBottomNav.currentPane1Destination galleries.forEach { gallery -> BottomNavItemWithSelector( icon = { @@ -93,7 +89,7 @@ fun TwoPaneNavScope.GalleryBottomNav( label = { NavItemLabel(stringResource(gallery.title)) }, selected = isNavItemSelected(currentDestination, gallery.route), onClick = { - twoPaneNavScope.navItemOnClick(navController, gallery.route, updateImageId, updateRoute) + this@GalleryBottomNav.navItemOnClick(navController, gallery.route, updateImageId, updateRoute) }, selectedContentColor = MaterialTheme.colors.primary, unselectedContentColor = MaterialTheme.colors.onPrimary diff --git a/NavigationRail/src/main/java/com/microsoft/device/display/samples/navigationrail/ui/view/GalleryView.kt b/NavigationRail/src/main/java/com/microsoft/device/display/samples/navigationrail/ui/view/GalleryView.kt index 88e130b..0ab2ed2 100644 --- a/NavigationRail/src/main/java/com/microsoft/device/display/samples/navigationrail/ui/view/GalleryView.kt +++ b/NavigationRail/src/main/java/com/microsoft/device/display/samples/navigationrail/ui/view/GalleryView.kt @@ -15,9 +15,8 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.GridCells -import androidx.compose.foundation.lazy.LazyVerticalGrid -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.selection.selectable import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold @@ -80,8 +79,6 @@ fun TwoPaneNavScope.GalleryViewWithTopBar( activity?.finish() } - val twoPaneNavScope = this - // Use navigation rail when dual screen (more space), otherwise use bottom navigation Scaffold( bottomBar = { @@ -91,15 +88,16 @@ fun TwoPaneNavScope.GalleryViewWithTopBar( ) { paddingValues -> Row(Modifier.padding(paddingValues)) { if (isDualScreen) - twoPaneNavScope.GalleryNavRail(navController, navDestinations, updateImageId, updateRoute) + this@GalleryViewWithTopBar.GalleryNavRail(navController, navDestinations, updateImageId, updateRoute) Scaffold( topBar = { GalleryTopBar(section.route, horizontalPadding) } - ) { + ) { paddingValues -> GalleryView( galleryList = section.list, currentImageId = imageId, - onImageSelected = { id -> twoPaneNavScope.onImageSelected(id, updateImageId, navController) }, + onImageSelected = { id -> this@GalleryViewWithTopBar.onImageSelected(id, updateImageId, navController) }, horizontalPadding = horizontalPadding, + paddingValues = paddingValues ) } } @@ -137,10 +135,12 @@ fun GalleryView( galleryList: List, currentImageId: Int?, onImageSelected: (Int) -> Unit, - horizontalPadding: Dp + horizontalPadding: Dp, + paddingValues: PaddingValues ) { LazyVerticalGrid( - cells = GridCells.Fixed(count = NUM_COLUMNS), + modifier = Modifier.padding(paddingValues), + columns = GridCells.Fixed(count = NUM_COLUMNS), verticalArrangement = Arrangement.spacedBy(GALLERY_SPACING, Alignment.Top), horizontalArrangement = Arrangement.spacedBy(GALLERY_SPACING, Alignment.CenterHorizontally), contentPadding = PaddingValues( @@ -149,8 +149,10 @@ fun GalleryView( bottom = GALLERY_SPACING ) ) { - items(galleryList) { item -> - GalleryItem(item, currentImageId, onImageSelected) + galleryList.forEach { item -> + item { + GalleryItem(item, currentImageId, onImageSelected) + } } } } diff --git a/README.md b/README.md index a932cbd..2ea29a3 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ products: description: "Samples showing how to use Jetpack Compose to achieve dual-screen user interface patterns." urlFragment: all --- -![build-test-check](https://github.com/microsoft/surface-duo-compose-samples/actions/workflows/build_test_check.yml/badge.svg) ![Compose Version](https://img.shields.io/badge/Jetpack%20Compose-1.1.1-brightgreen) +![build-test-check](https://github.com/microsoft/surface-duo-compose-samples/actions/workflows/build_test_check.yml/badge.svg) ![Compose Version](https://img.shields.io/badge/Jetpack%20Compose-1.2.0-brightgreen) # Surface Duo Jetpack Compose Samples @@ -26,7 +26,7 @@ Please check out our page on [Jetpack Compose for Microsoft Surface Duo](https:/ ## Prerequisites -- Jetpack Compose version: `1.1.1` +- Jetpack Compose version: `1.2.0` - Jetpack WindowManager version: `1.0.0` @@ -70,17 +70,17 @@ The samples are built with Microsoft Compose libraries, [TwoPaneLayout](https:// | Blog post | Video | |---|---| -| [Source Editor & Diary samples for Jetpack Compose](https://devblogs.microsoft.com/surface-duo/jetpack-compose-source-editor-diary-samples/) | [Twitch #79: More new Jetpack Compose samples] (https://www.youtube.com/watch?v=eO0D3MHvKLA) -| [Video+Chat and Calculator samples for Jetpack Compose](https://devblogs.microsoft.com/surface-duo/jetpack-compose-video-calculator-samples/) | [Twitch #77: New Jetpack Compose samples](https://www.twitch.tv/videos/1519558235) | -| [Jetpack Compose TwoPaneLayout update](https://devblogs.microsoft.com/surface-duo/jetpack-compose-foldable-twopanelayout/) | [Twitch #76: Jetpack Compose TwoPaneLayout update](https://www.youtube.com/watch?v=cI73qh_mTOo)| +| [Source Editor & Diary samples for Jetpack Compose](https://devblogs.microsoft.com/surface-duo/jetpack-compose-source-editor-diary-samples/) | [Twitch #79: More new Jetpack Compose samples](https://www.youtube.com/watch?v=eO0D3MHvKLA) | +| [Video+Chat and Calculator samples for Jetpack Compose](https://devblogs.microsoft.com/surface-duo/jetpack-compose-video-calculator-samples/) | [Twitch #77: New Jetpack Compose samples](https://www.youtube.com/watch?v=b4WG3moVriA) | +| [Jetpack Compose TwoPaneLayout update](https://devblogs.microsoft.com/surface-duo/jetpack-compose-foldable-twopanelayout/) | [Twitch #76: Jetpack Compose TwoPaneLayout update](https://www.youtube.com/watch?v=cI73qh_mTOo) | | [Write foldable tests quickly with Test Kit](https://devblogs.microsoft.com/surface-duo/foldable-ui-test-kit/) | [Twitch #63: Test Kit for foldable apps](https://www.youtube.com/watch?v=3I0qU5SeUBM) | | [Jetpack Compose UI testing](https://devblogs.microsoft.com/surface-duo/jetpack-compose-ui-test/) | [Twitch #59: Jetpack Compose testing](https://www.youtube.com/watch?v=Q3lDz7PjO7U) | | [Jetpack Compose WindowState preview](https://devblogs.microsoft.com/surface-duo/jetpack-compose-windowstate-preview/) | [Twitch #53: Jetpack Compose WindowState for foldable devices](https://www.youtube.com/watch?v=qOIliow-uS4) | | [Get started with Jetpack Compose](https://devblogs.microsoft.com/surface-duo/get-started-with-jetpack-compose/) | [Twitch #44: Get started with Jetpack Compose](https://www.youtube.com/watch?v=ijXDWDtdiIE) | -| [Jetpack Compose Navigation Rail](https://devblogs.microsoft.com/surface-duo/jetpack-compose-navigation-rail/) | [Twitch #42: Jetpack Compose Navigation Rail](https://www.youtube.com/watch?v=pdoIyOU7Suk) +| [Jetpack Compose Navigation Rail](https://devblogs.microsoft.com/surface-duo/jetpack-compose-navigation-rail/) | [Twitch #42: Jetpack Compose Navigation Rail](https://www.youtube.com/watch?v=pdoIyOU7Suk) | | [New TwoPaneLayout Compose library preview](https://devblogs.microsoft.com/surface-duo/jetpack-compose-twopanelayout-preview/) | [Twitch #25: TwoPaneLayout preview for Jetpack Compose](https://www.youtube.com/watch?v=Q66bR2jKdrg) | | [Jetpack Compose foldable and dual-screen development](https://devblogs.microsoft.com/surface-duo/jetpack-compose-foldable-samples) | [Twitch #9: Jetpack Compose samples](https://www.youtube.com/watch?v=m8bMjFhBbN8) | -| [Jetpack Compose on Microsoft Surface Duo](https://devblogs.microsoft.com/surface-duo/jetpack-compose-dual-screen-sample/) | N/A| +| [Jetpack Compose on Microsoft Surface Duo](https://devblogs.microsoft.com/surface-duo/jetpack-compose-dual-screen-sample/) | N/A | ## Related links diff --git a/SourceEditorCompose/build.gradle b/SourceEditorCompose/build.gradle index 84a656b..c782ce9 100644 --- a/SourceEditorCompose/build.gradle +++ b/SourceEditorCompose/build.gradle @@ -42,6 +42,7 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.microsoft.device.display.samples.sourceeditorcompose' } dependencies { diff --git a/SourceEditorCompose/src/main/AndroidManifest.xml b/SourceEditorCompose/src/main/AndroidManifest.xml index 67b187a..4a61256 100644 --- a/SourceEditorCompose/src/main/AndroidManifest.xml +++ b/SourceEditorCompose/src/main/AndroidManifest.xml @@ -7,8 +7,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> Unit) { - val twoPaneScope = this Scaffold( - topBar = { - TopAppBar( - title = { - Text(text = stringResource(R.string.app_name)) - }, - backgroundColor = MaterialTheme.colors.primaryVariant, - contentColor = Color.White, - actions = { - if (twoPaneScope.isSinglePane) { - IconButton(onClick = { twoPaneScope.navigateToPane2() }) { - Icon( - imageVector = Icons.Filled.PlayArrow, - contentDescription = stringResource(R.string.show_html) - ) - } - } - } - ) - } + topBar = { EditorTopBar() } ) { TextField( value = text, onValueChange = { newText -> updateText(newText) }, - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxSize() + .padding(it) ) } } + +@Composable +fun TwoPaneScope.EditorTopBar() { + TopAppBar( + title = { Text(text = stringResource(R.string.app_name)) }, + backgroundColor = MaterialTheme.colors.primaryVariant, + contentColor = Color.White, + actions = { + if (this@EditorTopBar.isSinglePane) { + IconButton(onClick = { this@EditorTopBar.navigateToPane2() }) { + Icon( + imageVector = Icons.Filled.PlayArrow, + contentDescription = stringResource(R.string.show_html) + ) + } + } + } + ) +} diff --git a/SourceEditorCompose/src/main/java/com/microsoft/device/display/samples/sourceeditorcompose/ui/pages/PreviewPage.kt b/SourceEditorCompose/src/main/java/com/microsoft/device/display/samples/sourceeditorcompose/ui/pages/PreviewPage.kt index 9dc1f53..7d0d4f3 100644 --- a/SourceEditorCompose/src/main/java/com/microsoft/device/display/samples/sourceeditorcompose/ui/pages/PreviewPage.kt +++ b/SourceEditorCompose/src/main/java/com/microsoft/device/display/samples/sourceeditorcompose/ui/pages/PreviewPage.kt @@ -10,6 +10,7 @@ package com.microsoft.device.display.samples.sourceeditorcompose.ui.pages import android.view.ViewGroup import android.webkit.WebView import android.webkit.WebViewClient +import androidx.compose.foundation.layout.padding import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme @@ -19,6 +20,7 @@ import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Edit import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.viewinterop.AndroidView @@ -30,31 +32,11 @@ import com.microsoft.device.dualscreen.twopanelayout.TwoPaneScope */ @Composable fun TwoPaneScope.PreviewPage(text: String) { - val twoPaneScope = this Scaffold( - topBar = { - TopAppBar( - title = { - if (isSinglePane) { - Text(text = stringResource(id = R.string.app_name)) - } - }, - contentColor = Color.White, - backgroundColor = MaterialTheme.colors.primaryVariant, - actions = { - if (twoPaneScope.isSinglePane) { - IconButton(onClick = { twoPaneScope.navigateToPane1() }) { - Icon( - imageVector = Icons.Filled.Edit, - contentDescription = stringResource(R.string.show_source) - ) - } - } - } - ) - } - ) { + topBar = { PreviewTopBar() } + ) { paddingValues -> AndroidView( + modifier = Modifier.padding(paddingValues), factory = { WebView(it).apply { layoutParams = ViewGroup.LayoutParams( @@ -71,3 +53,25 @@ fun TwoPaneScope.PreviewPage(text: String) { ) } } + +@Composable +fun TwoPaneScope.PreviewTopBar() { + TopAppBar( + title = { + if (isSinglePane) + Text(text = stringResource(id = R.string.app_name)) + }, + contentColor = Color.White, + backgroundColor = MaterialTheme.colors.primaryVariant, + actions = { + if (this@PreviewTopBar.isSinglePane) { + IconButton(onClick = { this@PreviewTopBar.navigateToPane1() }) { + Icon( + imageVector = Icons.Filled.Edit, + contentDescription = stringResource(R.string.show_source) + ) + } + } + } + ) +} diff --git a/TwoPage/build.gradle b/TwoPage/build.gradle index 01630fe..6552ef6 100644 --- a/TwoPage/build.gradle +++ b/TwoPage/build.gradle @@ -38,6 +38,7 @@ android { excludes += ['META-INF/licenses/**', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] } } + namespace 'com.microsoft.device.display.samples.twopage' } dependencies { @@ -63,4 +64,6 @@ dependencies { androidTestImplementation testDependencies.composeJunit androidTestImplementation testDependencies.uiAutomator androidTestImplementation microsoftDependencies.composeTesting + + debugImplementation testDependencies.composeUITestManifest } \ No newline at end of file diff --git a/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageContentTest.kt b/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageContentTest.kt index c24e3a3..5ac9438 100644 --- a/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageContentTest.kt +++ b/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageContentTest.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.hasScrollAction -import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.text.font.FontWeight @@ -28,7 +28,7 @@ import org.junit.Test class PageContentTest { @get: Rule - val composeTestRule = createAndroidComposeRule() + val composeTestRule = createComposeRule() /** * Tests that page content is scrollable @@ -42,14 +42,14 @@ class PageContentTest { } // Assert last text element in not visible - composeTestRule.onNodeWithText(composeTestRule.getString(R.string.article_title)).assertIsNotDisplayed() + composeTestRule.onNodeWithText(getString(R.string.article_title)).assertIsNotDisplayed() // Assert page has scroll action composeTestRule.onNode(hasScrollAction()).assertExists() // Scroll to end of page content and assert last element is now visible - composeTestRule.onNodeWithText(composeTestRule.getString(R.string.article_title)).performScrollTo() - composeTestRule.onNodeWithText(composeTestRule.getString(R.string.article_title)).assertIsDisplayed() + composeTestRule.onNodeWithText(getString(R.string.article_title)).performScrollTo() + composeTestRule.onNodeWithText(getString(R.string.article_title)).assertIsDisplayed() } /** @@ -69,7 +69,7 @@ class PageContentTest { composeTestRule.onNodeWithText(pageNumberText).assertIsDisplayed() // Scroll to end of page content and assert that page number is still visible - composeTestRule.onNodeWithText(composeTestRule.getString(R.string.article_title)).performScrollTo() + composeTestRule.onNodeWithText(getString(R.string.article_title)).performScrollTo() composeTestRule.onNodeWithText(pageNumberText).assertIsDisplayed() } diff --git a/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageSwipeTest.kt b/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageSwipeTest.kt index 1cf0ed2..66a7919 100644 --- a/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageSwipeTest.kt +++ b/TwoPage/src/androidTest/java/com/microsoft/device/display/samples/twopage/PageSwipeTest.kt @@ -5,7 +5,6 @@ package com.microsoft.device.display.samples.twopage -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.junit4.createAndroidComposeRule @@ -14,31 +13,24 @@ import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeLeft import androidx.compose.ui.test.swipeRight -import androidx.compose.ui.unit.dp -import com.microsoft.device.display.samples.twopage.ui.theme.TwoPageAppTheme -import com.microsoft.device.display.samples.twopage.ui.view.TwoPageApp -import com.microsoft.device.display.samples.twopage.ui.view.TwoPageAppContent +import com.microsoft.device.dualscreen.testing.compose.foldableRuleChain import com.microsoft.device.dualscreen.testing.compose.getString -import com.microsoft.device.dualscreen.testing.createWindowLayoutInfoPublisherRule import com.microsoft.device.dualscreen.testing.filters.MockFoldingFeature import com.microsoft.device.dualscreen.testing.filters.SingleScreenTest -import com.microsoft.device.dualscreen.windowstate.WindowState +import com.microsoft.device.dualscreen.testing.rules.FoldableTestRule +import com.microsoft.device.dualscreen.testing.runner.FoldableJUnit4ClassRunner import org.junit.Rule import org.junit.Test -import org.junit.rules.RuleChain import org.junit.rules.TestRule +import org.junit.runner.RunWith +@RunWith(FoldableJUnit4ClassRunner::class) class PageSwipeTest { private val composeTestRule = createAndroidComposeRule() - private val publisherRule = createWindowLayoutInfoPublisherRule() + private val foldableTestRule = FoldableTestRule() - @get: Rule - val testRule: TestRule - - init { - testRule = RuleChain.outerRule(publisherRule).around(composeTestRule) - RuleChain.outerRule(composeTestRule) - } + @get:Rule + val testRule: TestRule = foldableRuleChain(composeTestRule, foldableTestRule) /** * Tests that the pages swipe only between 1 and 4 in single screen mode @@ -46,12 +38,6 @@ class PageSwipeTest { @Test @SingleScreenTest fun app_singlescreen_pagesSwipeWithinLimits() { - composeTestRule.setContent { - TwoPageAppTheme { - TwoPageAppContent(pane1WidthDp = 0.dp, pane2WidthDp = 0.dp, isDualScreen = false, foldSizeDp = 0.dp) - } - } - swipeOnePageAtATime() } @@ -61,12 +47,6 @@ class PageSwipeTest { @Test @MockFoldingFeature(orientation = MockFoldingFeature.FoldingFeatureOrientation.HORIZONTAL) fun app_horizontalFold_pagesSwipeWithinLimits() { - composeTestRule.setContent { - TwoPageAppTheme { - TwoPageApp(WindowState(hasFold = true, foldIsHorizontal = true, foldIsSeparating = true)) - } - } - swipeOnePageAtATime() } @@ -119,19 +99,6 @@ class PageSwipeTest { @Test @MockFoldingFeature(orientation = MockFoldingFeature.FoldingFeatureOrientation.VERTICAL) fun app_verticalFold_pagesSwipeWithinLimits() { - composeTestRule.setContent { - val pageWidth = LocalConfiguration.current.screenWidthDp / 2 - - TwoPageAppTheme { - TwoPageAppContent( - pane1WidthDp = pageWidth.dp, - pane2WidthDp = pageWidth.dp, - isDualScreen = true, - foldSizeDp = 0.dp - ) - } - } - val pageTags = listOf(R.string.page1_tag, R.string.page2_tag, R.string.page3_tag, R.string.page4_tag) // Swipe forwards diff --git a/TwoPage/src/main/AndroidManifest.xml b/TwoPage/src/main/AndroidManifest.xml index 7abec0f..e979273 100644 --- a/TwoPage/src/main/AndroidManifest.xml +++ b/TwoPage/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + Unit>, isDualScreen: Boolean) { +fun PageViews(pages: List<@Composable () -> Unit>, isDualScreen: Boolean, paddingValues: PaddingValues) { val maxPage = (pages.size - 1).coerceAtLeast(0) val pagerState: PagerState = remember { PagerState(currentPage = 0, minPage = 0, maxPage = maxPage) } pagerState.isDualMode = isDualScreen ViewPager( state = pagerState, - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) ) { pages[page]() } diff --git a/VideoPlusChat/build.gradle b/VideoPlusChat/build.gradle index b153e21..cb63855 100644 --- a/VideoPlusChat/build.gradle +++ b/VideoPlusChat/build.gradle @@ -42,6 +42,7 @@ android { excludes += '/META-INF/{AL2.0,LGPL2.1}' } } + namespace 'com.microsoft.device.display.samples.videochatcomposesample' } dependencies { diff --git a/VideoPlusChat/src/main/AndroidManifest.xml b/VideoPlusChat/src/main/AndroidManifest.xml index a1b9d9b..da285e0 100644 --- a/VideoPlusChat/src/main/AndroidManifest.xml +++ b/VideoPlusChat/src/main/AndroidManifest.xml @@ -5,8 +5,7 @@ --> + xmlns:tools="http://schemas.android.com/tools"> diff --git a/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/ChatPage.kt b/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/ChatPage.kt index 21b6395..c77800d 100644 --- a/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/ChatPage.kt +++ b/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/ChatPage.kt @@ -62,6 +62,7 @@ fun ChatPage(focusManager: FocusManager) { modifier = Modifier .fillMaxHeight(0.85f) .fillMaxWidth() + .padding(it) .background(MaterialTheme.colors.secondary) ) } @@ -149,6 +150,7 @@ fun ChatInputBar(focusManager: FocusManager) { @Composable fun ChatList(modifier: Modifier = Modifier) { val chatModel = ChatModel() + LazyColumn( modifier = modifier.padding(all = 10.dp) ) { diff --git a/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/VideoPage.kt b/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/VideoPage.kt index 986cb3e..84f2642 100644 --- a/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/VideoPage.kt +++ b/VideoPlusChat/src/main/java/com/microsoft/device/display/samples/videochatcomposesample/ui/views/VideoPage.kt @@ -51,26 +51,14 @@ fun VideoPage( @Composable fun FullscreenButton(modifier: Modifier, isFullScreen: Boolean, updateFullScreen: (Boolean) -> Unit) { fun onClick() = updateFullScreen(!isFullScreen) + val drawableResId = if (isFullScreen) R.drawable.exitfullscreen else R.drawable.fullscreen + val stringResId = if (isFullScreen) R.string.contentFull else R.string.contentMin - if (isFullScreen) Icon( + Icon( tint = MaterialTheme.colors.onBackground, - painter = painterResource(id = R.drawable.exitfullscreen), - contentDescription = stringResource(id = R.string.contentFull), - modifier = modifier.clickable( - onClick = { - onClick() - } - ) - - ) else Icon( - tint = MaterialTheme.colors.onBackground, - painter = painterResource(id = R.drawable.fullscreen), - contentDescription = stringResource(id = R.string.contentMin), - modifier = modifier.clickable( - onClick = { - onClick() - } - ) + painter = painterResource(id = drawableResId), + contentDescription = stringResource(id = stringResId), + modifier = modifier.clickable(onClick = { onClick() }) ) } diff --git a/dependencies.gradle b/dependencies.gradle index 6bd4a8a..128b152 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -4,9 +4,9 @@ */ ext { - gradlePluginVersion = '7.1.1' - kotlinVersion = "1.6.10" - compileSdkVersion = 31 + gradlePluginVersion = '7.2.0' + kotlinVersion = "1.7.0" + compileSdkVersion = 32 targetSdkVersion = compileSdkVersion minSdkVersion = 23 @@ -21,9 +21,9 @@ ext { ] // AndroidX versions - appCompatVersion = '1.4.1' - ktxCoreVersion = '1.7.0' - lcRunVersion = '2.4.1' + appCompatVersion = '1.4.2' + ktxCoreVersion = '1.8.0' + lcRunVersion = '2.5.1' androidxDependencies = [ appCompat : "androidx.appcompat:appcompat:$appCompatVersion", ktxCore : "androidx.core:core-ktx:$ktxCoreVersion", @@ -31,9 +31,9 @@ ext { ] // Compose dependencies - composeVersion = "1.1.1" - activityComposeVersion = "1.4.0" - navigationComposeVersion = '2.4.2' + composeVersion = '1.2.0' + activityComposeVersion = '1.5.1' + navigationComposeVersion = '2.5.1' composeDependencies = [ composeAnimation : "androidx.compose.animation:animation:$composeVersion", composeRuntime : "androidx.compose.runtime:runtime-livedata:$composeVersion", @@ -64,8 +64,8 @@ ext { ] // Google dependencies - materialVersion = '1.5.0' - exoPlayerVersion = '2.17.1' + materialVersion = '1.6.1' + exoPlayerVersion = '2.18.1' systemUiControllerVersion = '0.17.0' googleDependencies = [ material: "com.google.android.material:material:$materialVersion", @@ -74,10 +74,10 @@ ext { ] // Microsoft dependencies - twoPaneLayoutVersion = "1.0.1-alpha02" - windowStateVersion = "1.0.0-alpha04" - composeTestingVersion = "1.0.0-alpha04" - dragAndDropVersion = "1.0.0-alpha01" + twoPaneLayoutVersion = "1.0.1-alpha03" + windowStateVersion = "1.0.0-alpha05" + composeTestingVersion = "1.0.0-alpha06" + dragAndDropVersion = "1.0.0-alpha02" microsoftDependencies = [ twoPaneLayout : "com.microsoft.device.dualscreen:twopanelayout:$twoPaneLayoutVersion", windowState : "com.microsoft.device.dualscreen:windowstate:$windowStateVersion", diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 847372c..6f9f9a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -5,7 +5,7 @@ #Thu Feb 25 11:36:45 PST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME