diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 5d46d9a..01c3edd 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -147,6 +147,7 @@ kotlin { implementation(libs.android.credentials.play.services) implementation(libs.translate) + implementation(libs.instantapps) } } } diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index 915517e..e19f2a3 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -1,6 +1,10 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:dist="http://schemas.android.com/apk/distribution" + android:targetSandboxVersion="2"> + + @@ -29,6 +33,7 @@ android:icon="@android:drawable/ic_menu_compass" android:label="@string/app_name" android:usesCleartextTraffic="false" + android:networkSecurityConfig="@xml/network_security" android:hardwareAccelerated="true" android:enableOnBackInvokedCallback="true" android:appCategory="news" diff --git a/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.android.kt b/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.android.kt new file mode 100644 index 0000000..be329ae --- /dev/null +++ b/composeApp/src/androidMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.android.kt @@ -0,0 +1,27 @@ +package dev.datlag.aniflow.other + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import com.google.android.gms.instantapps.InstantApps +import dev.datlag.aniflow.findActivity + +actual class InstantAppHelper(private val context: Context) { + + actual val isInstantApp: Boolean + get() = InstantApps.getPackageManagerCompat(context).isInstantApp + + actual fun showInstallPrompt() { + context.findActivity()?.let { + InstantApps.showInstallPrompt(it, null, 1337, null) + } + } +} + +@Composable +actual fun rememberInstantAppHelper(): InstantAppHelper { + val context = LocalContext.current + + return remember(context) { InstantAppHelper(context) } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/res/xml/network_security.xml b/composeApp/src/androidMain/res/xml/network_security.xml new file mode 100644 index 0000000..7944f5b --- /dev/null +++ b/composeApp/src/androidMain/res/xml/network_security.xml @@ -0,0 +1,19 @@ + + + + + + + + + anilist.co + googleapis.com + trace.moe + nekosapi.com + + + + + + + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.kt new file mode 100644 index 0000000..5e2ce7c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.kt @@ -0,0 +1,12 @@ +package dev.datlag.aniflow.other + +import androidx.compose.runtime.Composable + +expect class InstantAppHelper { + val isInstantApp: Boolean + + fun showInstallPrompt() +} + +@Composable +expect fun rememberInstantAppHelper(): InstantAppHelper \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt index 1c41456..345637d 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/other/StateSaver.kt @@ -32,9 +32,6 @@ data object StateSaver { mediumOverview[id] = index to offset } - var settingsOverview: Int = 0 - var settingsOverviewOffset: Int = 0 - data object Home { var airingOverview: Int = 0 var airingOverviewOffset: Int = 0 diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/custom/EditFAB.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/custom/EditFAB.kt deleted file mode 100644 index dd4aaa0..0000000 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/custom/EditFAB.kt +++ /dev/null @@ -1,192 +0,0 @@ -package dev.datlag.aniflow.ui.custom - -import androidx.compose.animation.* -import androidx.compose.animation.core.Spring -import androidx.compose.animation.core.spring -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material.icons.filled.Star -import androidx.compose.material.icons.filled.Visibility -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.TransformOrigin -import androidx.compose.ui.unit.dp -import dev.datlag.aniflow.SharedRes -import dev.datlag.tooling.compose.withDefaultContext -import dev.datlag.tooling.compose.withIOContext -import dev.icerock.moko.resources.compose.painterResource -import dev.icerock.moko.resources.compose.stringResource -import kotlinx.coroutines.delay - -@Composable -fun EditFAB( - displayAdd: Boolean = false, - bsAvailable: Boolean = false, - expanded: Boolean = false, - onBS: () -> Unit, - onRate: () -> Unit, - onProgress: () -> Unit -) { - Column( - horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically) - ) { - var showOtherFABs by remember(expanded) { mutableStateOf(expanded) } - - AnimatedVisibility( - visible = showOtherFABs, - enter = slideInVertically( - animationSpec = bouncySpring(), - initialOffsetY = { -(-it / 2) } - ) + fadeIn( - animationSpec = bouncySpring() - ), - exit = slideOutVertically( - animationSpec = bouncySpring(), - targetOffsetY = { -(-it / 2) } - ) + fadeOut( - animationSpec = bouncySpring() - ) - ) { - LabelFAB( - onClick = { - showOtherFABs = false - onProgress() - } - ) { - Icon( - imageVector = Icons.Default.Visibility, - contentDescription = null - ) - } - } - AnimatedVisibility( - visible = showOtherFABs, - enter = slideInVertically( - animationSpec = bouncySpring(), - initialOffsetY = { -(-it / 2) } - ) + fadeIn( - animationSpec = bouncySpring() - ), - exit = slideOutVertically( - animationSpec = bouncySpring(), - targetOffsetY = { -(-it / 2) } - ) + fadeOut( - animationSpec = bouncySpring() - ) - ) { - LabelFAB( - onClick = { - showOtherFABs = false - onRate() - } - ) { - Icon( - imageVector = Icons.Default.Star, - contentDescription = null - ) - } - } - AnimatedVisibility( - visible = showOtherFABs && bsAvailable, - enter = slideInVertically( - animationSpec = bouncySpring(), - initialOffsetY = { -(-it / 2) } - ) + fadeIn( - animationSpec = bouncySpring() - ), - exit = slideOutVertically( - animationSpec = bouncySpring(), - targetOffsetY = { -(-it / 2) } - ) + fadeOut( - animationSpec = bouncySpring() - ) - ) { - LabelFAB( - onClick = { - showOtherFABs = false - onBS() - } - ) { - Image( - modifier = Modifier.size(24.dp), - painter = painterResource(SharedRes.images.bs), - contentDescription = stringResource(SharedRes.strings.bs), - colorFilter = ColorFilter.tint(LocalContentColor.current) - ) - } - } - - ExtendedFloatingActionButton( - onClick = { - showOtherFABs = !showOtherFABs - }, - expanded = expanded, - icon = { - val icon = if (displayAdd) { - Icons.Default.Add - } else { - Icons.Default.Edit - } - - Icon( - imageVector = icon, - contentDescription = null - ) - }, - text = { - val text = if (displayAdd) { - SharedRes.strings.add - } else { - SharedRes.strings.edit - } - - Text(text = stringResource(text)) - } - ) - } -} - -@Composable -private fun LabelFAB( - onClick: () -> Unit, - icon: @Composable () -> Unit -) { - var showLabel by remember { mutableStateOf(false) } - - LaunchedEffect(showLabel) { - if (!showLabel) { - showLabel = true - } - } - - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - SmallFloatingActionButton( - modifier = Modifier.padding(end = 4.dp), - onClick = onClick - ) { - icon() - } - } - - DisposableEffect(showLabel) { - onDispose { - showLabel = false - } - } -} - -private fun bouncySpring() = spring( - dampingRatio = Spring.DampingRatioMediumBouncy, - stiffness = Spring.StiffnessMedium -) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/custom/InstantAppContent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/custom/InstantAppContent.kt new file mode 100644 index 0000000..3016866 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/custom/InstantAppContent.kt @@ -0,0 +1,19 @@ +package dev.datlag.aniflow.ui.custom + +import androidx.compose.runtime.Composable +import dev.datlag.aniflow.other.InstantAppHelper +import dev.datlag.aniflow.other.rememberInstantAppHelper + +@Composable +fun InstantAppContent( + onInstantApp: @Composable (InstantAppHelper) -> Unit = {}, + content: @Composable () -> Unit +) { + val helper = rememberInstantAppHelper() + + if (helper.isInstantApp) { + onInstantApp(helper) + } else { + content() + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/component/CollapsingToolbar.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/component/CollapsingToolbar.kt index ac6de54..98e0c75 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/component/CollapsingToolbar.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/component/CollapsingToolbar.kt @@ -28,6 +28,7 @@ import dev.datlag.aniflow.LocalHaze import dev.datlag.aniflow.SharedRes import dev.datlag.aniflow.anilist.model.User import dev.datlag.aniflow.anilist.type.MediaType +import dev.datlag.aniflow.other.rememberInstantAppHelper import dev.datlag.tooling.compose.ifFalse import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import dev.icerock.moko.resources.compose.fontFamilyResource @@ -76,17 +77,23 @@ fun CollapsingToolbar( LargeTopAppBar( navigationIcon = { + val helper = rememberInstantAppHelper() + IconButton( modifier = Modifier.ifFalse(isCollapsed) { background(MaterialTheme.colorScheme.surface.copy(alpha = 0.75F), CircleShape) }, onClick = { - onProfileClick() + if (helper.isInstantApp) { + helper.showInstallPrompt() + } else { + onProfileClick() + } } ) { val user by userFlow.collectAsStateWithLifecycle(null) val tintColor = LocalContentColor.current - var colorFilter by remember(user) { mutableStateOf(ColorFilter.tint(tintColor)) } + var colorFilter by remember(user) { mutableStateOf(null) } AsyncImage( model = user?.avatar?.large, diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt index 53243c8..bbfcaf5 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CameraEnhance +import androidx.compose.material.icons.rounded.GetApp import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -35,11 +36,13 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi import dev.chrisbanes.haze.materials.HazeMaterials import dev.datlag.aniflow.LocalHaze import dev.datlag.aniflow.LocalPaddingValues +import dev.datlag.aniflow.SharedRes import dev.datlag.aniflow.anilist.type.MediaType import dev.datlag.aniflow.common.* import dev.datlag.aniflow.other.StateSaver import dev.datlag.aniflow.other.rememberImagePickerState import dev.datlag.aniflow.trace.TraceRepository +import dev.datlag.aniflow.ui.custom.InstantAppContent import dev.datlag.aniflow.ui.navigation.screen.component.CollapsingToolbar import dev.datlag.aniflow.ui.navigation.screen.component.HidingNavigationBar import dev.datlag.aniflow.ui.navigation.screen.component.NavigationBarState @@ -47,6 +50,7 @@ import dev.datlag.aniflow.ui.navigation.screen.home.component.AllLoadingView import dev.datlag.aniflow.ui.navigation.screen.home.component.DefaultOverview import dev.datlag.aniflow.ui.navigation.screen.home.component.ScheduleOverview import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle +import dev.icerock.moko.resources.compose.stringResource import io.github.aakira.napier.Napier @OptIn(ExperimentalMaterial3Api::class) @@ -79,65 +83,85 @@ fun HomeScreen(component: HomeComponent) { ) }, floatingActionButton = { - val traceState by component.traceState.collectAsStateWithLifecycle(TraceRepository.State.None) - val results = remember(traceState) { - (traceState as? TraceRepository.State.Success)?.response?.combinedResults.orEmpty().toList() - } + InstantAppContent( + onInstantApp = { helper -> + ExtendedFloatingActionButton( + onClick = { + helper.showInstallPrompt() + }, + expanded = listState.isScrollingUp() && listState.canScrollForward, + icon = { + Icon( + imageVector = Icons.Rounded.GetApp, + contentDescription = null + ) + }, + text = { + Text(text = stringResource(SharedRes.strings.install)) + } + ) + } + ) { + val traceState by component.traceState.collectAsStateWithLifecycle(TraceRepository.State.None) + val results = remember(traceState) { + (traceState as? TraceRepository.State.Success)?.response?.combinedResults.orEmpty().toList() + } - val dialogState = rememberUseCaseState( - visible = results.isNotEmpty(), - onCloseRequest = { component.clearTrace() }, - onDismissRequest = { component.clearTrace() }, - onFinishedRequest = { component.clearTrace() } - ) + val optionState = rememberUseCaseState( + visible = results.isNotEmpty(), + onCloseRequest = { component.clearTrace() }, + onDismissRequest = { component.clearTrace() }, + onFinishedRequest = { component.clearTrace() } + ) - LaunchedEffect(results) { - if (results.isNotEmpty()) { - dialogState.show() + LaunchedEffect(results) { + if (results.isNotEmpty()) { + optionState.show() + } } - } - OptionDialog( - state = dialogState, - config = OptionConfig( - mode = DisplayMode.LIST - ), - header = Header.Custom { padding -> - Text( - modifier = Modifier.padding(padding.merge(PaddingValues(16.dp))).fillMaxWidth(), - text = "Matching Anime", - textAlign = TextAlign.Center, - fontWeight = FontWeight.SemiBold, - style = MaterialTheme.typography.titleLarge + OptionDialog( + state = optionState, + config = OptionConfig( + mode = DisplayMode.LIST + ), + header = Header.Custom { padding -> + Text( + modifier = Modifier.padding(padding.merge(PaddingValues(16.dp))).fillMaxWidth(), + text = "Matching Anime", + textAlign = TextAlign.Center, + fontWeight = FontWeight.SemiBold, + style = MaterialTheme.typography.titleLarge + ) + }, + selection = OptionSelection.Single( + options = results.map { + Option( + titleText = it.aniList.asMedium().preferred(null) + ) + }, + onSelectOption = { option, _ -> + component.details(results[option].aniList.asMedium()) + } ) - }, - selection = OptionSelection.Single( - options = results.map { - Option( - titleText = it.aniList.asMedium().preferred(null) + ) + + ExtendedFloatingActionButton( + onClick = { + imagePicker.launch() + }, + expanded = listState.isScrollingUp() && listState.canScrollForward, + icon = { + Icon( + imageVector = Icons.Filled.CameraEnhance, + contentDescription = null ) }, - onSelectOption = { option, _ -> - component.details(results[option].aniList.asMedium()) + text = { + Text(text = "Scan") } ) - ) - - ExtendedFloatingActionButton( - onClick = { - imagePicker.launch() - }, - expanded = listState.isScrollingUp() && listState.canScrollForward, - icon = { - Icon( - imageVector = Icons.Filled.CameraEnhance, - contentDescription = null - ) - }, - text = { - Text(text = "Scan") - } - ) + } }, bottomBar = { HidingNavigationBar( diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/dialog/settings/SettingsDialog.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/dialog/settings/SettingsDialog.kt index 8dab1a0..56eaedd 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/dialog/settings/SettingsDialog.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/dialog/settings/SettingsDialog.kt @@ -52,13 +52,7 @@ fun SettingsScreen(component: SettingsComponent) { windowInsets = insets, sheetState = sheetState ) { - val listState = rememberLazyListState( - initialFirstVisibleItemIndex = StateSaver.List.settingsOverview, - initialFirstVisibleItemScrollOffset = StateSaver.List.settingsOverviewOffset - ) - LazyColumn( - state = listState, modifier = Modifier.fillMaxWidth(), contentPadding = bottomPadding.merge(PaddingValues(16.dp)), verticalArrangement = Arrangement.spacedBy(8.dp) @@ -221,12 +215,5 @@ fun SettingsScreen(component: SettingsComponent) { } } } - - DisposableEffect(listState) { - onDispose { - StateSaver.List.settingsOverview = listState.firstVisibleItemIndex - StateSaver.List.settingsOverviewOffset = listState.firstVisibleItemScrollOffset - } - } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt index 8a9c9ba..82fced4 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/MediumScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.rounded.GetApp import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -43,7 +44,7 @@ import dev.datlag.aniflow.anilist.type.MediaStatus import dev.datlag.aniflow.common.* import dev.datlag.aniflow.other.StateSaver import dev.datlag.aniflow.other.UserHelper -import dev.datlag.aniflow.ui.custom.EditFAB +import dev.datlag.aniflow.ui.custom.InstantAppContent import dev.datlag.aniflow.ui.navigation.screen.medium.component.* import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle import dev.icerock.moko.resources.compose.painterResource @@ -94,26 +95,45 @@ fun MediumScreen(component: MediumComponent) { ) }, floatingActionButton = { - val notReleased by component.status.mapCollect(component.initialMedium.status) { - it == MediaStatus.UNKNOWN__ || it == MediaStatus.NOT_YET_RELEASED - } + InstantAppContent( + onInstantApp = { helper -> + ExtendedFloatingActionButton( + onClick = { helper.showInstallPrompt() }, + expanded = listState.isScrollingUp() && listState.canScrollForward, + icon = { + Icon( + imageVector = Icons.Rounded.GetApp, + contentDescription = null, + ) + }, + text = { + Text(text = stringResource(SharedRes.strings.install)) + } + ) + } + ) { + val notReleased by component.status.mapCollect(component.initialMedium.status) { + it == MediaStatus.UNKNOWN__ || it == MediaStatus.NOT_YET_RELEASED + } - if (!notReleased) { - val status by component.listStatus.collectAsStateWithLifecycle(component.initialMedium.entry?.status ?: MediaListStatus.UNKNOWN__) - val type by component.type.collectAsStateWithLifecycle(component.initialMedium.type) + if (!notReleased) { + val status by component.listStatus.collectAsStateWithLifecycle(component.initialMedium.entry?.status ?: MediaListStatus.UNKNOWN__) + val type by component.type.collectAsStateWithLifecycle(component.initialMedium.type) - ExtendedFloatingActionButton( - onClick = { component.edit() }, - icon = { - Icon( - imageVector = status.icon(), - contentDescription = null, - ) - }, - text = { - Text(text = stringResource(status.stringRes(type))) - } - ) + ExtendedFloatingActionButton( + onClick = { component.edit() }, + expanded = listState.isScrollingUp() && listState.canScrollForward, + icon = { + Icon( + imageVector = status.icon(), + contentDescription = null, + ) + }, + text = { + Text(text = stringResource(status.stringRes(type))) + } + ) + } } } ) { diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt index e0d96ec..3693506 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/component/CollapsingToolbar.kt @@ -33,6 +33,7 @@ import dev.datlag.aniflow.anilist.MediumRepository import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.common.notPreferred import dev.datlag.aniflow.common.preferred +import dev.datlag.aniflow.other.rememberInstantAppHelper import dev.datlag.aniflow.settings.model.AppSettings import dev.datlag.aniflow.ui.custom.shareHandler import dev.datlag.tooling.compose.ifFalse @@ -169,9 +170,10 @@ fun CollapsingToolbar( val mediumState by mediumFlow.collectAsStateWithLifecycle(null) val siteUrl by siteUrlFlow.collectAsStateWithLifecycle(initialMedium.siteUrl) val shareHandler = shareHandler() + val instantAppHelper = rememberInstantAppHelper() AnimatedVisibility( - visible = mediumState is MediumRepository.State.Success, + visible = mediumState is MediumRepository.State.Success && !instantAppHelper.isInstantApp, enter = fadeIn(), exit = fadeOut() ) { diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt index d5160ee..9a94460 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/medium/dialog/character/CharacterDialog.kt @@ -27,6 +27,7 @@ import dev.datlag.aniflow.LocalEdgeToEdge import dev.datlag.aniflow.SharedRes import dev.datlag.aniflow.anilist.CharacterRepository import dev.datlag.aniflow.common.* +import dev.datlag.aniflow.other.rememberInstantAppHelper import dev.datlag.aniflow.ui.navigation.screen.medium.component.TranslateButton import dev.datlag.tooling.compose.ifFalse import dev.datlag.tooling.compose.ifTrue @@ -62,6 +63,7 @@ fun CharacterDialog(component: CharacterComponent) { ) { val image by component.image.collectAsStateWithLifecycle(component.initialChar.image) val state by component.state.collectAsStateWithLifecycle(null) + val instantAppHelper = rememberInstantAppHelper() this@ModalBottomSheet.AnimatedVisibility( modifier = Modifier.align(Alignment.CenterStart), @@ -106,7 +108,7 @@ fun CharacterDialog(component: CharacterComponent) { this@ModalBottomSheet.AnimatedVisibility( modifier = Modifier.align(Alignment.CenterEnd), - visible = state is CharacterRepository.State.Success, + visible = state is CharacterRepository.State.Success && !instantAppHelper.isInstantApp, enter = fadeIn(), exit = fadeOut() ) { diff --git a/composeApp/src/commonMain/moko-resources/base/strings.xml b/composeApp/src/commonMain/moko-resources/base/strings.xml index acc80f7..e79ddf3 100644 --- a/composeApp/src/commonMain/moko-resources/base/strings.xml +++ b/composeApp/src/commonMain/moko-resources/base/strings.xml @@ -70,4 +70,5 @@ Dropped Paused Repeating + Install diff --git a/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.ios.kt b/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.ios.kt new file mode 100644 index 0000000..4077ae5 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/dev/datlag/aniflow/other/InstantAppHelper.ios.kt @@ -0,0 +1,17 @@ +package dev.datlag.aniflow.other + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember + +actual data object InstantAppHelper { + actual val isInstantApp: Boolean + get() = false + + actual fun showInstallPrompt() { } + +} + +@Composable +actual fun rememberInstantAppHelper(): InstantAppHelper { + return remember { InstantAppHelper } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6beed23..446ef67 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,6 +26,7 @@ flowredux = "1.2.1" google-identity = "1.1.0" haze = "0.7.1" html-converter = "0.9.5" +instantapps = "18.0.1" kache = "2.1.0" kasechange = "1.4.1" kmpalette = "3.1.0" @@ -83,6 +84,7 @@ google-identity = { group = "com.google.android.libraries.identity.googleid", na haze = { group = "dev.chrisbanes.haze", name = "haze", version.ref = "haze" } haze-materials = { group = "dev.chrisbanes.haze", name = "haze-materials", version.ref = "haze" } html-converter = { group = "be.digitalia.compose.htmlconverter", name = "htmlconverter", version.ref = "html-converter" } +instantapps = { group = "com.google.android.gms", name = "play-services-instantapps", version.ref = "instantapps" } kache = { group = "com.mayakapps.kache", name = "kache", version.ref = "kache" } kasechange = { group = "net.pearx.kasechange", name = "kasechange", version.ref = "kasechange" } kmpalette = { group = "com.kmpalette", name = "kmpalette-core", version.ref = "kmpalette" }