From 4794b400d9ad2c5c8e3ad22737838a275dc5081d Mon Sep 17 00:00:00 2001 From: sameerasw Date: Mon, 2 Mar 2026 22:30:11 +0530 Subject: [PATCH 1/6] feat: Improved your device detection and display --- .../ui/activities/YourAndroidActivity.kt | 38 ++- .../ui/components/DeviceHeroCard.kt | 270 +++++++++--------- .../essentials/utils/DeviceImageMapper.kt | 6 +- .../essentials/utils/GSMArenaService.kt | 14 +- 4 files changed, 180 insertions(+), 148 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt index 263797c3f..e41839633 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt @@ -90,19 +90,37 @@ class YourAndroidViewModel : ViewModel() { viewModelScope.launch { _isSpecsLoading.value = true val specs = withContext(Dispatchers.IO) { - // Remove generic terms from the hardware name - val brand = deviceInfo.manufacturer.replace("Google", "") - .replace("samsung", "Samsung") - .trim() + val manufacturer = deviceInfo.manufacturer val model = deviceInfo.model - .replace("Pixel", "") - .replace("Galaxy", "") - .trim() + val deviceName = deviceInfo.deviceName + val deviceCodename = deviceInfo.device + + // Generate a prioritized list of search queries + val queries = mutableListOf() + + // 1. Marketing name (Manufacturer + Model) + if (model.contains(manufacturer, ignoreCase = true)) { + queries.add(model) + } else { + queries.add("$manufacturer $model") + } + + // 2. Model number directly if it's different from marketing name + if (!queries.contains(model)) { + queries.add(model) + } - // Fallback to simpler search - val searchBrand = if(brand.isEmpty()) deviceInfo.manufacturer else brand + // 3. User-defined device name (sometimes it's the marketing name) + if (deviceName.isNotBlank() && !queries.contains(deviceName)) { + queries.add(deviceName) + } - GSMArenaService.fetchSpecs(brand = searchBrand, model = model) + // 4. Device codename (e.g., "shiba", "a51") + if (deviceCodename.isNotBlank() && !queries.contains(deviceCodename)) { + queries.add(deviceCodename) + } + + GSMArenaService.fetchSpecs(*queries.toTypedArray()) } _deviceSpecs.value = specs _isSpecsLoading.value = false diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt index 587b1f50c..3549865c8 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt @@ -50,7 +50,12 @@ fun DeviceHeroCard( modifier: Modifier = Modifier ) { val imageUrls = deviceSpecs?.imageUrls ?: emptyList() - val pageCount = 1 + imageUrls.size + val isPixel = deviceInfo.manufacturer.contains("Google", ignoreCase = true) + + // Only show the illustration page if it's a Pixel AND we have a mapping + val illustrationRes = DeviceImageMapper.getDeviceDrawable(deviceInfo.model) + val showIllustration = isPixel && illustrationRes != 0 + val pageCount = (if (showIllustration) 1 else 0) + imageUrls.size val pagerState = rememberPagerState(pageCount = { pageCount }) Column( @@ -59,80 +64,79 @@ fun DeviceHeroCard( .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Box( - modifier = Modifier - .graphicsLayer { - translationY = imageOffset().toPx() - } - .fillMaxWidth() - .height(480.dp) - .clip(MaterialTheme.shapes.medium), - contentAlignment = Alignment.Center - ) { - HorizontalPager( - state = pagerState, - modifier = Modifier.fillMaxSize(), - verticalAlignment = Alignment.CenterVertically - ) { page -> - Box( + if (pageCount > 0) { + Box( + modifier = Modifier + .graphicsLayer { + translationY = imageOffset().toPx() + } + .fillMaxWidth() + .height(480.dp) + .clip(MaterialTheme.shapes.medium), + contentAlignment = Alignment.Center + ) { + HorizontalPager( + state = pagerState, modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - if (page == 0) { - // stylized vector - Icon( - painter = painterResource( - id = DeviceImageMapper.getDeviceDrawable( - deviceInfo.model - ) - ), - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - modifier = Modifier - .fillMaxHeight(0.85f) - .fillMaxWidth(0.85f) - ) - } else { - // real image from gsmarena - AsyncImage( - model = imageUrls[page - 1], - contentDescription = "Device Image", - contentScale = ContentScale.Fit, - modifier = Modifier - .fillMaxHeight(0.85f) - .fillMaxWidth(0.85f) - .clip(RoundedCornerShape(24.dp)) - .shimmer() - ) + verticalAlignment = Alignment.CenterVertically + ) { page -> + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + if (showIllustration && page == 0) { + // stylized vector + Icon( + painter = painterResource(id = illustrationRes), + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier + .fillMaxHeight(0.85f) + .fillMaxWidth(0.85f) + ) + } else { + // real image from gsmarena + val imageIndex = if (showIllustration) page - 1 else page + AsyncImage( + model = imageUrls[imageIndex], + contentDescription = "Device Image", + contentScale = ContentScale.Fit, + modifier = Modifier + .fillMaxHeight(0.85f) + .fillMaxWidth(0.85f) + .clip(RoundedCornerShape(24.dp)) + .shimmer() + ) + } } } - } - // Page Indicator dots - if (pageCount > 1) { - Row( - Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 16.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - repeat(pageCount) { iteration -> - val color = if (pagerState.currentPage == iteration) - MaterialTheme.colorScheme.primary - else - MaterialTheme.colorScheme.primary.copy(alpha = 0.2f) - Box( - modifier = Modifier - .size(8.dp) - .clip(MaterialTheme.shapes.small) - .background(color) - ) + // Page Indicator dots + if (pageCount > 1) { + Row( + Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 16.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + repeat(pageCount) { iteration -> + val color = if (pagerState.currentPage == iteration) + MaterialTheme.colorScheme.primary + else + MaterialTheme.colorScheme.primary.copy(alpha = 0.2f) + Box( + modifier = Modifier + .size(8.dp) + .clip(MaterialTheme.shapes.small) + .background(color) + ) + } } } } - } - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(24.dp)) + } // User-set Device Name Text( @@ -172,79 +176,81 @@ fun DeviceHeroCard( }, ) { - - Row( - modifier = Modifier - .background( - MaterialTheme.colorScheme.surfaceContainerHighest, - shape = Shapes.extraSmall - ) - .fillMaxWidth() - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { + val androidLogoRes = DeviceImageMapper.getAndroidLogo(deviceInfo) + if (isPixel && androidLogoRes != 0) { Row( + modifier = Modifier + .background( + MaterialTheme.colorScheme.surfaceContainerHighest, + shape = Shapes.extraSmall + ) + .fillMaxWidth() + .padding(16.dp), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) + horizontalArrangement = Arrangement.Center ) { - Icon( - painter = painterResource(id = DeviceImageMapper.getAndroidLogo(deviceInfo)), - contentDescription = null, - tint = Color.Unspecified, - modifier = Modifier.size(56.dp) - ) - Column(horizontalAlignment = Alignment.Start) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = "Android ${deviceInfo.androidVersion} (${ - DeviceUtils.getOSName( - deviceInfo.sdkInt, - deviceInfo.osCodename - ) - })", - style = MaterialTheme.typography.bodyLarge, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.primary - ) - val isBeta = deviceInfo.buildTag.lowercase().contains("beta") - val isCanary = deviceInfo.buildTag.lowercase().contains("canary") + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Icon( + painter = painterResource(id = androidLogoRes), + contentDescription = null, + tint = Color.Unspecified, + modifier = Modifier.size(56.dp) + ) + Column(horizontalAlignment = Alignment.Start) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = "Android ${deviceInfo.androidVersion} (${ + DeviceUtils.getOSName( + deviceInfo.sdkInt, + deviceInfo.osCodename + ) + })", + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.primary + ) + val isBeta = deviceInfo.buildTag.lowercase().contains("beta") + val isCanary = deviceInfo.buildTag.lowercase().contains("canary") - if (isBeta || isCanary) { - Spacer(modifier = Modifier.size(8.dp)) - Box( - modifier = Modifier - .background( - MaterialTheme.colorScheme.primary, - shape = MaterialTheme.shapes.large + if (isBeta || isCanary) { + Spacer(modifier = Modifier.size(8.dp)) + Box( + modifier = Modifier + .background( + MaterialTheme.colorScheme.primary, + shape = MaterialTheme.shapes.large + ) + .padding(horizontal = 6.dp, vertical = 2.dp) + ) { + Text( + text = if (isCanary) "CANARY" else "BETA", + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onPrimary ) - .padding(horizontal = 6.dp, vertical = 2.dp) - ) { - Text( - text = if (isCanary) "CANARY" else "BETA", - style = MaterialTheme.typography.labelSmall, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onPrimary - ) + } } } + Text( + text = "API ${deviceInfo.sdkInt} • Patch: ${ + DeviceUtils.formatSecurityPatch( + deviceInfo.securityPatch + ) + }", + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Text( + text = "Build: ${deviceInfo.display}", + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.8f) + ) } - Text( - text = "API ${deviceInfo.sdkInt} • Patch: ${ - DeviceUtils.formatSecurityPatch( - deviceInfo.securityPatch - ) - }", - style = MaterialTheme.typography.labelMedium, - fontWeight = FontWeight.Medium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - Text( - text = "Build: ${deviceInfo.display}", - style = MaterialTheme.typography.labelSmall, - fontWeight = FontWeight.Normal, - color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.8f) - ) } } } diff --git a/app/src/main/java/com/sameerasw/essentials/utils/DeviceImageMapper.kt b/app/src/main/java/com/sameerasw/essentials/utils/DeviceImageMapper.kt index 72edce3b6..34556d7f0 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/DeviceImageMapper.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/DeviceImageMapper.kt @@ -33,9 +33,9 @@ object DeviceImageMapper { R.drawable.pixel_9pro_9proxl_10_10pro_10proxl m.contains("pixel 9") -> R.drawable.pixel_9 - + // Default fallback - else -> R.drawable.rounded_android_24 + else -> 0 } } @@ -53,7 +53,7 @@ object DeviceImageMapper { osName.contains("android 14") || osName.contains("upside") || sdk >= 34 -> R.drawable.android14 osName.contains("android 13") || osName.contains("tiramisu") || sdk >= 33 -> R.drawable.android13 osName.contains("android 12") || osName.contains("snow cone") || sdk >= 31 -> R.drawable.android12 - else -> R.drawable.rounded_android_24 + else -> 0 } } } diff --git a/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt b/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt index bc32610f9..22808d7e4 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt @@ -9,10 +9,18 @@ import org.jsoup.nodes.Document object GSMArenaService { private const val BASE_URL = "https://www.gsmarena.com" - fun fetchSpecs(brand: String, model: String): DeviceSpecs? { + fun fetchSpecs(vararg queries: String): DeviceSpecs? { + for (query in queries) { + val specs = tryFetchSpecs(query) + if (specs != null) return specs + } + return null + } + + private fun tryFetchSpecs(query: String): DeviceSpecs? { return try { - val query = "$brand $model".replace(" ", "+") - val searchUrl = "$BASE_URL/results.php3?sQuickSearch=yes&sName=$query" + val formattedQuery = query.replace(" ", "+") + val searchUrl = "$BASE_URL/results.php3?sQuickSearch=yes&sName=$formattedQuery" val searchDoc: Document = Jsoup.connect(searchUrl) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36") From e0d67973cf7ee69f07649a2a56980b756752fb5f Mon Sep 17 00:00:00 2001 From: sameerasw Date: Mon, 2 Mar 2026 22:30:45 +0530 Subject: [PATCH 2/6] feat: Improved your android api call and scraping --- .../ui/activities/YourAndroidActivity.kt | 6 +- .../essentials/utils/GSMArenaService.kt | 62 ++++++++++++++++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt b/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt index e41839633..ed65562c8 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/activities/YourAndroidActivity.kt @@ -120,7 +120,11 @@ class YourAndroidViewModel : ViewModel() { queries.add(deviceCodename) } - GSMArenaService.fetchSpecs(*queries.toTypedArray()) + GSMArenaService.fetchSpecs( + preferredName = manufacturer, + preferredModel = model, + queries = queries.toTypedArray() + ) } _deviceSpecs.value = specs _isSpecsLoading.value = false diff --git a/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt b/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt index 22808d7e4..f3d3ae3e2 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/GSMArenaService.kt @@ -9,15 +9,23 @@ import org.jsoup.nodes.Document object GSMArenaService { private const val BASE_URL = "https://www.gsmarena.com" - fun fetchSpecs(vararg queries: String): DeviceSpecs? { + fun fetchSpecs( + preferredName: String, + preferredModel: String, + vararg queries: String + ): DeviceSpecs? { for (query in queries) { - val specs = tryFetchSpecs(query) + val specs = tryFetchSpecs(query, preferredName, preferredModel) if (specs != null) return specs } return null } - private fun tryFetchSpecs(query: String): DeviceSpecs? { + private fun tryFetchSpecs( + query: String, + preferredName: String, + preferredModel: String + ): DeviceSpecs? { return try { val formattedQuery = query.replace(" ", "+") val searchUrl = "$BASE_URL/results.php3?sQuickSearch=yes&sName=$formattedQuery" @@ -27,12 +35,21 @@ object GSMArenaService { .timeout(30000) .get() - val firstDeviceElement = searchDoc.select(".makers li").firstOrNull() ?: return null - val firstDevicePath = firstDeviceElement.select("a").attr("href") - val searchThumbnail = firstDeviceElement.select("img").attr("src") + val results = searchDoc.select(".makers li") + if (results.isEmpty()) return null + + val bestMatchingElement = results.firstOrNull { element -> + val deviceName = element.select("span").text() + isBetterMatch(deviceName, preferredName, preferredModel) + } ?: results.first() + + val devicePath = bestMatchingElement?.select("a")?.attr("href") ?: "" + val searchThumbnail = bestMatchingElement?.select("img")?.attr("src") ?: "" + + if (devicePath.isBlank()) return null val deviceUrl = - if (firstDevicePath.startsWith("/")) "$BASE_URL$firstDevicePath" else "$BASE_URL/$firstDevicePath" + if (devicePath.startsWith("/")) "$BASE_URL$devicePath" else "$BASE_URL/$devicePath" val deviceDoc: Document = Jsoup.connect(deviceUrl) .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36") @@ -111,4 +128,35 @@ object GSMArenaService { null } } + + private fun isBetterMatch( + foundName: String, + preferredName: String, + preferredModel: String + ): Boolean { + val found = foundName.lowercase() + val prefName = preferredName.lowercase() + val prefModel = preferredModel.lowercase() + + val variants = listOf("pro", "max", "plus", "xl", "ultra", "fold", "flip", "power", "neo", "gt", "lite", "ace", "prime", "edge") + for (variant in variants) { + if (found.contains(variant) && !prefName.contains(variant) && !prefModel.contains(variant)) { + return false + } + } + + if (found.contains(prefName) || found.contains(prefModel)) { + val modelIndex = found.indexOf(prefName).takeIf { it != -1 } ?: found.indexOf(prefModel) + if (modelIndex != -1) { + val afterModel = found.substring(modelIndex + (if (found.contains(prefName)) prefName.length else prefModel.length)).trim() + if (afterModel.isNotEmpty()) { + val firstWord = afterModel.split(" ").firstOrNull() ?: "" + if (variants.contains(firstWord)) return false + } + } + return true + } + + return false + } } From bbd132624f1612b965c3f5ad99ea797e2c0fde9d Mon Sep 17 00:00:00 2001 From: sameerasw Date: Mon, 2 Mar 2026 22:35:59 +0530 Subject: [PATCH 3/6] feat: Remove beta tag from your android --- .../ui/components/DeviceHeroCard.kt | 21 ---------- .../sameerasw/essentials/utils/DeviceUtils.kt | 41 +------------------ 2 files changed, 1 insertion(+), 61 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt b/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt index 3549865c8..6cb2789c1 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/components/DeviceHeroCard.kt @@ -212,27 +212,6 @@ fun DeviceHeroCard( fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.primary ) - val isBeta = deviceInfo.buildTag.lowercase().contains("beta") - val isCanary = deviceInfo.buildTag.lowercase().contains("canary") - - if (isBeta || isCanary) { - Spacer(modifier = Modifier.size(8.dp)) - Box( - modifier = Modifier - .background( - MaterialTheme.colorScheme.primary, - shape = MaterialTheme.shapes.large - ) - .padding(horizontal = 6.dp, vertical = 2.dp) - ) { - Text( - text = if (isCanary) "CANARY" else "BETA", - style = MaterialTheme.typography.labelSmall, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colorScheme.onPrimary - ) - } - } } Text( text = "API ${deviceInfo.sdkInt} • Patch: ${ diff --git a/app/src/main/java/com/sameerasw/essentials/utils/DeviceUtils.kt b/app/src/main/java/com/sameerasw/essentials/utils/DeviceUtils.kt index fcdef2629..df14d766b 100644 --- a/app/src/main/java/com/sameerasw/essentials/utils/DeviceUtils.kt +++ b/app/src/main/java/com/sameerasw/essentials/utils/DeviceUtils.kt @@ -51,7 +51,7 @@ object DeviceUtils { activityManager.getMemoryInfo(memoryInfo) val buildId = Build.DISPLAY - val buildInfo = findBuildInfo(context, buildId) ?: getBetaDetailsFromPrefix(buildId) + val buildInfo = findBuildInfo(context, buildId) val deviceSecurityPatch = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) Build.VERSION.SECURITY_PATCH else "Unknown" @@ -80,45 +80,6 @@ object DeviceUtils { ) } - private fun getBetaDetailsFromPrefix(buildId: String): org.json.JSONObject? { - val build = buildId.uppercase() - val details = org.json.JSONObject() - return when { - build.startsWith("UPB") || build.startsWith("U1B") -> { - details.put("version", "Android 15 Beta") - details.put("tag", "Beta Prefix") - details - } - - build.startsWith("AP11") || build.startsWith("AP21") || build.startsWith("AP31") -> { - details.put("version", "Android 16 Beta") - details.put("tag", "Beta Prefix") - details - } - - build.startsWith("BP") -> { - // Check if it's likely a QPR beta based on the request (BPxx) - details.put("version", "Android 16 QPR Beta") - details.put("tag", "Platform/QPR Beta") - details - } - - build.startsWith("CP") -> { - details.put("version", "Android 17 Beta") - details.put("tag", "Developer Beta") - details - } - - build.startsWith("ZP") -> { - details.put("version", "Android Canary") - details.put("tag", "Canary Build") - details - } - - else -> null - } - } - private fun findBuildInfo(context: Context, buildId: String): org.json.JSONObject? { return try { val jsonString = From 5e5b77da9908ab19f5e0574c4eccddb434a7f164 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Mon, 2 Mar 2026 22:48:18 +0530 Subject: [PATCH 4/6] feat: Your Android return animation --- .../ui/composables/SetupFeatures.kt | 38 +++++++++++++++---- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt index 6750f9777..719d75a38 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt @@ -33,6 +33,8 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -72,6 +74,7 @@ import com.sameerasw.essentials.utils.BiometricSecurityHelper import com.sameerasw.essentials.utils.HapticUtil import com.sameerasw.essentials.viewmodels.MainViewModel import kotlinx.coroutines.delay +import kotlinx.coroutines.launch private const val FEATURE_MAPS_POWER_SAVING = R.string.feat_maps_power_saving_title @@ -783,7 +786,8 @@ fun SetupFeatures( var isFocused by remember { mutableStateOf(false) } val pullRefreshState = rememberPullToRefreshState() - var isRefreshing by remember { mutableStateOf(false) } + var isRefreshing by rememberSaveable { mutableStateOf(false) } + val scope = rememberCoroutineScope() val allFeatures = FeatureRegistry.ALL_FEATURES @@ -796,12 +800,32 @@ fun SetupFeatures( } } + val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current + var shouldResetRefreshing by rememberSaveable { mutableStateOf(false) } + + androidx.compose.runtime.DisposableEffect(lifecycleOwner) { + val observer = androidx.lifecycle.LifecycleEventObserver { _, event -> + if (event == androidx.lifecycle.Lifecycle.Event.ON_RESUME) { + if (shouldResetRefreshing) { + scope.launch { + delay(200) + isRefreshing = false + shouldResetRefreshing = false + } + } + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } + LaunchedEffect(isRefreshing) { if (isRefreshing) { HapticUtil.performUIHaptic(view) context.startActivity(Intent(context, YourAndroidActivity::class.java)) - delay(500) - isRefreshing = false + shouldResetRefreshing = true } } @@ -845,11 +869,11 @@ fun SetupFeatures( Spacer(modifier = Modifier.height(contentPadding.calculateTopPadding())) val deviceInfo = remember { DeviceUtils.getDeviceInfo(context) } - val fraction = pullRefreshState.distanceFraction - val thresholdPassed = fraction >= 1f + val displayFraction = if (isRefreshing) 1f else pullRefreshState.distanceFraction + val thresholdPassed = displayFraction >= 1f val cardExpansion by androidx.compose.animation.core.animateDpAsState( - targetValue = 120.dp * fraction.coerceIn(0f, 1f), + targetValue = 120.dp * displayFraction.coerceIn(0f, 1f), animationSpec = androidx.compose.animation.core.spring(stiffness = androidx.compose.animation.core.Spring.StiffnessMediumLow), label = "cardExpansion" ) @@ -903,7 +927,7 @@ fun SetupFeatures( imageVector = androidx.compose.material.icons.Icons.Rounded.KeyboardArrowDown, contentDescription = null, modifier = Modifier.size(24.dp).graphicsLayer { - rotationZ = (fraction * 180f).coerceIn(0f, 180f) + rotationZ = (displayFraction * 180f).coerceIn(0f, 180f) }, tint = contentColor ) From b279146e521eef1bb5179b07cd2afd5423626a76 Mon Sep 17 00:00:00 2001 From: sameerasw Date: Mon, 2 Mar 2026 23:05:42 +0530 Subject: [PATCH 5/6] feat: Expandable your android section improvements --- .../ui/composables/SetupFeatures.kt | 77 +++++++++++++++---- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt b/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt index 719d75a38..21c047526 100644 --- a/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt +++ b/app/src/main/java/com/sameerasw/essentials/ui/composables/SetupFeatures.kt @@ -7,6 +7,7 @@ import android.content.Context import android.content.Intent import android.provider.Settings import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -866,11 +867,10 @@ fun SetupFeatures( verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start ) { - Spacer(modifier = Modifier.height(contentPadding.calculateTopPadding())) - val deviceInfo = remember { DeviceUtils.getDeviceInfo(context) } val displayFraction = if (isRefreshing) 1f else pullRefreshState.distanceFraction val thresholdPassed = displayFraction >= 1f + val statusBarPadding = contentPadding.calculateTopPadding() val cardExpansion by androidx.compose.animation.core.animateDpAsState( targetValue = 120.dp * displayFraction.coerceIn(0f, 1f), @@ -902,6 +902,30 @@ fun SetupFeatures( label = "borderColor" ) + val chevronAlpha by androidx.compose.animation.core.animateFloatAsState( + targetValue = if (thresholdPassed) 0f else 1f, + animationSpec = androidx.compose.animation.core.tween(durationMillis = 300), + label = "chevronAlpha" + ) + + val chevronWidth by androidx.compose.animation.core.animateDpAsState( + targetValue = if (thresholdPassed) 0.dp else 24.dp + 8.dp, // size + spacer + animationSpec = androidx.compose.animation.core.tween(durationMillis = 300), + label = "chevronWidth" + ) + + val fontWeight by androidx.compose.animation.core.animateIntAsState( + targetValue = if (thresholdPassed) 700 else 500, + animationSpec = androidx.compose.animation.core.tween(durationMillis = 300), + label = "fontWeight" + ) + + val textScale by androidx.compose.animation.core.animateFloatAsState( + targetValue = if (thresholdPassed) 1.5f else 1f, + animationSpec = androidx.compose.animation.core.tween(durationMillis = 300), + label = "textScale" + ) + // My Android OutlinedCard( onClick = { @@ -911,9 +935,14 @@ fun SetupFeatures( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) - .padding(top = 16.dp, bottom = 0.dp) - .height(64.dp + cardExpansion), - shape = MaterialTheme.shapes.extraLarge, + .padding(top = 0.dp, bottom = 0.dp) + .height(64.dp + statusBarPadding + cardExpansion), + shape = RoundedCornerShape( + topStart = 0.dp, + topEnd = 0.dp, + bottomStart = 28.dp, + bottomEnd = 28.dp + ), colors = CardDefaults.outlinedCardColors( containerColor = containerColor, contentColor = if (thresholdPassed) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface @@ -922,20 +951,36 @@ fun SetupFeatures( ) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { + Spacer(modifier = Modifier.height(statusBarPadding)) Row(verticalAlignment = Alignment.CenterVertically) { - Icon( - imageVector = androidx.compose.material.icons.Icons.Rounded.KeyboardArrowDown, - contentDescription = null, - modifier = Modifier.size(24.dp).graphicsLayer { - rotationZ = (displayFraction * 180f).coerceIn(0f, 180f) - }, - tint = contentColor - ) - Spacer(modifier = Modifier.width(8.dp)) + Box( + modifier = Modifier + .width(chevronWidth) + .graphicsLayer { alpha = chevronAlpha } + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + imageVector = androidx.compose.material.icons.Icons.Rounded.KeyboardArrowDown, + contentDescription = null, + modifier = Modifier + .size(24.dp) + .graphicsLayer { + rotationZ = (displayFraction * 180f).coerceIn(0f, 180f) + }, + tint = contentColor + ) + Spacer(modifier = Modifier.width(8.dp)) + } + } Text( text = deviceInfo.deviceName, - style = MaterialTheme.typography.titleMedium, - fontWeight = androidx.compose.ui.text.font.FontWeight.Medium, + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = androidx.compose.ui.text.font.FontWeight(fontWeight) + ), + modifier = Modifier.graphicsLayer { + scaleX = textScale + scaleY = textScale + }, color = if (thresholdPassed) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface ) } From 81b74297322a5c5cddb882bf81410a8034a641ef Mon Sep 17 00:00:00 2001 From: sameerasw Date: Mon, 2 Mar 2026 23:05:57 +0530 Subject: [PATCH 6/6] feat: Update search placeholder --- app/src/main/res/values-ach/strings.xml | 2 +- app/src/main/res/values-af/strings.xml | 2 +- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-ca/strings.xml | 2 +- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-da/strings.xml | 2 +- app/src/main/res/values-el/strings.xml | 2 +- app/src/main/res/values-en/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fi/strings.xml | 2 +- app/src/main/res/values-he/strings.xml | 2 +- app/src/main/res/values-hu/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-no/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values-pt/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-si/strings.xml | 2 +- app/src/main/res/values-sr/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 2 +- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 2 +- app/src/main/res/values-zh/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 27 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/src/main/res/values-ach/strings.xml b/app/src/main/res/values-ach/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-ach/strings.xml +++ b/app/src/main/res/values-ach/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-af/strings.xml b/app/src/main/res/values-af/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-af/strings.xml +++ b/app/src/main/res/values-af/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 3ba88c459..c3fce9dde 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index dc5716dfc..217506ae3 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index fa4f5a665..103aa3aa1 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -315,7 +315,7 @@ Le app bloccate richiederanno l\'autenticazione all\'apertura. L\'app rimarrà s Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 30f10b7d6..19b651aed 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 8ae04e85d..7c8d4d225 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 088160720..8f81fb981 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 39bafee28..d01eaa496 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 0342bf409..f7cc2f99a 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 895200d62..0aa49eb1b 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -314,7 +314,7 @@ Visuals System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b0d22040e..53297f608 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -330,7 +330,7 @@ System - Search for Tools, Mods and Tweaks + Search Essentials No results for \"%1$s\" Search Results %1$s requires following permissions