Skip to content

Commit 4ddaa52

Browse files
authored
Merge pull request #295 from ptkNktq/enhance/transition
Enhance/transition
2 parents 79115e0 + 4f1d408 commit 4ddaa52

11 files changed

Lines changed: 407 additions & 327 deletions

File tree

AndroidApp/ui/src/main/kotlin/me/nya_n/notificationnotifier/ui/common/AppList.kt

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import androidx.compose.ui.unit.dp
2323
import me.nya_n.notificationnotifier.model.InstalledApp
2424
import me.nya_n.notificationnotifier.ui.R
2525
import me.nya_n.notificationnotifier.ui.theme.AppTheme
26+
import me.nya_n.notificationnotifier.ui.util.LocalAnimatedVisibilityScope
27+
import me.nya_n.notificationnotifier.ui.util.LocalSharedTransitionScope
28+
import me.nya_n.notificationnotifier.ui.util.iconSharedTransitionKey
2629

2730
@Composable
2831
fun AppList(
@@ -36,7 +39,12 @@ fun AppList(
3639
items(
3740
count = items.size,
3841
key = { "($it)${items[it]}" },
39-
itemContent = { AppListItem(app = items[it], onAppSelected = onAppSelected) }
42+
itemContent = {
43+
AppListItem(
44+
app = items[it],
45+
onAppSelected = onAppSelected
46+
)
47+
}
4048
)
4149
}
4250
}
@@ -47,7 +55,10 @@ fun AppListItem(
4755
app: InstalledApp,
4856
onAppSelected: (InstalledApp) -> Unit
4957
) {
58+
val sharedTransitionScope = LocalSharedTransitionScope.current
59+
val animatedVisibilityScope = LocalAnimatedVisibilityScope.current
5060
val interactionSource = remember { MutableInteractionSource() }
61+
5162
Box(
5263
modifier = Modifier
5364
.fillMaxWidth()
@@ -62,7 +73,20 @@ fun AppListItem(
6273
) {
6374
GrayScaleAppIcon(
6475
app = app,
65-
modifier = Modifier.size(56.dp),
76+
modifier = Modifier
77+
.size(56.dp)
78+
.then(
79+
if (sharedTransitionScope != null && animatedVisibilityScope != null) {
80+
with(sharedTransitionScope) {
81+
Modifier.sharedElement(
82+
rememberSharedContentState(key = app.iconSharedTransitionKey),
83+
animatedVisibilityScope = animatedVisibilityScope
84+
)
85+
}
86+
} else {
87+
Modifier
88+
}
89+
),
6690
isInListView = true
6791
)
6892
Box(

AndroidApp/ui/src/main/kotlin/me/nya_n/notificationnotifier/ui/common/AppScaffold.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@ import me.nya_n.notificationnotifier.ui.theme.AppTheme
1818
@Composable
1919
fun AppScaffold(
2020
snackbarHostState: SnackbarHostState,
21-
hasBackContent: Boolean = false,
22-
onBack: () -> Unit = { },
21+
onBack: (() -> Unit)? = null,
2322
bottomBar: @Composable () -> Unit = { },
2423
content: @Composable (PaddingValues) -> Unit
2524
) {
2625
Scaffold(
2726
topBar = {
28-
TopBar(
29-
hasBackContent = hasBackContent,
30-
onBack = onBack
31-
)
27+
TopBar(onBack = onBack)
3228
},
3329
bottomBar = bottomBar,
3430
containerColor = MaterialTheme.colorScheme.secondary,

AndroidApp/ui/src/main/kotlin/me/nya_n/notificationnotifier/ui/common/TopBar.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ import me.nya_n.notificationnotifier.ui.theme.AppTheme
1818
@OptIn(ExperimentalMaterial3Api::class)
1919
@Composable
2020
fun TopBar(
21-
hasBackContent: Boolean = false,
22-
onBack: () -> Unit = { }
21+
onBack: (() -> Unit)? = null
2322
) {
2423
TopAppBar(
2524
title = {
@@ -29,10 +28,11 @@ fun TopBar(
2928
)
3029
},
3130
navigationIcon = {
32-
if (hasBackContent) {
33-
IconButton(
34-
onClick = onBack
35-
) {
31+
IconButton(
32+
onClick = onBack ?: { },
33+
enabled = onBack != null
34+
) {
35+
if (onBack != null) {
3636
Icon(
3737
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
3838
contentDescription = stringResource(id = R.string.back)
@@ -59,7 +59,6 @@ private fun TopBarPreview() {
5959
private fun SubContentTopBarPreview() {
6060
AppTheme {
6161
TopBar(
62-
hasBackContent = true,
6362
onBack = { }
6463
)
6564
}

AndroidApp/ui/src/main/kotlin/me/nya_n/notificationnotifier/ui/screen/app/AppScreen.kt

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package me.nya_n.notificationnotifier.ui.screen.app
33
import android.content.Intent
44
import android.provider.Settings
55
import androidx.activity.compose.LocalActivity
6+
import androidx.compose.animation.SharedTransitionLayout
67
import androidx.compose.animation.slideInHorizontally
78
import androidx.compose.animation.slideOutHorizontally
89
import androidx.compose.runtime.Composable
10+
import androidx.compose.runtime.CompositionLocalProvider
911
import androidx.compose.runtime.DisposableEffect
1012
import androidx.compose.runtime.collectAsState
1113
import androidx.compose.runtime.getValue
@@ -16,14 +18,13 @@ import androidx.navigation.compose.NavHost
1618
import androidx.navigation.compose.composable
1719
import androidx.navigation.compose.rememberNavController
1820
import com.google.gson.Gson
19-
import me.nya_n.notificationnotifier.model.InstalledApp
2021
import me.nya_n.notificationnotifier.ui.common.RequireNotificationPermissionDialog
2122
import me.nya_n.notificationnotifier.ui.common.RequirePackageVisibilityDialog
2223
import me.nya_n.notificationnotifier.ui.screen.about.AboutScreen
23-
import me.nya_n.notificationnotifier.ui.screen.detail.DetailScreen
2424
import me.nya_n.notificationnotifier.ui.screen.license.LicenseScreen
2525
import me.nya_n.notificationnotifier.ui.screen.main.MainScreen
2626
import me.nya_n.notificationnotifier.ui.theme.AppTheme
27+
import me.nya_n.notificationnotifier.ui.util.LocalSharedTransitionScope
2728
import org.koin.androidx.compose.koinViewModel
2829
import java.net.URLEncoder
2930

@@ -75,35 +76,33 @@ fun AppScreen(
7576
}
7677
}
7778

78-
NavHost(
79-
navController = navController,
80-
startDestination = Screen.Main.name,
81-
enterTransition = {
82-
slideInHorizontally(initialOffsetX = { it })
83-
},
84-
exitTransition = {
85-
slideOutHorizontally(targetOffsetX = { -it })
86-
},
87-
popEnterTransition = {
88-
slideInHorizontally(initialOffsetX = { -it })
89-
},
90-
popExitTransition = {
91-
slideOutHorizontally(targetOffsetX = { it })
92-
},
93-
) {
94-
composable(Screen.Main.route) { MainScreen(navController) }
95-
composable(Screen.License.route) { LicenseScreen(navController) }
96-
composable(Screen.Detail.route) {
97-
val app = Gson().fromJson(
98-
it.arguments?.getString("app"),
99-
InstalledApp::class.java
100-
)
101-
DetailScreen(
102-
navController = navController,
103-
app = app
104-
)
79+
SharedTransitionLayout {
80+
NavHost(
81+
navController = navController,
82+
startDestination = Screen.Main.name,
83+
enterTransition = {
84+
slideInHorizontally(initialOffsetX = { it })
85+
},
86+
exitTransition = {
87+
slideOutHorizontally(targetOffsetX = { -it })
88+
},
89+
popEnterTransition = {
90+
slideInHorizontally(initialOffsetX = { -it })
91+
},
92+
popExitTransition = {
93+
slideOutHorizontally(targetOffsetX = { it })
94+
},
95+
) {
96+
composable(Screen.Main.route) {
97+
CompositionLocalProvider(
98+
LocalSharedTransitionScope provides this@SharedTransitionLayout
99+
) {
100+
MainScreen(navController = navController)
101+
}
102+
}
103+
composable(Screen.License.route) { LicenseScreen(navController) }
104+
composable(Screen.About.route) { AboutScreen() }
105105
}
106-
composable(Screen.About.route) { AboutScreen() }
107106
}
108107
}
109108
}
@@ -113,7 +112,10 @@ sealed class Screen(
113112
val name: String,
114113
private val args: List<String> = emptyList()
115114
) {
116-
data object Main : Screen("main")
115+
data object Main : Screen("main") {
116+
data object Targets : Screen("targets")
117+
data object Detail : Screen("detail", listOf("app"))
118+
}
117119
data object License : Screen("license")
118120
data object Detail : Screen("detail", listOf("app"))
119121
data object About : Screen("about")

AndroidApp/ui/src/main/kotlin/me/nya_n/notificationnotifier/ui/screen/detail/DetailScreen.kt

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@ import androidx.navigation.NavController
3636
import me.nya_n.notificationnotifier.model.InstalledApp
3737
import me.nya_n.notificationnotifier.ui.R
3838
import me.nya_n.notificationnotifier.ui.common.AppOutlinedButton
39-
import me.nya_n.notificationnotifier.ui.common.AppScaffold
4039
import me.nya_n.notificationnotifier.ui.common.Category
4140
import me.nya_n.notificationnotifier.ui.common.GrayScaleAppIcon
4241
import me.nya_n.notificationnotifier.ui.common.SnackbarMessage
4342
import me.nya_n.notificationnotifier.ui.theme.AppColors
4443
import me.nya_n.notificationnotifier.ui.theme.AppTheme
44+
import me.nya_n.notificationnotifier.ui.util.LocalAnimatedVisibilityScope
45+
import me.nya_n.notificationnotifier.ui.util.LocalSharedTransitionScope
46+
import me.nya_n.notificationnotifier.ui.util.iconSharedTransitionKey
4547
import org.koin.androidx.compose.koinViewModel
4648
import org.koin.core.parameter.parametersOf
4749

@@ -61,12 +63,8 @@ fun DetailScreen(
6163
viewModel.messageShown()
6264
}
6365
DetailContent(
64-
snackbarHostState = snackbarHostState,
6566
app = app,
6667
condition = uiState.condition,
67-
onBack = {
68-
navController.popBackStack()
69-
},
7068
onDeleteApp = {
7169
viewModel.deleteTarget()
7270
navController.previousBackStackEntry?.apply {
@@ -81,27 +79,21 @@ fun DetailScreen(
8179
/** 詳細画面のコンテンツ本体 */
8280
@Composable
8381
fun DetailContent(
84-
snackbarHostState: SnackbarHostState,
8582
app: InstalledApp,
8683
condition: String,
87-
onBack: () -> Unit,
8884
onDeleteApp: () -> Unit,
8985
onConditionChanged: (String) -> Unit
9086
) {
91-
AppScaffold(
92-
snackbarHostState = snackbarHostState,
93-
hasBackContent = true,
94-
onBack = onBack
87+
Column(
88+
modifier = Modifier
89+
.fillMaxSize()
90+
.padding(horizontal = 20.dp),
9591
) {
96-
Column(
97-
modifier = Modifier
98-
.fillMaxSize()
99-
.padding(it)
100-
.padding(horizontal = 20.dp),
101-
) {
102-
AppInfo(app, onDeleteApp)
103-
NotificationSetting(condition, onConditionChanged)
104-
}
92+
AppInfo(
93+
app = app,
94+
onDeleteApp = onDeleteApp
95+
)
96+
NotificationSetting(condition, onConditionChanged)
10597
}
10698
}
10799

@@ -113,14 +105,30 @@ private fun AppInfo(
113105
app: InstalledApp,
114106
onDeleteApp: () -> Unit
115107
) {
108+
val sharedTransitionScope = LocalSharedTransitionScope.current
109+
val animatedVisibilityScope = LocalAnimatedVisibilityScope.current
110+
116111
Category(name = stringResource(id = R.string.app_info))
117112
Box(
118113
modifier = Modifier.padding(vertical = 20.dp)
119114
) {
120115
Row {
121116
GrayScaleAppIcon(
122117
app = app,
123-
modifier = Modifier.size(80.dp),
118+
modifier = Modifier
119+
.size(80.dp)
120+
.then(
121+
if (sharedTransitionScope != null && animatedVisibilityScope != null) {
122+
with(sharedTransitionScope) {
123+
Modifier.sharedElement(
124+
rememberSharedContentState(key = app.iconSharedTransitionKey),
125+
animatedVisibilityScope = animatedVisibilityScope
126+
)
127+
}
128+
} else {
129+
Modifier
130+
}
131+
),
124132
isInListView = false
125133
)
126134
Box(
@@ -188,13 +196,10 @@ private fun NotificationSetting(
188196
@Preview(backgroundColor = 0xFFC7B5A8, showBackground = true)
189197
@Composable
190198
private fun DetailPreview() {
191-
val snackbarHostState = remember { SnackbarHostState() }
192199
AppTheme {
193200
DetailContent(
194-
snackbarHostState = snackbarHostState,
195201
app = InstalledApp("Sample App Name", "example.sample.test"),
196202
condition = "^.*$",
197-
onBack = { },
198203
onDeleteApp = { },
199204
onConditionChanged = { }
200205
)
@@ -204,16 +209,13 @@ private fun DetailPreview() {
204209
@Preview(backgroundColor = 0xFFC7B5A8, showBackground = true)
205210
@Composable
206211
private fun LongAppNameDetailPreview() {
207-
val snackbarHostState = remember { SnackbarHostState() }
208212
AppTheme {
209213
DetailContent(
210-
snackbarHostState = snackbarHostState,
211214
app = InstalledApp(
212215
"Sample App Name So Loooooooooooooooooooong",
213216
"example.sample.test"
214217
),
215218
condition = "",
216-
onBack = { },
217219
onDeleteApp = { },
218220
onConditionChanged = { }
219221
)

AndroidApp/ui/src/main/kotlin/me/nya_n/notificationnotifier/ui/screen/license/LicenseScreen.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ fun LicenseScreen(navController: NavController) {
1919
val snackbarHostState = remember { SnackbarHostState() }
2020
AppScaffold(
2121
snackbarHostState = snackbarHostState,
22-
hasBackContent = true,
2322
onBack = {
2423
navController.popBackStack()
2524
}

0 commit comments

Comments
 (0)