diff --git a/app/src/main/java/org/pixysos/updater/core/data/model/PixysBuildProperty.kt b/app/src/main/java/org/pixysos/updater/core/data/model/PixysBuildProperty.kt index e64693b..abae0ce 100644 --- a/app/src/main/java/org/pixysos/updater/core/data/model/PixysBuildProperty.kt +++ b/app/src/main/java/org/pixysos/updater/core/data/model/PixysBuildProperty.kt @@ -6,5 +6,6 @@ package org.pixysos.updater.core.data.model data class PixysBuildProperty( val device: String? = null, val releaseType: String? = null, - val buildEdition: String? = null + val buildEdition: String? = null, + val securityPatchDate: String? = null ) diff --git a/app/src/main/java/org/pixysos/updater/core/data/repository/BuildPropertiesRepository.kt b/app/src/main/java/org/pixysos/updater/core/data/repository/BuildPropertiesRepository.kt index 9082129..3118c6f 100644 --- a/app/src/main/java/org/pixysos/updater/core/data/repository/BuildPropertiesRepository.kt +++ b/app/src/main/java/org/pixysos/updater/core/data/repository/BuildPropertiesRepository.kt @@ -22,4 +22,9 @@ interface BuildPropertiesRepository { * Returns the build variant. Can be either "vanilla" or "gapps". */ fun getBuildEdition(): String? + + /** + * Returns the security patch date. + */ + fun getSecurityPatchDate(): String? } \ No newline at end of file diff --git a/app/src/main/java/org/pixysos/updater/core/data/repository/PixysBuildPropertiesRepository.kt b/app/src/main/java/org/pixysos/updater/core/data/repository/PixysBuildPropertiesRepository.kt index bb587d5..12770bb 100644 --- a/app/src/main/java/org/pixysos/updater/core/data/repository/PixysBuildPropertiesRepository.kt +++ b/app/src/main/java/org/pixysos/updater/core/data/repository/PixysBuildPropertiesRepository.kt @@ -3,6 +3,8 @@ package org.pixysos.updater.core.data.repository import android.os.SystemProperties import org.pixysos.updater.BuildConfig import org.pixysos.updater.core.data.model.PixysBuildProperty +import java.text.SimpleDateFormat +import java.util.Locale import javax.inject.Inject /** @@ -15,15 +17,32 @@ class PixysBuildPropertiesRepository @Inject constructor( get() = PixysBuildProperty( device = getDeviceCodename(), releaseType = getReleaseType(), - buildEdition = getBuildEdition() + buildEdition = getBuildEdition(), + securityPatchDate = getSecurityPatchDate() ) - override fun getDeviceCodename(): String? = - SystemProperties.get(BuildConfig.PROP_DEVICE, "N/A") + override fun getDeviceCodename(): String { + val device = SystemProperties.get(BuildConfig.PROP_VENDOR_DEVICE, "N/A") + val model = SystemProperties.get(BuildConfig.PROP_VENDOR_MODEL, "N/A") + + if (device == "N/A" || model == "N/A") { + return "N/A" + } + + return "$model ($device)" + } override fun getReleaseType(): String? = SystemProperties.get(BuildConfig.PROP_RELEASE_TYPE, "unofficial") override fun getBuildEdition(): String? = SystemProperties.get(BuildConfig.PROP_BUILD_EDITION, "N/A") + + override fun getSecurityPatchDate(): String? { + val securityPatchDateString = + SystemProperties.get(BuildConfig.PROP_SECURITY_PATCH_LEVEL, "N/A") + val securityPatchDate = + SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(securityPatchDateString) + return SimpleDateFormat("MMMM dd, yyyy", Locale.getDefault()).format(securityPatchDate) + } } \ No newline at end of file diff --git a/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesScreen.kt b/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesScreen.kt index bf309c9..2bd5baa 100644 --- a/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesScreen.kt +++ b/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesScreen.kt @@ -1,6 +1,5 @@ package org.pixysos.updater.feature.updates -import android.util.Log import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.animation.AnimatedContent @@ -12,9 +11,6 @@ import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.rememberScrollableState -import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -28,9 +24,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExtendedFloatingActionButton @@ -41,7 +34,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.rememberNestedScrollInteropConnection @@ -51,6 +43,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.pixysos.updater.R +import org.pixysos.updater.core.data.model.PixysBuildProperty import org.pixysos.updater.core.designsystem.component.UpdaterTopAppBar import org.pixysos.updater.core.designsystem.theme.GreenLooksGood @@ -61,7 +54,7 @@ fun UpdatesScreen( viewModel: UpdatesViewModel = hiltViewModel() ) { val updatesUiState: UpdatesUiState by viewModel.uiState.collectAsStateWithLifecycle() - val scrollState = rememberScrollState() + val deviceInfoSummaryList = viewModel.deviceSummaryList val connection = rememberNestedScrollInteropConnection() Scaffold( @@ -72,14 +65,23 @@ fun UpdatesScreen( ) }, floatingActionButton = { - ExtendedFloatingActionButton(onClick = { /*TODO*/ }, modifier = Modifier.windowInsetsPadding( - WindowInsets.navigationBars)) { - Image(painter = painterResource(id = R.drawable.ic_sync), contentDescription = stringResource( - R.string.check_for_updates_content_desc - ), + ExtendedFloatingActionButton( + onClick = { /*TODO*/ }, modifier = Modifier.windowInsetsPadding( + WindowInsets.navigationBars + ) + ) { + Image( + painter = painterResource(id = R.drawable.ic_sync), + contentDescription = stringResource( + R.string.check_for_updates_content_desc + ), colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface) ) - Text(text = stringResource(R.string.check_updates), style = MaterialTheme.typography.labelLarge, modifier = Modifier.padding(start = 16.dp)) + Text( + text = stringResource(R.string.check_updates), + style = MaterialTheme.typography.labelLarge, + modifier = Modifier.padding(start = 16.dp) + ) } } ) { padding -> @@ -93,12 +95,16 @@ fun UpdatesScreen( is UpdatesUiState.Error -> { } + is UpdatesUiState.Success -> { - UpdateStatusCard(title = stringResource(id = R.string.looks_good), summary = stringResource( - id = R.string.no_updates_available, - ), - updateFound = true) + UpdateStatusCard( + title = stringResource(id = R.string.looks_good), summary = stringResource( + id = R.string.no_updates_available, + ), + updateFound = true + ) } + else -> { UpdateStatusCard( title = stringResource(id = R.string.looks_good), @@ -109,7 +115,14 @@ fun UpdatesScreen( } DeviceInformationSection( - modifier = Modifier.padding(top = 24.dp) + modifier = Modifier.padding(top = 24.dp), + deviceInfoList = deviceInfoSummaryList.mapIndexed { i, summary -> + DeviceInformation( + title = deviceInfoTitles[i], + iconRes = deviceInfoIcons[i], + summary = summary + ) + } ) } } @@ -137,27 +150,35 @@ fun UpdateStatusCard( modifier = modifier, shape = MaterialTheme.shapes.large, ) { - Row (modifier = Modifier.padding(24.dp), - verticalAlignment = Alignment.CenterVertically) { + Row( + modifier = Modifier.padding(24.dp), + verticalAlignment = Alignment.CenterVertically + ) { Image( painter = painterResource(id = R.drawable.ic_verified_user), contentDescription = "Security update good", colorFilter = ColorFilter.tint(color = GreenLooksGood.copy(alpha = if (updateFound) 1f else alpha)), modifier = Modifier.size(40.dp) ) - Column ( + Column( modifier = Modifier .padding(start = 16.dp) .weight(1f), verticalArrangement = Arrangement.Center ) { - AnimatedContent(targetState = title, label = "Title change animation") { targetTitle -> + AnimatedContent( + targetState = title, + label = "Title change animation" + ) { targetTitle -> Text( text = targetTitle, style = MaterialTheme.typography.headlineSmall ) } - AnimatedContent(targetState = summary, label = "Summary change animation") { targetSummary -> + AnimatedContent( + targetState = summary, + label = "Summary change animation" + ) { targetSummary -> Text( text = targetSummary, style = MaterialTheme.typography.bodyLarge, @@ -171,63 +192,48 @@ fun UpdateStatusCard( } -@Composable -fun NoUpdatesAvailable( - modifier: Modifier = Modifier -) { - Card( - modifier = modifier, - shape = MaterialTheme.shapes.large, - ) { - Row (modifier = Modifier.padding(24.dp), - verticalAlignment = Alignment.CenterVertically) { - Image( - painter = painterResource(id = R.drawable.ic_security_update_good), - modifier = Modifier - .clip(CircleShape) - .background(MaterialTheme.colorScheme.background) - .padding(12.dp) - .size(32.dp), - contentDescription = "Security update good", - colorFilter = ColorFilter.tint(color = GreenLooksGood) - ) - Column ( - modifier = Modifier - .padding(start = 16.dp) - .weight(1f), - verticalArrangement = Arrangement.Center - ) { - Text(text = stringResource(id = R.string.looks_good), style = MaterialTheme.typography.headlineSmall) - Text(text = stringResource(id = R.string.no_updates_available), style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.paddingFromBaseline(top = 12.dp), color = MaterialTheme.colorScheme.outline) - } - } - } -} +data class DeviceInformation( + @StringRes val title: Int, + @DrawableRes val iconRes: Int, + val summary: String +) -data class StatusInfo(@StringRes val title: Int, @DrawableRes val iconRes: Int, val summary: String) -val informationItems: List = listOf( - StatusInfo(title = R.string.security_update, R.drawable.ic_shield, "Update from October 05, 2023"), - StatusInfo(title = R.string.device, R.drawable.ic_smartphone, "Pixel 7"), - StatusInfo(title = R.string.pixys_version, R.drawable.ic_perm_device_information, "7.x.x"), +val deviceInfoTitles = listOf( + R.string.security_update, + R.string.device, + R.string.pixys_version +) + +val deviceInfoIcons = listOf( + R.drawable.ic_shield, + R.drawable.ic_smartphone, + R.drawable.ic_perm_device_information ) @Composable fun DeviceInformationSection( modifier: Modifier = Modifier, + deviceInfoList: List ) { LazyColumn( + modifier = modifier, contentPadding = PaddingValues(top = 24.dp, start = 8.dp) ) { item { - Text(text = stringResource(R.string.device_information), modifier = Modifier.padding(bottom = 16.dp)) + Text( + text = stringResource(R.string.device_information), + modifier = Modifier.padding(bottom = 16.dp) + ) } - items(informationItems) { statusInfo -> + items(deviceInfoList) { deviceInfo -> DeviceInformationElement( modifier = Modifier.padding(vertical = 8.dp), - titleRes = statusInfo.title, - summary = statusInfo.summary, - iconRes = statusInfo.iconRes + titleRes = deviceInfo.title, + summary = if (deviceInfo.title != R.string.security_update) deviceInfo.summary else stringResource( + id = R.string.update_from, + deviceInfo.summary + ), + iconRes = deviceInfo.iconRes ) } } @@ -241,9 +247,16 @@ fun DeviceInformationElement( summary: String, contentDescription: String? = null ) { - Row (verticalAlignment = Alignment.CenterVertically) { - Image(painter = painterResource(id = iconRes), contentDescription = contentDescription, colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary)) - Column(modifier = modifier.padding(start = 24.dp), verticalArrangement = Arrangement.Center) { + Row(verticalAlignment = Alignment.CenterVertically) { + Image( + painter = painterResource(id = iconRes), + contentDescription = contentDescription, + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary) + ) + Column( + modifier = modifier.padding(start = 24.dp), + verticalArrangement = Arrangement.Center + ) { Text(stringResource(id = titleRes), style = MaterialTheme.typography.titleLarge) Text( summary, diff --git a/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesViewModel.kt b/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesViewModel.kt index 0b757c3..9b484cd 100644 --- a/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesViewModel.kt +++ b/app/src/main/java/org/pixysos/updater/feature/updates/UpdatesViewModel.kt @@ -3,10 +3,13 @@ package org.pixysos.updater.feature.updates import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import org.pixysos.updater.core.data.model.PixysBuildProperty import org.pixysos.updater.core.data.model.UpdatePackage import org.pixysos.updater.core.data.repository.BuildPropertiesRepository import org.pixysos.updater.core.data.repository.UpdatesRepository @@ -27,6 +30,19 @@ class UpdatesViewModel @Inject constructor( started = SharingStarted.WhileSubscribed(5_000), initialValue = UpdatesUiState.Loading ) + + private val _buildProperty: MutableStateFlow = MutableStateFlow(buildPropertiesRepository.buildProperty) + val buildProperty: StateFlow + get() = _buildProperty.asStateFlow() + + val deviceSummaryList: List + get() = _buildProperty.value.let { property -> + listOf( + property.securityPatchDate!!, + property.device!!, + property.releaseType!! + ) + } } sealed interface UpdatesUiState { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49c5375..c1c8000 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,4 +13,5 @@ Check for updates Check updates Device Information + Update from %1$s \ No newline at end of file diff --git a/secrets.default.properties b/secrets.default.properties index 2522d51..beb52e7 100644 --- a/secrets.default.properties +++ b/secrets.default.properties @@ -1,7 +1,12 @@ BASE_URL="https://api.pixysos.com/ota/v1/" ANDROID_VERSION_NAME="twelve" -# Device properties +# Device properties. PROP_DEVICE="ro.pixys.device" PROP_RELEASE_TYPE="ro.pixys.releasetype" PROP_BUILD_EDITION="ro.pixys.edition" + +# Vendor properties. +PROP_VENDOR_MODEL="ro.product.vendor.model" +PROP_VENDOR_DEVICE="ro.product.vendor.device" +PROP_SECURITY_PATCH_LEVEL="ro.build.version.security_patch" \ No newline at end of file