Skip to content

Commit 35108d1

Browse files
nqmgamingclaude
andcommitted
feat(onboarding): add a VirusTotal security page to the tutorial
Explains that any package can be scanned against VirusTotal from the install screen, and that it needs a free API key pasted into Settings > Advanced. Links straight to virustotal.com/gui/my-apikey — the same URL the "no API key" error already cites. Gated behind showVirusTotalTip so only Mobile shows it; the scanner lives in the app module and TV has no Settings screen to paste a key into. OnboardingPage gains actionIcon so each action button can carry its own glyph instead of the hardcoded Tune icon. Strings in en, zh and vi. Verified on device. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 584d1de commit 35108d1

5 files changed

Lines changed: 38 additions & 6 deletions

File tree

app/src/main/java/app/pwhs/universalinstaller/MainActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ class MainActivity : ComponentActivity() {
112112
AppRoute.Onboarding -> OnboardingScreen(
113113
onFinish = { currentRoute = AppRoute.Main },
114114
showXiaomiTip = DeviceCompat.isXiaomi,
115+
showVirusTotalTip = true,
115116
)
116117
AppRoute.Main -> {
117118
LaunchedEffect(Unit) {

core/src/main/java/app/pwhs/core/presentation/onboarding/OnboardingScreen.kt

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ import androidx.compose.foundation.pager.rememberPagerState
2222
import androidx.compose.foundation.shape.CircleShape
2323
import androidx.compose.material.icons.Icons
2424
import androidx.compose.material.icons.automirrored.rounded.ArrowForward
25+
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
2526
import androidx.compose.material.icons.rounded.CheckCircle
27+
import androidx.compose.material.icons.rounded.GppGood
2628
import androidx.compose.material.icons.rounded.InstallMobile
2729
import androidx.compose.material.icons.rounded.Security
2830
import androidx.compose.material.icons.rounded.Tune
@@ -45,6 +47,7 @@ import androidx.compose.ui.Alignment
4547
import androidx.compose.ui.Modifier
4648
import androidx.compose.ui.graphics.vector.ImageVector
4749
import androidx.compose.ui.platform.LocalContext
50+
import androidx.compose.ui.platform.LocalUriHandler
4851
import androidx.compose.ui.res.stringResource
4952
import androidx.compose.ui.text.font.FontWeight
5053
import androidx.compose.ui.text.style.TextAlign
@@ -58,12 +61,16 @@ import androidx.datastore.preferences.core.edit
5861
import androidx.lifecycle.compose.LifecycleResumeEffect
5962
import kotlinx.coroutines.launch
6063

64+
/** Where a free VirusTotal API key comes from — the same URL the scanner error message cites. */
65+
private const val VIRUSTOTAL_API_KEY_URL = "https://www.virustotal.com/gui/my-apikey"
66+
6167
private data class OnboardingPage(
6268
val icon: ImageVector,
6369
val title: String,
6470
val description: String,
6571
/** Optional secondary action rendered under the description (e.g. "Open Developer options"). */
6672
val actionLabel: String? = null,
73+
val actionIcon: ImageVector? = null,
6774
val onAction: (() -> Unit)? = null,
6875
)
6976

@@ -73,14 +80,18 @@ private data class OnboardingPage(
7380
* @param showXiaomiTip inserts an extra page warning about MIUI/HyperOS optimization silently
7481
* blocking installs (issue #104). Callers gate it on [DeviceCompat.isXiaomi]; TV leaves it off
7582
* because the toggle doesn't exist on Xiaomi's TV builds.
83+
* @param showVirusTotalTip inserts a page explaining the VirusTotal scan. Mobile-only — the
84+
* scanner lives in the app module and TV has no Settings screen to paste an API key into.
7685
*/
7786
@Composable
7887
fun OnboardingScreen(
7988
onFinish: () -> Unit,
8089
showXiaomiTip: Boolean = false,
90+
showVirusTotalTip: Boolean = false,
8191
) {
8292
val context = LocalContext.current
8393
val activity = context as? android.app.Activity
94+
val uriHandler = LocalUriHandler.current
8495
// Hide the shortcut when Developer options aren't reachable — the text alone still tells the
8596
// user what to look for.
8697
val developerOptions = remember(showXiaomiTip) {
@@ -102,6 +113,18 @@ fun OnboardingScreen(
102113
description = stringResource(R.string.onboarding_page2_desc),
103114
)
104115
)
116+
if (showVirusTotalTip) {
117+
add(
118+
OnboardingPage(
119+
icon = Icons.Rounded.GppGood,
120+
title = stringResource(R.string.onboarding_virustotal_title),
121+
description = stringResource(R.string.onboarding_virustotal_desc),
122+
actionLabel = stringResource(R.string.onboarding_virustotal_get_key),
123+
actionIcon = Icons.AutoMirrored.Rounded.OpenInNew,
124+
onAction = { uriHandler.openUri(VIRUSTOTAL_API_KEY_URL) },
125+
)
126+
)
127+
}
105128
if (showXiaomiTip) {
106129
add(
107130
OnboardingPage(
@@ -111,6 +134,7 @@ fun OnboardingScreen(
111134
actionLabel = developerOptions?.let {
112135
stringResource(R.string.onboarding_xiaomi_open_developer_options)
113136
},
137+
actionIcon = Icons.Rounded.Tune,
114138
onAction = developerOptions?.let { intent -> { context.startActivity(intent) } },
115139
)
116140
)
@@ -306,12 +330,10 @@ private fun PageContent(
306330
val label = page.actionLabel ?: return@let
307331
Spacer(Modifier.height(32.dp))
308332
OutlinedButton(onClick = action) {
309-
Icon(
310-
Icons.Rounded.Tune,
311-
contentDescription = null,
312-
modifier = Modifier.size(18.dp),
313-
)
314-
Spacer(Modifier.width(8.dp))
333+
page.actionIcon?.let { icon ->
334+
Icon(icon, contentDescription = null, modifier = Modifier.size(18.dp))
335+
Spacer(Modifier.width(8.dp))
336+
}
315337
Text(label)
316338
}
317339
}

core/src/main/res/values-vi/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
<string name="onboarding_page2_desc">Duyệt qua các ứng dụng đã cài đặt, gỡ cài đặt những ứng dụng không mong muốn và quản lý thiết bị của bạn một cách dễ dàng.</string>
1010
<string name="onboarding_page3_title">Cho phép cài đặt</string>
1111
<string name="onboarding_page3_desc">Để cài đặt các gói, ứng dụng cần có quyền cài đặt từ các nguồn không xác định. Nhấn vào nút bên dưới để cấp nó.</string>
12+
<string name="onboarding_virustotal_title">Quét trước khi cài</string>
13+
<string name="onboarding_virustotal_desc">Mọi gói bạn mở đều có thể kiểm tra bằng VirusTotal — hơn 70 engine diệt virus — ngay trong màn hình cài đặt. Nhấn \"Kiểm tra bằng VirusTotal\" và kết quả hiện ra trước khi bạn xác nhận. Tính năng này cần API key miễn phí: lấy ở nút bên dưới rồi dán vào Cài đặt &gt; Nâng cao &gt; VirusTotal API Key.</string>
14+
<string name="onboarding_virustotal_get_key">Lấy API key miễn phí</string>
1215
<string name="onboarding_xiaomi_title">Nếu cài đặt thất bại trên MIUI</string>
1316
<string name="onboarding_xiaomi_desc">Tối ưu hóa MIUI/HyperOS có thể chặn cài đặt từ bên thứ ba mà không báo lý do. Nếu gặp trường hợp đó, hãy mở Tùy chọn nhà phát triển, tắt \"Tối ưu hóa MIUI\", cài đặt ứng dụng rồi bật lại. Khi đang tắt, giao diện hệ thống có thể bị lỗi hiển thị — khởi động lại điện thoại sẽ khắc phục.</string>
1417
<string name="onboarding_xiaomi_open_developer_options">Mở Tùy chọn nhà phát triển</string>

core/src/main/res/values-zh/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
<string name="onboarding_page2_desc">浏览已安装的应用程序、卸载不需要的应用程序并轻松管理您的设备。</string>
1010
<string name="onboarding_page3_title">允许安装</string>
1111
<string name="onboarding_page3_desc">要安装软件包,应用程序需要从未知来源安装的权限。点击下面的按钮以授予它。</string>
12+
<string name="onboarding_virustotal_title">安装前先扫描</string>
13+
<string name="onboarding_virustotal_desc">您打开的任何安装包都可以直接在安装界面用 VirusTotal(70 多个杀毒引擎)检查。点击\"使用 VirusTotal 检查\",结果会在您确认安装前显示。此功能需要免费的 API 密钥:在下方获取,然后粘贴到设置 &gt; 高级 &gt; VirusTotal API 密钥。</string>
14+
<string name="onboarding_virustotal_get_key">获取免费 API 密钥</string>
1215
<string name="onboarding_xiaomi_title">如果在 MIUI 上安装失败</string>
1316
<string name="onboarding_xiaomi_desc">MIUI/HyperOS 优化可能会在不给出原因的情况下阻止第三方安装。若遇到这种情况,请进入开发者选项,关闭\"MIUI 优化\",安装应用后再重新打开。关闭期间系统主题可能显示异常,重启手机即可恢复。</string>
1417
<string name="onboarding_xiaomi_open_developer_options">打开开发者选项</string>

core/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
<string name="onboarding_page2_desc">Browse installed apps, uninstall unwanted ones, and manage your device effortlessly.</string>
1212
<string name="onboarding_page3_title">Allow Installation</string>
1313
<string name="onboarding_page3_desc">To install packages, the app needs permission to install from unknown sources. Tap the button below to grant it.</string>
14+
<string name="onboarding_virustotal_title">Scan before you install</string>
15+
<string name="onboarding_virustotal_desc">Any package you open can be checked against VirusTotal — 70+ antivirus engines — straight from the install screen. Tap \"Check with VirusTotal\" and the result appears before you commit. It needs a free API key: get one below, then paste it into Settings &gt; Advanced &gt; VirusTotal API Key.</string>
16+
<string name="onboarding_virustotal_get_key">Get a free API key</string>
1417
<string name="onboarding_xiaomi_title">If an install fails on MIUI</string>
1518
<string name="onboarding_xiaomi_desc">MIUI/HyperOS optimization can block third-party installs without saying why. If that happens, open Developer options, turn off \"MIUI optimization\", install the app, then turn it back on. While it is off the system theme may look distorted — restarting the phone fixes that.</string>
1619
<string name="onboarding_xiaomi_open_developer_options">Open Developer options</string>

0 commit comments

Comments
 (0)