Skip to content

Commit ee6d2e7

Browse files
authored
build: publish mobile beta 1.8.0-beta.1
2 parents adc744b + 1ec0e4a commit ee6d2e7

49 files changed

Lines changed: 1668 additions & 573 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎app/build.gradle.kts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ android {
2626
applicationId = "dev.typetype.android"
2727
minSdk = 23
2828
targetSdk = 37
29-
versionCode = 10716
30-
versionName = "1.7.0-beta.17"
29+
versionCode = 10801
30+
versionName = "1.8.0-beta.1"
3131
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3232
resValue("string", "app_name", "TypeType")
3333
}
@@ -50,7 +50,7 @@ android {
5050
buildTypes {
5151
getByName("release") {
5252
isMinifyEnabled = true
53-
isShrinkResources = false
53+
isShrinkResources = true
5454
signingConfig = signingConfigs.findByName("release")?.takeIf { it.storeFile != null }
5555
proguardFiles(
5656
getDefaultProguardFile("proguard-android-optimize.txt"),

‎app/src/androidTest/java/dev/typetype/android/AppShellAdaptiveTest.kt‎

Lines changed: 0 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -64,76 +64,6 @@ class AppShellAdaptiveTest {
6464
@get:Rule
6565
val composeRule = createAndroidComposeRule<ComponentActivity>()
6666

67-
@Test
68-
fun wideWindowUsesNavigationRail() {
69-
setShellWidth(700.dp)
70-
71-
assertNodeCount(APP_NAVIGATION_RAIL_TAG, 1)
72-
assertNodeCount(APP_BOTTOM_NAVIGATION_TAG, 0)
73-
}
74-
75-
@Test
76-
fun compactWindowUsesBottomNavigation() {
77-
setShellWidth(400.dp)
78-
79-
assertNodeCount(APP_BOTTOM_NAVIGATION_TAG, 1)
80-
assertNodeCount(APP_NAVIGATION_RAIL_TAG, 0)
81-
}
82-
83-
@Test
84-
fun landscapePhoneUsesBottomNavigation() {
85-
setShellSize(width = 800.dp, height = 400.dp)
86-
87-
assertNodeCount(APP_BOTTOM_NAVIGATION_TAG, 1)
88-
assertNodeCount(APP_NAVIGATION_RAIL_TAG, 0)
89-
}
90-
91-
@Test
92-
fun shortsTabFollowsTheServerVisibilitySetting() {
93-
val showShorts = mutableStateOf(false)
94-
setShellSize(width = 300.dp, height = 500.dp, showShorts = showShorts)
95-
96-
composeRule.onNodeWithText("Shorts").assertDoesNotExist()
97-
98-
composeRule.runOnIdle { showShorts.value = true }
99-
100-
composeRule.onNodeWithText("Shorts").assertIsDisplayed()
101-
}
102-
103-
@Test
104-
fun compactNavigationRemainsVisibleAtTwoHundredPercentText() {
105-
setShellSize(width = 320.dp, height = 500.dp, fontScale = 2f)
106-
107-
listOf("Home", "Shorts", "Subscriptions", "Library").forEach {
108-
composeRule.onNodeWithText(it).assertIsDisplayed()
109-
}
110-
}
111-
112-
@Test
113-
fun rightToLeftLayoutMirrorsTheTopLevelTabs() {
114-
setShellSize(
115-
width = 400.dp,
116-
height = 800.dp,
117-
layoutDirection = LayoutDirection.Rtl,
118-
)
119-
120-
val home = composeRule.onNodeWithText("Home").bounds()
121-
val library = composeRule.onNodeWithText("Library").bounds()
122-
assertTrue(home.left > library.left)
123-
}
124-
125-
@Test
126-
fun directionalKeysMoveFocusAcrossTopLevelTabs() {
127-
setShellSize(width = 400.dp, height = 800.dp, keyboardInput = true)
128-
val home = composeRule.onNodeWithText("Home")
129-
130-
home.performSemanticsAction(SemanticsActions.RequestFocus)
131-
home.assertIsFocused()
132-
home.performKeyInput { pressKey(Key.DirectionRight) }
133-
134-
composeRule.onNodeWithText("Shorts").assertIsFocused()
135-
}
136-
13767
@Test
13868
fun topLevelNavigationDoesNotRestoreSearchOverItsOpeningTab() {
13969
composeRule.setContent {
@@ -318,59 +248,6 @@ class AppShellAdaptiveTest {
318248
composeRule.onNodeWithTag(SEARCH_FIELD_TAG).assertTextEquals("")
319249
}
320250

321-
private fun setShellWidth(width: Dp) {
322-
setShellSize(width = width, height = 800.dp)
323-
}
324-
325-
private fun setShellSize(
326-
width: Dp,
327-
height: Dp,
328-
showShorts: MutableState<Boolean> = mutableStateOf(true),
329-
fontScale: Float = 1f,
330-
layoutDirection: LayoutDirection = LayoutDirection.Ltr,
331-
keyboardInput: Boolean = false,
332-
) {
333-
composeRule.setContent {
334-
val systemDensity = LocalDensity.current
335-
val inputModeManager = LocalInputModeManager.current
336-
LaunchedEffect(keyboardInput) {
337-
if (keyboardInput) inputModeManager.requestInputMode(InputMode.Keyboard)
338-
}
339-
CompositionLocalProvider(
340-
LocalDensity provides Density(systemDensity.density, fontScale),
341-
LocalLayoutDirection provides layoutDirection,
342-
) {
343-
val navController = rememberNavController()
344-
AppShell(
345-
navController = navController,
346-
playerHostController = PlayerHostController(FakePlaybackQueueController()),
347-
onOpenSettings = {},
348-
onPlayVideo = {},
349-
onOpenChannel = {},
350-
onOpenAccounts = {},
351-
onClosePlayback = {},
352-
showShorts = showShorts.value,
353-
modifier = Modifier.requiredWidth(width).requiredHeight(height),
354-
) { contentModifier ->
355-
NavHost(
356-
navController = navController,
357-
startDestination = HomeRoute,
358-
modifier = contentModifier,
359-
) {
360-
composable<HomeRoute> { }
361-
}
362-
}
363-
}
364-
}
365-
}
366-
367-
private fun androidx.compose.ui.test.SemanticsNodeInteraction.bounds(): Rect =
368-
fetchSemanticsNode().boundsInRoot
369-
370-
private fun assertNodeCount(tag: String, expected: Int) {
371-
val count = composeRule.onAllNodesWithTag(tag).fetchSemanticsNodes().size
372-
assertEquals(expected, count)
373-
}
374251
}
375252

376253
private const val SEARCH_FIELD_TAG = "search_field"
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
package dev.typetype.android
2+
3+
import androidx.activity.ComponentActivity
4+
import androidx.compose.foundation.text.BasicTextField
5+
import androidx.compose.foundation.layout.requiredHeight
6+
import androidx.compose.foundation.layout.requiredWidth
7+
import androidx.compose.runtime.MutableState
8+
import androidx.compose.runtime.CompositionLocalProvider
9+
import androidx.compose.runtime.LaunchedEffect
10+
import androidx.compose.runtime.mutableStateOf
11+
import androidx.compose.runtime.getValue
12+
import androidx.compose.runtime.saveable.rememberSaveable
13+
import androidx.compose.runtime.setValue
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.geometry.Rect
16+
import androidx.compose.ui.input.InputMode
17+
import androidx.compose.ui.platform.LocalDensity
18+
import androidx.compose.ui.platform.LocalInputModeManager
19+
import androidx.compose.ui.platform.LocalLayoutDirection
20+
import androidx.compose.ui.platform.testTag
21+
import androidx.compose.ui.semantics.SemanticsActions
22+
import androidx.compose.ui.test.junit4.StateRestorationTester
23+
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
24+
import androidx.compose.ui.test.assertIsDisplayed
25+
import androidx.compose.ui.test.assertIsFocused
26+
import androidx.compose.ui.test.assertTextEquals
27+
import androidx.compose.ui.test.onNodeWithContentDescription
28+
import androidx.compose.ui.test.onAllNodesWithTag
29+
import androidx.compose.ui.test.onNodeWithTag
30+
import androidx.compose.ui.test.onNodeWithText
31+
import androidx.compose.ui.test.performClick
32+
import androidx.compose.ui.test.performKeyInput
33+
import androidx.compose.ui.test.performSemanticsAction
34+
import androidx.compose.ui.test.performTextInput
35+
import androidx.compose.ui.input.key.Key
36+
import androidx.compose.ui.test.pressKey
37+
import androidx.compose.ui.unit.Density
38+
import androidx.compose.ui.unit.Dp
39+
import androidx.compose.ui.unit.LayoutDirection
40+
import androidx.compose.ui.unit.dp
41+
import androidx.navigation.compose.NavHost
42+
import androidx.navigation.compose.composable
43+
import androidx.navigation.compose.rememberNavController
44+
import androidx.navigation.NavHostController
45+
import androidx.test.espresso.Espresso.closeSoftKeyboard
46+
import dev.typetype.android.core.ui.navigation.HomeRoute
47+
import dev.typetype.android.core.ui.navigation.ChannelRoute
48+
import dev.typetype.android.core.ui.navigation.LibraryRoute
49+
import dev.typetype.android.core.ui.navigation.SearchRoute
50+
import dev.typetype.android.core.ui.navigation.SubscriptionsRoute
51+
import dev.typetype.android.domain.playback.PlaybackQueueController
52+
import dev.typetype.android.domain.playback.PlaybackQueueEntry
53+
import dev.typetype.android.domain.playback.PlaybackQueueSnapshot
54+
import dev.typetype.android.domain.playback.PlaybackQueueState
55+
import dev.typetype.android.feature.player.host.PlayerHostController
56+
import kotlinx.coroutines.flow.MutableStateFlow
57+
import kotlinx.coroutines.flow.StateFlow
58+
import org.junit.Rule
59+
import org.junit.Assert.assertEquals
60+
import org.junit.Assert.assertTrue
61+
import org.junit.Test
62+
63+
class AppShellNavigationLayoutTest {
64+
@get:Rule
65+
val composeRule = createAndroidComposeRule<ComponentActivity>()
66+
67+
@Test
68+
fun wideWindowUsesBottomNavigation() {
69+
setShellWidth(700.dp)
70+
71+
assertNodeCount(APP_NAVIGATION_RAIL_TAG, 0)
72+
assertNodeCount(APP_BOTTOM_NAVIGATION_TAG, 1)
73+
}
74+
75+
@Test
76+
fun compactWindowUsesBottomNavigation() {
77+
setShellWidth(400.dp)
78+
79+
assertNodeCount(APP_BOTTOM_NAVIGATION_TAG, 1)
80+
assertNodeCount(APP_NAVIGATION_RAIL_TAG, 0)
81+
}
82+
83+
@Test
84+
fun landscapePhoneUsesBottomNavigation() {
85+
setShellSize(width = 800.dp, height = 400.dp)
86+
87+
assertNodeCount(APP_BOTTOM_NAVIGATION_TAG, 1)
88+
assertNodeCount(APP_NAVIGATION_RAIL_TAG, 0)
89+
}
90+
91+
@Test
92+
fun shortsTabFollowsTheServerVisibilitySetting() {
93+
val showShorts = mutableStateOf(false)
94+
setShellSize(width = 300.dp, height = 500.dp, showShorts = showShorts)
95+
96+
composeRule.onNodeWithText("Shorts").assertDoesNotExist()
97+
98+
composeRule.runOnIdle { showShorts.value = true }
99+
100+
composeRule.onNodeWithText("Shorts").assertIsDisplayed()
101+
}
102+
103+
@Test
104+
fun compactNavigationRemainsVisibleAtTwoHundredPercentText() {
105+
setShellSize(width = 320.dp, height = 500.dp, fontScale = 2f)
106+
107+
listOf("Home", "Shorts", "Subscriptions", "Library").forEach {
108+
composeRule.onNodeWithText(it).assertIsDisplayed()
109+
}
110+
}
111+
112+
@Test
113+
fun rightToLeftLayoutMirrorsTheTopLevelTabs() {
114+
setShellSize(
115+
width = 400.dp,
116+
height = 800.dp,
117+
layoutDirection = LayoutDirection.Rtl,
118+
)
119+
120+
val home = composeRule.onNodeWithText("Home").bounds()
121+
val library = composeRule.onNodeWithText("Library").bounds()
122+
assertTrue(home.left > library.left)
123+
}
124+
125+
@Test
126+
fun directionalKeysMoveFocusAcrossTopLevelTabs() {
127+
setShellSize(width = 400.dp, height = 800.dp, keyboardInput = true)
128+
val home = composeRule.onNodeWithText("Home")
129+
130+
home.performSemanticsAction(SemanticsActions.RequestFocus)
131+
home.assertIsFocused()
132+
home.performKeyInput { pressKey(Key.DirectionRight) }
133+
134+
composeRule.onNodeWithText("Shorts").assertIsFocused()
135+
}
136+
137+
private fun setShellWidth(width: Dp) {
138+
setShellSize(width = width, height = 800.dp)
139+
}
140+
141+
private fun setShellSize(
142+
width: Dp,
143+
height: Dp,
144+
showShorts: MutableState<Boolean> = mutableStateOf(true),
145+
fontScale: Float = 1f,
146+
layoutDirection: LayoutDirection = LayoutDirection.Ltr,
147+
keyboardInput: Boolean = false,
148+
) {
149+
composeRule.setContent {
150+
val systemDensity = LocalDensity.current
151+
val inputModeManager = LocalInputModeManager.current
152+
LaunchedEffect(keyboardInput) {
153+
if (keyboardInput) inputModeManager.requestInputMode(InputMode.Keyboard)
154+
}
155+
CompositionLocalProvider(
156+
LocalDensity provides Density(systemDensity.density, fontScale),
157+
LocalLayoutDirection provides layoutDirection,
158+
) {
159+
val navController = rememberNavController()
160+
AppShell(
161+
navController = navController,
162+
playerHostController = PlayerHostController(TabletLayoutQueueController()),
163+
onOpenSettings = {},
164+
onPlayVideo = {},
165+
onOpenChannel = {},
166+
onOpenAccounts = {},
167+
onClosePlayback = {},
168+
showShorts = showShorts.value,
169+
modifier = Modifier.requiredWidth(width).requiredHeight(height),
170+
) { contentModifier ->
171+
NavHost(
172+
navController = navController,
173+
startDestination = HomeRoute,
174+
modifier = contentModifier,
175+
) {
176+
composable<HomeRoute> { }
177+
}
178+
}
179+
}
180+
}
181+
}
182+
183+
private fun androidx.compose.ui.test.SemanticsNodeInteraction.bounds(): Rect =
184+
fetchSemanticsNode().boundsInRoot
185+
186+
private fun assertNodeCount(tag: String, expected: Int) {
187+
val count = composeRule.onAllNodesWithTag(tag).fetchSemanticsNodes().size
188+
assertEquals(expected, count)
189+
}
190+
}
191+
192+
private class TabletLayoutQueueController : PlaybackQueueController {
193+
override val state: StateFlow<PlaybackQueueState> = MutableStateFlow(PlaybackQueueState())
194+
override fun start(title: String, entries: List<PlaybackQueueEntry>, shuffle: Boolean) = Unit
195+
override fun restore(snapshot: PlaybackQueueSnapshot) = Unit
196+
override fun clear() = Unit
197+
}

0 commit comments

Comments
 (0)