@@ -44,6 +44,7 @@ import androidx.compose.material.icons.rounded.Search
4444import androidx.compose.material.icons.rounded.SearchOff
4545import androidx.compose.material.icons.rounded.SettingsApplications
4646import androidx.compose.material.icons.rounded.Terminal
47+ import androidx.compose.material.icons.rounded.Wallpaper
4748import androidx.compose.material.icons.rounded.WifiTethering
4849import androidx.compose.material3.ExperimentalMaterial3Api
4950import androidx.compose.material3.HorizontalDivider
@@ -417,46 +418,32 @@ private fun SettingUi(
417418 )
418419 }
419420
420- // Auto-confirm moved up next to the external-open modes: it only applies
421- // to APKs opened from another app, so it belongs to that question rather
422- // than sitting among the after-install toggles it used to follow.
423- if (q.isBlank()) OptionGroupHeader (stringResource(R .string.setting_external_open_title))
424- SearchableItem (q, stringResource(R .string.setting_external_open_title), stringResource(R .string.setting_external_open_notification_sub)) {
425- ExternalOpenModeSelector (
426- current = externalOpenMode,
427- onChange = onExternalOpenModeChanged,
428- )
429- }
430- SearchableItem (q, stringResource(R .string.setting_auto_confirm_title), stringResource(R .string.setting_auto_confirm_subtitle)) {
431- SwitchPreference (
432- title = stringResource(R .string.setting_auto_confirm_title),
433- subtitle = stringResource(R .string.setting_auto_confirm_subtitle),
434- checked = uiState.autoConfirmExternalInstall,
435- onCheckedChange = onAutoConfirmExternalInstallChanged,
436- )
437- }
438-
439- if (q.isBlank()) OptionGroupHeader (stringResource(R .string.setting_install_ui_style_title))
440- SearchableItem (q, stringResource(R .string.setting_install_ui_style_title), stringResource(R .string.setting_install_ui_style_sub)) {
441- InstallUiStyleSelector (
442- current = installUiStyle,
443- onChange = onInstallUiStyleChanged,
444- )
445- }
446-
447- // Divider only makes sense when the full (unfiltered) list shows.
448- // Below it sit the two settings that belong to no group above — they
449- // change what the rest of the system sees, not how a single install runs.
421+ // Everything about how the installer looks — the external-open modes, the
422+ // card position, auto-confirm, the Download tab — now lives on its own
423+ // screen. The keyword list keeps this row findable by what moved.
450424 if (q.isBlank()) {
451425 HorizontalDivider (modifier = Modifier .padding(horizontal = 16 .dp), thickness = 0.5 .dp)
452426 }
453-
454- SearchableItem (q, stringResource(R .string.setting_show_download_tab_title), stringResource(R .string.setting_show_download_tab_subtitle)) {
455- SwitchPreference (
456- title = stringResource(R .string.setting_show_download_tab_title),
457- subtitle = stringResource(R .string.setting_show_download_tab_subtitle),
458- checked = uiState.showDownloadTab,
459- onCheckedChange = onShowDownloadTabChanged,
427+ SearchableItem (
428+ q,
429+ stringResource(R .string.install_ui_screen_title),
430+ " dialog notification bottom sheet position card download tab appearance" ,
431+ ) {
432+ ListItem (
433+ headlineContent = { Text (stringResource(R .string.install_ui_screen_title)) },
434+ supportingContent = { Text (stringResource(R .string.install_ui_entry_subtitle)) },
435+ leadingContent = {
436+ Icon (Icons .Rounded .Wallpaper , null , tint = MaterialTheme .colorScheme.primary)
437+ },
438+ modifier = Modifier .clickable {
439+ context.startActivity(
440+ android.content.Intent (
441+ context,
442+ app.pwhs.universalinstaller.presentation.setting.installui.InstallUiActivity ::class .java,
443+ )
444+ )
445+ },
446+ colors = ListItemDefaults .colors(containerColor = Color .Transparent ),
460447 )
461448 }
462449 SearchableItem (q, stringResource(R .string.setting_default_installer_title), stringResource(R .string.setting_default_installer_subtitle)) {
@@ -1097,170 +1084,7 @@ private fun SecurityLevelSelector(
10971084}
10981085
10991086
1100- /* *
1101- * Dialog / Notification / Notification-and-install, for packages opened by another app.
1102- *
1103- * A list of radio rows rather than the segmented control used elsewhere: each option needs a line
1104- * of explanation, and "installs without asking" is not a choice to make from a two-word label.
1105- */
1106- @Composable
1107- private fun ExternalOpenModeSelector (
1108- current : ExternalOpenMode ,
1109- onChange : (ExternalOpenMode ) -> Unit ,
1110- ) {
1111- // No title of its own: the group header above already reads "When another app opens an APK",
1112- // and repeating it here was the second of the two headings this control used to carry.
1113- Column (modifier = Modifier .fillMaxWidth().padding(horizontal = 16 .dp, vertical = 8 .dp)) {
1114- listOf (
1115- Triple (ExternalOpenMode .Dialog , R .string.setting_external_open_dialog, R .string.setting_external_open_dialog_sub),
1116- Triple (ExternalOpenMode .Notification , R .string.setting_external_open_notification, R .string.setting_external_open_notification_sub),
1117- Triple (ExternalOpenMode .AutoNotification , R .string.setting_external_open_auto_notification, R .string.setting_external_open_auto_notification_sub),
1118- ).forEach { (mode, titleRes, subRes) ->
1119- Row (
1120- modifier = Modifier
1121- .fillMaxWidth()
1122- .clickable { if (mode != current) onChange(mode) }
1123- .padding(vertical = 8 .dp),
1124- verticalAlignment = Alignment .Top ,
1125- ) {
1126- RadioButton (
1127- selected = mode == current,
1128- onClick = { if (mode != current) onChange(mode) },
1129- )
1130- Spacer (Modifier .width(8 .dp))
1131- Column (modifier = Modifier .weight(1f )) {
1132- Text (stringResource(titleRes), style = MaterialTheme .typography.bodyLarge)
1133- Text (
1134- text = stringResource(subRes),
1135- style = MaterialTheme .typography.bodySmall,
1136- color = MaterialTheme .colorScheme.onSurfaceVariant,
1137- )
1138- }
1139- }
1140- }
1141- }
1142- }
11431087
11441088
1145- /* *
1146- * Where the install card sits: centered, or anchored at the bottom.
1147- *
1148- * Two thumbnails rather than the segmented control this used to be. The setting decides what the
1149- * install UI *looks like*, and "Dialog | Bottom sheet" asked the user to picture two Material
1150- * component names — the only way to find out was to pick one and install something. A drawn
1151- * mock-up answers the question at a glance, and drops the paragraph of prose that stood in for it.
1152- *
1153- * The card idiom (fixed-size tile, primary border and a check when selected) is the one
1154- * [app.pwhs.universalinstaller.presentation.setting.theme.ThemeScreen] already uses for its theme
1155- * presets, so the two visual pickers in Settings look like the same control.
1156- */
1157- @Composable
1158- private fun InstallUiStyleSelector (
1159- current : InstallUiStyle ,
1160- onChange : (InstallUiStyle ) -> Unit ,
1161- ) {
1162- Row (
1163- modifier = Modifier .fillMaxWidth().padding(horizontal = 16 .dp, vertical = 8 .dp),
1164- horizontalArrangement = Arrangement .spacedBy(12 .dp),
1165- ) {
1166- listOf (
1167- InstallUiStyle .Dialog to R .string.setting_install_ui_style_dialog,
1168- InstallUiStyle .Sheet to R .string.setting_install_ui_style_sheet,
1169- ).forEach { (style, labelRes) ->
1170- InstallUiStyleCard (
1171- style = style,
1172- label = stringResource(labelRes),
1173- selected = style == current,
1174- onClick = { if (style != current) onChange(style) },
1175- )
1176- }
1177- }
1178- }
11791089
1180- /* *
1181- * One thumbnail: a phone-shaped frame with a miniature install card drawn where the real one lands.
1182- */
1183- @Composable
1184- private fun InstallUiStyleCard (
1185- style : InstallUiStyle ,
1186- label : String ,
1187- selected : Boolean ,
1188- onClick : () -> Unit ,
1189- ) {
1190- val accent = MaterialTheme .colorScheme.primary
1191- Column (horizontalAlignment = Alignment .CenterHorizontally ) {
1192- Surface (
1193- // selectable(), not clickable(): this is one of a set of mutually exclusive choices,
1194- // and TalkBack should announce it as such rather than as a plain button.
1195- modifier = Modifier
1196- .size(width = 92 .dp, height = 124 .dp)
1197- .selectable(selected = selected, role = Role .RadioButton , onClick = onClick),
1198- shape = RoundedCornerShape (12 .dp),
1199- color = MaterialTheme .colorScheme.surfaceVariant,
1200- border = BorderStroke (
1201- width = if (selected) 2 .dp else 1 .dp,
1202- color = if (selected) accent else MaterialTheme .colorScheme.outlineVariant,
1203- ),
1204- ) {
1205- Box (
1206- modifier = Modifier .fillMaxSize().padding(10 .dp),
1207- // The whole point of the thumbnail: the mini card sits where the real one will.
1208- contentAlignment = if (style == InstallUiStyle .Sheet ) {
1209- Alignment .BottomCenter
1210- } else {
1211- Alignment .Center
1212- },
1213- ) {
1214- Column (
1215- modifier = Modifier
1216- // A sheet spans the width; a centered card is inset on both sides.
1217- .fillMaxWidth(if (style == InstallUiStyle .Sheet ) 1f else 0.82f )
1218- .clip(RoundedCornerShape (6 .dp))
1219- .background(MaterialTheme .colorScheme.surface)
1220- .padding(horizontal = 6 .dp, vertical = 7 .dp),
1221- verticalArrangement = Arrangement .spacedBy(4 .dp),
1222- ) {
1223- MiniCardLine (0.7f , MaterialTheme .colorScheme.onSurface.copy(alpha = 0.55f ))
1224- MiniCardLine (1f , MaterialTheme .colorScheme.onSurfaceVariant.copy(alpha = 0.35f ))
1225- Spacer (Modifier .height(1 .dp))
1226- // Stands in for the Install button, in the accent color so the mock-up reads
1227- // as this app's card rather than a generic grey box.
1228- MiniCardLine (0.45f , accent, height = 6 .dp)
1229- }
1230- }
1231- }
1232- Spacer (Modifier .height(6 .dp))
1233- Row (verticalAlignment = Alignment .CenterVertically ) {
1234- if (selected) {
1235- Icon (
1236- imageVector = Icons .Rounded .CheckCircle ,
1237- contentDescription = null ,
1238- tint = accent,
1239- modifier = Modifier .size(14 .dp),
1240- )
1241- Spacer (Modifier .width(4 .dp))
1242- }
1243- Text (
1244- text = label,
1245- style = MaterialTheme .typography.bodyMedium,
1246- color = if (selected) accent else MaterialTheme .colorScheme.onSurfaceVariant,
1247- )
1248- }
1249- }
1250- }
12511090
1252- /* * A single bar inside the thumbnail, standing in for a line of text or a button. */
1253- @Composable
1254- private fun MiniCardLine (
1255- widthFraction : Float ,
1256- color : Color ,
1257- height : Dp = 4.dp,
1258- ) {
1259- Box (
1260- modifier = Modifier
1261- .fillMaxWidth(widthFraction)
1262- .height(height)
1263- .clip(RoundedCornerShape (2 .dp))
1264- .background(color),
1265- )
1266- }
0 commit comments