Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Android version check #2257

Merged
merged 13 commits into from
Jan 7, 2024
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package io.parity.signer.screens.initial.eachstartchecks.osversion

import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SecurityUpdate
import androidx.compose.material.icons.outlined.SettingsBackupRestore
import androidx.compose.material.icons.outlined.UpdateDisabled
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.withAnnotation
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.parity.signer.R
import io.parity.signer.components.base.PrimaryButtonWide
import io.parity.signer.domain.Callback
import io.parity.signer.screens.scan.errors.COMPOSE_URL_TAG_ANNOTATION
import io.parity.signer.ui.theme.SignerNewTheme
import io.parity.signer.ui.theme.SignerTypeface
import io.parity.signer.ui.theme.fill12
import io.parity.signer.ui.theme.fill6
import io.parity.signer.ui.theme.pink300
import io.parity.signer.ui.theme.pink500
import io.parity.signer.ui.theme.textSecondary
import io.parity.signer.ui.theme.textTertiary

@Composable
fun WrongOsVersionNotificationScreen(
currentOSVersion: String,
minimalOsVersion: String,
onProceed: Callback
) {
Column(
modifier = Modifier
.padding(24.dp)
) {
Spacer(Modifier.weight(0.5f))
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
) {
Image(
imageVector = Icons.Outlined.SecurityUpdate,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colors.pink500),
modifier = Modifier
.padding(horizontal = 8.dp)
.size(80.dp)
.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.padding(top = 16.dp))
Text(
modifier = Modifier
.fillMaxWidth(1f),
text = stringResource(R.string.initial_screen_outdated_os_title),
color = MaterialTheme.colors.primary,
style = SignerTypeface.TitleL,
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.padding(top = 16.dp))
Text(
modifier = Modifier
.fillMaxWidth(1f),
text = stringResource(
R.string.initial_screen_outdated_os_description,
currentOSVersion,
minimalOsVersion,
),
color = MaterialTheme.colors.textTertiary,
style = SignerTypeface.BodyL,
textAlign = TextAlign.Center,
)
Spacer(modifier = Modifier.padding(top = 24.dp))

Spacer(modifier = Modifier.padding(top = 8.dp))
PrimaryButtonWide(
modifier = Modifier.padding(vertical = 24.dp),
label = stringResource(R.string.onboarding_skip),
onClicked = onProceed,
)
}
Spacer(Modifier.weight(0.5f))
}
}


@Preview(
name = "light", group = "themes", uiMode = Configuration.UI_MODE_NIGHT_NO,
showBackground = true, backgroundColor = 0xFFFFFFFF,
)
@Preview(
name = "dark", group = "themes", uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true, backgroundColor = 0xFF000000,
)
@Composable
private fun WrongOsVersionNotificationScreenPreview() {
Box(modifier = Modifier.fillMaxSize()) {
SignerNewTheme() {
WrongOsVersionNotificationScreen(
"6.0", "7.1",
onProceed = {},
)
}
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.parity.signer.screens.initial.eachstartchecks.osversion

import android.os.Build
import androidx.lifecycle.ViewModel


class WrongOsVersionViewModel() : ViewModel() {

fun isShouldShow(): Boolean {
return Build.VERSION.SDK_INT < MinRecommendedOsVersion
}

fun getCurrentOSVersion(): String {
return Build.VERSION.RELEASE
}

fun getMinRecommendedOsVersion(): String {
return MinRecommendedOsVersionString
}
}

//todo dmitry set values given my security team
const val MinRecommendedOsVersion: Int = Build.VERSION_CODES.O
const val MinRecommendedOsVersionString: String = "Oreo"
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.parity.signer.screens.initial.firstTimeOnly

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.composable
import androidx.navigation.navigation
import io.parity.signer.screens.initial.eachstartchecks.osversion.WrongOsVersionNotificationScreen
import io.parity.signer.screens.initial.eachstartchecks.osversion.WrongOsVersionViewModel
import io.parity.signer.screens.initial.termsconsent.TermsConsentScreenFull
import io.parity.signer.ui.rootnavigation.MainGraphRoutes


fun NavGraphBuilder.firstTimeOnlyOnboarding(
routePath: String,
navController: NavHostController,
) {
navigation(
route = routePath,
startDestination = FirstTimeOnboarding.osVersionNotification,
) {
composable(route = FirstTimeOnboarding.osVersionNotification) {
val osVersionVM: WrongOsVersionViewModel = viewModel()
if (osVersionVM.isShouldShow()) {
WrongOsVersionNotificationScreen(
currentOSVersion = osVersionVM.getCurrentOSVersion(),
minimalOsVersion = osVersionVM.getMinRecommendedOsVersion(),
onProceed = {
navController.navigate(FirstTimeOnboarding.termsConsentRoute) {
popUpTo(0)
}
}
)
} else {
navController.navigate(FirstTimeOnboarding.termsConsentRoute) {
popUpTo(0)
}
}
}
composable(route = FirstTimeOnboarding.termsConsentRoute) {
TermsConsentScreenFull(
navigateNextScreen = {
navController.navigate(MainGraphRoutes.eachTimeOnboardingRoute) {
popUpTo(0)
}
},
)
}
}
}

private object FirstTimeOnboarding {
const val osVersionNotification = "navigation_point_"

// const val onboardingExplanationRoute = "navigation_onboarding_explanation"
const val termsConsentRoute = "navigation_point_terms_consent"
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fun NavGraphBuilder.splashScreen(globalNavController: NavHostController) {
val viewModel: SplashScreenViewModel = viewModel()
val context = LocalContext.current
LaunchedEffect(Unit) {
if (viewModel.shouldShowOnboarding(context)) {
if (viewModel.shouldShowSingleRunChecks(context)) {
globalNavController.navigate(MainGraphRoutes.firstTimeOnboarding) {
popUpTo(0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import io.parity.signer.screens.initial.termsconsent.OnBoardingViewModel

class SplashScreenViewModel : ViewModel() {

fun shouldShowOnboarding(context: Context): Boolean {
return OnBoardingViewModel.shouldShowOnboarding(context)
fun shouldShowSingleRunChecks(context: Context): Boolean {
return OnBoardingViewModel.shouldShowSingleRunChecks(context)
}

fun isShouldShowAirgap(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class OnBoardingViewModel : ViewModel() {


companion object {
fun shouldShowOnboarding(context: Context): Boolean {
fun shouldShowSingleRunChecks(context: Context): Boolean {
return !context.isDbCreatedAndOnboardingPassed()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import io.parity.signer.domain.Callback

@Composable
fun TermsConsentScreenFull(navigateNextScreen: Callback) {
if (!OnBoardingViewModel.shouldShowOnboarding(LocalContext.current)) {
if (!OnBoardingViewModel.shouldShowSingleRunChecks(LocalContext.current)) {
navigateNextScreen()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navigation
import io.parity.signer.domain.findActivity
import io.parity.signer.screens.initial.eachstartchecks.enableEachStartAppFlow
import io.parity.signer.screens.initial.firstTimeOnly.firstTimeOnlyOnboarding
import io.parity.signer.screens.initial.splash.splashScreen
import io.parity.signer.screens.initial.termsconsent.TermsConsentScreenFull
import kotlinx.coroutines.delay


Expand Down Expand Up @@ -89,29 +86,4 @@ object MainGraphRoutes {
}


fun NavGraphBuilder.firstTimeOnlyOnboarding(
routePath: String,
navController: NavHostController,
) {
navigation(
route = routePath,
startDestination = FirstTimeOnboarding.termsConsentRoute,
) {
composable(route = FirstTimeOnboarding.termsConsentRoute) {
TermsConsentScreenFull(
navigateNextScreen = {
navController.navigate(MainGraphRoutes.eachTimeOnboardingRoute) {
popUpTo(0)
}
},
)
}
}
}

private object FirstTimeOnboarding {
// const val onboardingExplanationRoute = "navigation_onboarding_explanation"
const val termsConsentRoute = "navigation_point_terms_consent"
}


2 changes: 2 additions & 0 deletions android/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@
<string name="add_derived_keys_screen_cta">Confirm and Add Keys</string>
<string name="add_derived_keys_screen_alert_title">Confirm by entering the PIN code</string>
<string name="add_derived_keys_screen_alert_content">To finalize the addition of keys to the key set, please confirm that you have scanned the QR code back into the app by entering the PIN code.</string>
<string name="initial_screen_outdated_os_title">Consider updating operation system before using vault</string>
<string name="initial_screen_outdated_os_description">Current Oos version is %1$s and minimal recommended is %1$s due to known vulnerabilities of old OS versions.</string>

</resources>

Expand Down
Loading