1+ package com.flipcash.app.updates
2+
3+ import androidx.compose.animation.AnimatedVisibility
4+ import androidx.compose.animation.fadeIn
5+ import androidx.compose.animation.fadeOut
6+ import androidx.compose.foundation.layout.Arrangement
7+ import androidx.compose.foundation.layout.Box
8+ import androidx.compose.foundation.layout.Column
9+ import androidx.compose.foundation.layout.fillMaxSize
10+ import androidx.compose.foundation.layout.fillMaxWidth
11+ import androidx.compose.foundation.layout.navigationBarsPadding
12+ import androidx.compose.foundation.layout.padding
13+ import androidx.compose.material.Icon
14+ import androidx.compose.material.Surface
15+ import androidx.compose.material.Text
16+ import androidx.compose.runtime.Composable
17+ import androidx.compose.runtime.CompositionLocalProvider
18+ import androidx.compose.runtime.getValue
19+ import androidx.compose.runtime.remember
20+ import androidx.compose.runtime.rememberCoroutineScope
21+ import androidx.compose.ui.Alignment
22+ import androidx.compose.ui.Modifier
23+ import androidx.compose.ui.res.painterResource
24+ import androidx.compose.ui.res.stringResource
25+ import androidx.compose.ui.text.style.TextAlign
26+ import androidx.compose.ui.tooling.preview.Preview
27+ import androidx.lifecycle.Lifecycle
28+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
29+ import com.flipcash.app.theme.FlipcashDesignSystem
30+ import com.flipcash.features.appupdates.R
31+ import com.getcode.theme.CodeTheme
32+ import com.getcode.ui.biometrics.BiometricsState
33+ import com.getcode.ui.theme.ButtonState
34+ import com.getcode.ui.theme.CodeButton
35+ import com.getcode.ui.theme.CodeScaffold
36+ import com.getcode.ui.utils.RepeatOnLifecycle
37+ import com.google.android.play.core.install.model.InstallStatus
38+ import com.google.android.play.core.install.model.UpdateAvailability
39+ import kotlinx.coroutines.flow.MutableStateFlow
40+ import kotlinx.coroutines.launch
41+
42+ @Composable
43+ fun UpdateRequiredBlockingView (
44+ biometricsState : BiometricsState ,
45+ modifier : Modifier = Modifier ,
46+ ) {
47+ val appUpdater = LocalAppUpdater .current
48+ val availableUpdate by appUpdater.availableUpdate.collectAsStateWithLifecycle()
49+ val composeScope = rememberCoroutineScope()
50+
51+ AnimatedVisibility (
52+ visible = availableUpdate?.isUpdateAvailable == true && biometricsState.passed,
53+ enter = fadeIn(),
54+ exit = fadeOut()
55+ ) {
56+ Surface (
57+ modifier = modifier,
58+ color = CodeTheme .colors.brand.copy(alpha = 0.87f ),
59+ ) {
60+ CodeScaffold (
61+ bottomBar = {
62+ CodeButton (
63+ modifier = Modifier
64+ .fillMaxWidth()
65+ .padding(horizontal = CodeTheme .dimens.inset)
66+ .padding(bottom = CodeTheme .dimens.grid.x1)
67+ .navigationBarsPadding(),
68+ onClick = {
69+ composeScope.launch {
70+ appUpdater.startUpdate()
71+ }
72+ },
73+ text = stringResource(id = R .string.action_unlockCode),
74+ buttonState = ButtonState .Filled
75+ )
76+ }
77+ ) { padding ->
78+ Box (
79+ modifier = Modifier
80+ .fillMaxSize()
81+ .padding(padding)
82+ ) {
83+ Column (
84+ modifier = Modifier
85+ .fillMaxWidth()
86+ .padding(horizontal = CodeTheme .dimens.inset)
87+ .align(Alignment .Center ),
88+ verticalArrangement = Arrangement .spacedBy(CodeTheme .dimens.grid.x4),
89+ horizontalAlignment = Alignment .CenterHorizontally
90+ ) {
91+ Icon (
92+ painter = painterResource(R .drawable.ic_app_update_required),
93+ contentDescription = null ,
94+ )
95+
96+ Column (
97+ modifier = Modifier .fillMaxWidth(),
98+ verticalArrangement = Arrangement .spacedBy(CodeTheme .dimens.grid.x2),
99+ horizontalAlignment = Alignment .CenterHorizontally ,
100+ ) {
101+ Text (
102+ text = stringResource(R .string.title_updateRequired),
103+ style = CodeTheme .typography.textLarge,
104+ color = CodeTheme .colors.textMain,
105+ )
106+ Text (
107+ modifier = Modifier
108+ .fillMaxWidth(0.8f ),
109+ text = stringResource(R .string.subtitle_updateRequired),
110+ style = CodeTheme .typography.textMedium,
111+ color = CodeTheme .colors.textSecondary,
112+ textAlign = TextAlign .Center ,
113+ )
114+ }
115+ }
116+ }
117+ }
118+ }
119+ }
120+
121+ RepeatOnLifecycle (
122+ targetState = Lifecycle .State .RESUMED
123+ ) {
124+ appUpdater.checkForUpdate()
125+ }
126+ }
127+
128+ @Composable
129+ @Preview
130+ private fun PreviewUpdateRequiredView () {
131+ FlipcashDesignSystem {
132+ val appUpdater = remember {
133+ object : AppUpdateController {
134+ override val availableUpdate = MutableStateFlow <UpdateInfo ?>(
135+ UpdateInfo (
136+ updateAvailability = UpdateAvailability .UPDATE_AVAILABLE ,
137+ updatePriority = 5 ,
138+ clientVersionStalenessDays = null ,
139+ bytesDownloaded = 0 ,
140+ totalBytesToDownload = 100 ,
141+ installStatus = InstallStatus .UNKNOWN ,
142+ availableVersionCode = 3000
143+ )
144+ )
145+
146+ override suspend fun checkForUpdate () = Unit
147+
148+ override suspend fun startUpdate (): Result <Unit > = Result .success(Unit )
149+
150+ }
151+ }
152+ CompositionLocalProvider (LocalAppUpdater provides appUpdater) {
153+ UpdateRequiredBlockingView (
154+ biometricsState = BiometricsState (passed = true ),
155+ )
156+ }
157+ }
158+ }
0 commit comments