diff --git a/app/src/main/java/com/deadzon/app/app/DeadZonApp.kt b/app/src/main/java/com/deadzon/app/app/DeadZonApp.kt index 20fae44a..182133bc 100644 --- a/app/src/main/java/com/deadzon/app/app/DeadZonApp.kt +++ b/app/src/main/java/com/deadzon/app/app/DeadZonApp.kt @@ -1,29 +1,16 @@ package com.deadzon.app.app import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.collectAsState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -31,35 +18,22 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.deadzon.app.core.ui.BottomActionBar import com.deadzon.app.core.ui.DeadZonBackground -import com.deadzon.app.core.ui.DeadZonLogoMark -import com.deadzon.app.core.ui.GlassCard -import com.deadzon.app.core.ui.SectionTitle import com.deadzon.app.feature.dashboard.DashboardScreen +import com.deadzon.app.feature.modules.ModuleCatalog +import com.deadzon.app.feature.modules.ModuleSectionScreen +import com.deadzon.app.feature.modules.ModulesScreen import com.deadzon.app.feature.monet.MonetScreen -import com.deadzon.app.feature.monet.MonetViewModel import com.deadzon.app.feature.settings.SettingsScreen +import com.deadzon.app.feature.tools.ToolsScreen @Composable -fun DeadZonApp(vm: MonetViewModel = hiltViewModel()) { - val state by vm.uiState.collectAsState() - - if (!state.rootGateCompleted) { - RootGateScreen( - busy = state.busy, - rootAvailable = state.rootAvailable, - message = state.lastResult, - onRefresh = vm::refreshRootStatus, - onRequest = vm::requestRootFromOnboarding - ) - return - } - +fun DeadZonApp() { val navController = rememberNavController() - val tabs = listOf(Screen.Dashboard, Screen.Monet, Screen.Settings) + val tabs = listOf(Screen.Home, Screen.Modules, Screen.Tools, Screen.Settings) DeadZonBackground { Scaffold( - containerColor = Color.Transparent, + containerColor = androidx.compose.ui.graphics.Color.Transparent, bottomBar = { BottomActionBar { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { @@ -79,53 +53,21 @@ fun DeadZonApp(vm: MonetViewModel = hiltViewModel()) { ) { paddingValues -> NavHost( navController = navController, - startDestination = Screen.Dashboard.route, + startDestination = Screen.Home.route, modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { - composable(Screen.Dashboard.route) { DashboardScreen(navController) } - composable(Screen.Monet.route) { MonetScreen() } + composable(Screen.Home.route) { DashboardScreen(navController) } + composable(Screen.Modules.route) { ModulesScreen(navController) } + composable(Screen.Tools.route) { ToolsScreen(navController) } composable(Screen.Settings.route) { SettingsScreen() } - } - } - } -} - -@Composable -private fun RootGateScreen( - busy: Boolean, - rootAvailable: Boolean, - message: String?, - onRefresh: () -> Unit, - onRequest: () -> Unit -) { - DeadZonBackground { - Column( - modifier = Modifier - .fillMaxSize() - .padding(20.dp), - verticalArrangement = Arrangement.Center - ) { - GlassCard { - DeadZonLogoMark(modifier = Modifier.align(Alignment.CenterHorizontally)) - Spacer(modifier = Modifier.height(8.dp)) - SectionTitle( - title = "Root Access Required", - subtitle = "Dead Zon needs root to control Monet overlays internally" - ) - Text(if (rootAvailable) "Root detected. Continue." else "Tap request to trigger your root manager prompt.") - message?.let { Text(it, color = Color(0xFFFFA0A0)) } - Row(horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth()) { - OutlinedButton(onClick = onRefresh, modifier = Modifier.weight(1f)) { Text("Refresh") } - Button( - onClick = onRequest, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFB91010)) - ) { - if (busy) CircularProgressIndicator(strokeWidth = 2.dp, color = Color.White) else Text("Request Root") - } - } + composable(Screen.Monet.route) { MonetScreen() } + composable(Screen.AboutPhone.route) { ModuleSectionScreen(ModuleCatalog.aboutPhone) } + composable(Screen.Notifications.route) { ModuleSectionScreen(ModuleCatalog.notifications) } + composable(Screen.NotificationAnimations.route) { ModuleSectionScreen(ModuleCatalog.notificationAnimations) } + composable(Screen.Lockscreen.route) { ModuleSectionScreen(ModuleCatalog.lockscreen) } + composable(Screen.ControlCenter.route) { ModuleSectionScreen(ModuleCatalog.controlCenter) } } } } diff --git a/app/src/main/java/com/deadzon/app/app/Screen.kt b/app/src/main/java/com/deadzon/app/app/Screen.kt index 38b7092b..147d3f6f 100644 --- a/app/src/main/java/com/deadzon/app/app/Screen.kt +++ b/app/src/main/java/com/deadzon/app/app/Screen.kt @@ -2,12 +2,22 @@ package com.deadzon.app.app import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Dashboard +import androidx.compose.material.icons.filled.Extension +import androidx.compose.material.icons.filled.GridView import androidx.compose.material.icons.filled.Palette import androidx.compose.material.icons.filled.Settings import androidx.compose.ui.graphics.vector.ImageVector sealed class Screen(val route: String, val title: String, val icon: ImageVector) { - data object Dashboard : Screen("dashboard", "Dashboard", Icons.Default.Dashboard) - data object Monet : Screen("monet", "Monet", Icons.Default.Palette) + data object Home : Screen("home", "Home", Icons.Default.Dashboard) + data object Modules : Screen("modules", "Modules", Icons.Default.GridView) + data object Tools : Screen("tools", "Tools", Icons.Default.Extension) data object Settings : Screen("settings", "Settings", Icons.Default.Settings) + + data object Monet : Screen("monet", "Monet", Icons.Default.Palette) + data object AboutPhone : Screen("modules/about_phone", "About Phone", Icons.Default.GridView) + data object Notifications : Screen("modules/notifications", "Notifications", Icons.Default.GridView) + data object NotificationAnimations : Screen("modules/notification_animations", "Notification Animations", Icons.Default.GridView) + data object Lockscreen : Screen("modules/lockscreen", "Lockscreen", Icons.Default.GridView) + data object ControlCenter : Screen("modules/control_center", "Control Center", Icons.Default.GridView) } diff --git a/app/src/main/java/com/deadzon/app/data/prefs/MonetPreferences.kt b/app/src/main/java/com/deadzon/app/data/prefs/MonetPreferences.kt index bb358799..eb5dd371 100644 --- a/app/src/main/java/com/deadzon/app/data/prefs/MonetPreferences.kt +++ b/app/src/main/java/com/deadzon/app/data/prefs/MonetPreferences.kt @@ -23,7 +23,6 @@ class MonetPreferences @Inject constructor( val applyToApp = booleanPreferencesKey("apply_to_app") val preset = stringPreferencesKey("preset") val targets = stringPreferencesKey("targets") - val rootGateCompleted = booleanPreferencesKey("root_gate_completed") } val state: Flow = context.monetDataStore.data.map { prefs -> @@ -33,7 +32,6 @@ class MonetPreferences @Inject constructor( presetId = prefs[Keys.preset] ?: "stock", selectedTargets = (prefs[Keys.targets]?.split(',')?.filter { it.isNotBlank() } ?.toSet() ?: MonetOverlayRegistry.targets.map { it.preferenceKey }.toSet()), - rootGateCompleted = prefs[Keys.rootGateCompleted] ?: false ) } @@ -45,14 +43,12 @@ class MonetPreferences @Inject constructor( presetId = prefs[Keys.preset] ?: "stock", selectedTargets = (prefs[Keys.targets]?.split(',')?.filter { it.isNotBlank() } ?.toSet() ?: MonetOverlayRegistry.targets.map { it.preferenceKey }.toSet()), - rootGateCompleted = prefs[Keys.rootGateCompleted] ?: false - ) + ) val next = transform(current) prefs[Keys.monetEnabled] = next.monetEnabled prefs[Keys.applyToApp] = next.applyToApp prefs[Keys.preset] = next.presetId prefs[Keys.targets] = next.selectedTargets.joinToString(",") - prefs[Keys.rootGateCompleted] = next.rootGateCompleted } } } @@ -61,8 +57,7 @@ data class StoredMonetState( val monetEnabled: Boolean, val applyToApp: Boolean, val presetId: String, - val selectedTargets: Set, - val rootGateCompleted: Boolean + val selectedTargets: Set ) typealias MutablePreferences = androidx.datastore.preferences.core.MutablePreferences diff --git a/app/src/main/java/com/deadzon/app/domain/monet/MonetModels.kt b/app/src/main/java/com/deadzon/app/domain/monet/MonetModels.kt index 39cbbbc4..ed4c714d 100644 --- a/app/src/main/java/com/deadzon/app/domain/monet/MonetModels.kt +++ b/app/src/main/java/com/deadzon/app/domain/monet/MonetModels.kt @@ -43,6 +43,5 @@ data class MonetUiState( val rootAvailable: Boolean = false, val busy: Boolean = false, val logs: List = emptyList(), - val lastResult: String? = null, - val rootGateCompleted: Boolean = false + val lastResult: String? = null ) diff --git a/app/src/main/java/com/deadzon/app/feature/dashboard/DashboardScreen.kt b/app/src/main/java/com/deadzon/app/feature/dashboard/DashboardScreen.kt index 103c25c0..46936682 100644 --- a/app/src/main/java/com/deadzon/app/feature/dashboard/DashboardScreen.kt +++ b/app/src/main/java/com/deadzon/app/feature/dashboard/DashboardScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AutoFixHigh +import androidx.compose.material.icons.filled.GridView import androidx.compose.material.icons.filled.Palette import androidx.compose.material.icons.filled.Verified import androidx.compose.material3.Button @@ -44,8 +45,8 @@ fun DashboardScreen(navController: NavController, vm: MonetViewModel = hiltViewM Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { DeadZonLogoMark() Column(verticalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.padding(top = 8.dp)) { - Text("DEAD ZON") - Text("Premium Overlay Control", color = Color(0xFFB9A6A6)) + Text("DeadZon") + Text("Unified ROM Feature Hub", color = Color(0xFFB9A6A6)) } } } @@ -55,26 +56,30 @@ fun DashboardScreen(navController: NavController, vm: MonetViewModel = hiltViewM MetricChip(Icons.Default.Verified, "Monet", if (state.monetEnabled) "Enabled" else "Disabled") } Column(Modifier.weight(1f)) { - MetricChip(Icons.Default.Palette, "Preset", state.selectedPresetId.uppercase()) + MetricChip(Icons.Default.Palette, "Palette", state.selectedPresetId.uppercase()) } } MetricChip(Icons.Default.AutoFixHigh, "Selected Targets", state.selectedTargets.size.toString()) + MetricChip(Icons.Default.GridView, "Modules", "About • Notifications • Lockscreen • Control Center") Row(horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth()) { Button( - onClick = { vm.apply(false) }, + onClick = { navController.navigate(Screen.Modules.route) }, + modifier = Modifier.weight(1f) + ) { Text("Open Modules") } + Button( + onClick = { navController.navigate(Screen.Tools.route) }, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFB91010)) - ) { Text("Quick Apply") } - Button(onClick = { navController.navigate(Screen.Monet.route) }, modifier = Modifier.weight(1f)) { Text("Open Monet") } + ) { Text("Open Tools") } } GlassCard { - SectionTitle("Future Modules", "Temporary placeholders for this visual pass") - Text("• Performance profiles") - Text("• Thermal manager") - Text("• Network tuning") + SectionTitle("What changed", "DeadZon now centralizes ROM options that were previously split across Settings.") + Text("• Home for overview") + Text("• Modules for ROM feature pages") + Text("• Tools for action-based controls like Monet") } } } diff --git a/app/src/main/java/com/deadzon/app/feature/modules/ModuleModels.kt b/app/src/main/java/com/deadzon/app/feature/modules/ModuleModels.kt new file mode 100644 index 00000000..209f92cc --- /dev/null +++ b/app/src/main/java/com/deadzon/app/feature/modules/ModuleModels.kt @@ -0,0 +1,115 @@ +package com.deadzon.app.feature.modules + +enum class FeatureControlType { TOGGLE, SLIDER, LIST, COLOR, ACTION } + +data class FeatureOption( + val key: String, + val title: String, + val summary: String? = null, + val type: FeatureControlType, + val defaultValue: String, + val choices: List = emptyList(), + val requiresRootAction: Boolean = false +) + +data class FeatureSection( + val title: String, + val subtitle: String? = null, + val options: List +) + +object ModuleCatalog { + val aboutPhone = FeatureSection( + title = "About Phone", + subtitle = "Profile, header style, blur and background behavior", + options = listOf( + FeatureOption("my_device_name", "Device name", type = FeatureControlType.LIST, defaultValue = "Redmi Note 13 Pro+", choices = listOf("Redmi Note 13 Pro+", "Custom")), + FeatureOption("mezo_version_name", "Version label", type = FeatureControlType.LIST, defaultValue = "3.0.6.0", choices = listOf("3.0.6.0", "Custom")), + FeatureOption("horizontal_specs", "Enhanced specs layout", summary = "Xiaomi-style spec cards", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("change_mod", "About phone style", type = FeatureControlType.LIST, defaultValue = "Style 1", choices = listOf("Style 1", "Style 2", "Style 3")), + FeatureOption("lock_backg", "Header background mode", type = FeatureControlType.LIST, defaultValue = "Blur", choices = listOf("Blur", "Transparent")), + FeatureOption("lock_wall_about", "Header image source", type = FeatureControlType.LIST, defaultValue = "Lockscreen", choices = listOf("Lockscreen", "Wallpaper")), + FeatureOption("show_weather_header", "Show weather in header", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("hide_mini", "Header logo mode", type = FeatureControlType.LIST, defaultValue = "Logo", choices = listOf("Logo", "Mini")), + FeatureOption("mmu_habout_blur", "Header blur", type = FeatureControlType.SLIDER, defaultValue = "69"), + FeatureOption("mmu_wabout_blur", "Mini wallpaper blur", type = FeatureControlType.SLIDER, defaultValue = "69"), + FeatureOption("lock_infbackg", "Info card background", type = FeatureControlType.LIST, defaultValue = "Transparent", choices = listOf("Transparent", "Blur")), + FeatureOption("lock_wall_infabout", "Info card image source", type = FeatureControlType.LIST, defaultValue = "Wallpaper", choices = listOf("Wallpaper", "Lockscreen")) + ) + ) + + val notifications = FeatureSection( + title = "Notifications", + subtitle = "General behavior, lockscreen, privacy and quality controls", + options = listOf( + FeatureOption("mezo_notif_icons_size", "Notification icon size", type = FeatureControlType.LIST, defaultValue = "35", choices = listOf("28", "32", "35", "40")), + FeatureOption("bg_hide_lock_dnd", "Hide persistent DND notification", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("icon_mezo_app_color", "Colorize notification icons", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("status_bar_expand_top_notification", "Expand top notification", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("notif_mezo_sound", "Notification sound", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("mezo_notification", "Transparent notifications", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("show_notif_mezo_lock", "Show notifications on lockscreen", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("smart_notifications", "Smart notifications", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("disable_headsup", "Heads-up notification restriction", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("privacy_mezo_dot", "Privacy dot", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("enable_mezo_mock_locations", "Mock locations", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("lock_to_app_enabled", "Lock to app", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("vibrate_mezo_on", "Vibration and touch feedback", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("network_dealy", "Network delay optimization", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("edge_mode_enabled", "Edge mode", type = FeatureControlType.TOGGLE, defaultValue = "false") + ) + ) + + val notificationAnimations = FeatureSection( + title = "Notification Animations", + subtitle = "Animation sets, style, loop, interpolator and duration", + options = listOf( + FeatureOption("anim_expand_enable", "Enable animation set 1", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("anim_expand_style", "Animation style (set 1)", type = FeatureControlType.LIST, defaultValue = "5", choices = listOf("1", "2", "3", "4", "5")), + FeatureOption("loopanim", "Loop mode (set 1)", type = FeatureControlType.LIST, defaultValue = "3", choices = listOf("1", "2", "3")), + FeatureOption("anim_expand_interpolator", "Interpolator (set 1)", type = FeatureControlType.LIST, defaultValue = "5", choices = listOf("1", "2", "3", "4", "5")), + FeatureOption("anim_expand_duration", "Duration (set 1)", type = FeatureControlType.SLIDER, defaultValue = "1000"), + FeatureOption("anim_expand_enable2", "Enable animation set 2", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("anim_expand_style2", "Animation style (set 2)", type = FeatureControlType.LIST, defaultValue = "4", choices = listOf("1", "2", "3", "4", "5")), + FeatureOption("loopanim2", "Loop mode (set 2)", type = FeatureControlType.LIST, defaultValue = "3", choices = listOf("1", "2", "3")), + FeatureOption("anim_expand_interpolator2", "Interpolator (set 2)", type = FeatureControlType.LIST, defaultValue = "5", choices = listOf("1", "2", "3", "4", "5")), + FeatureOption("anim_expand_duration2", "Duration (set 2)", type = FeatureControlType.SLIDER, defaultValue = "1000") + ) + ) + + val lockscreen = FeatureSection( + title = "Lockscreen", + subtitle = "Notifications and charging info behavior", + options = listOf( + FeatureOption("show_notif_mezo_lock", "Show notifications on lockscreen", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("vibrate_mezo_on", "Vibration", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("charge_animation_type", "Charging animation style", type = FeatureControlType.LIST, defaultValue = "1", choices = listOf("Wave", "Pulse", "Classic")), + FeatureOption("bg_extra_charging_info", "Show charging info", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("bg_show_temp", "Show temperature", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("bg_show_ampere", "Show ampere", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("bg_show_voltage", "Show voltage", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("bg_show_power", "Show power", type = FeatureControlType.TOGGLE, defaultValue = "true"), + FeatureOption("bg_refresh_interval", "Refresh interval", type = FeatureControlType.LIST, defaultValue = "2000", choices = listOf("1000", "2000", "3000", "5000")), + FeatureOption("bg_charging_info_text_size", "Charging text size", type = FeatureControlType.SLIDER, defaultValue = "15") + ) + ) + + val controlCenter = FeatureSection( + title = "Control Center / Quick Settings", + subtitle = "Tile count, rows and color customization", + options = listOf( + FeatureOption("toggles_mezo_count_single", "Tile count (normal)", type = FeatureControlType.SLIDER, defaultValue = "6"), + FeatureOption("toggles_mezo_count_expanded", "Tile count (expanded)", type = FeatureControlType.SLIDER, defaultValue = "6"), + FeatureOption("toggles_mezo_count_expanded_rows", "Rows (expanded)", type = FeatureControlType.SLIDER, defaultValue = "3"), + FeatureOption("QS_bg_color_show", "Enable custom tile colors", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("QS_bg_color_enabled", "Tile enabled background", type = FeatureControlType.COLOR, defaultValue = "#ff00ff00"), + FeatureOption("QS_bg_color_disabled", "Tile disabled background", type = FeatureControlType.COLOR, defaultValue = "#1a000000"), + FeatureOption("QS_bg_color", "Tile base background", type = FeatureControlType.COLOR, defaultValue = "#00000000"), + FeatureOption("new_QS_background_color_force", "Force background color", type = FeatureControlType.TOGGLE, defaultValue = "false"), + FeatureOption("new_QS_background_alpha", "Background alpha", type = FeatureControlType.SLIDER, defaultValue = "0"), + FeatureOption("QS_fg_color_enabled", "Enabled foreground color", type = FeatureControlType.COLOR, defaultValue = "#ffffffff"), + FeatureOption("QS_fg_color_disabled", "Disabled foreground color", type = FeatureControlType.COLOR, defaultValue = "#ffffffff"), + FeatureOption("restart_systemui", "Restart SystemUI", summary = "Applies visual updates immediately", type = FeatureControlType.ACTION, defaultValue = "idle", requiresRootAction = true) + ) + ) +} diff --git a/app/src/main/java/com/deadzon/app/feature/modules/ModulesScreen.kt b/app/src/main/java/com/deadzon/app/feature/modules/ModulesScreen.kt new file mode 100644 index 00000000..61c900fe --- /dev/null +++ b/app/src/main/java/com/deadzon/app/feature/modules/ModulesScreen.kt @@ -0,0 +1,164 @@ +package com.deadzon.app.feature.modules + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.ExposedDropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Slider +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import com.deadzon.app.app.Screen +import com.deadzon.app.core.ui.DeadZonBackground +import com.deadzon.app.core.ui.GlassCard +import com.deadzon.app.core.ui.SectionTitle + +@Composable +fun ModulesScreen(navController: NavController) { + DeadZonBackground { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + SectionTitle("Modules", "ROM controls organized in one polished hub") + ModuleEntry("About Phone", "Header style, blur and device profile") { navController.navigate(Screen.AboutPhone.route) } + ModuleEntry("Notifications", "Icons, privacy, lockscreen behavior") { navController.navigate(Screen.Notifications.route) } + ModuleEntry("Notification Animations", "Animation sets and duration") { navController.navigate(Screen.NotificationAnimations.route) } + ModuleEntry("Lockscreen", "Charging and lockscreen info") { navController.navigate(Screen.Lockscreen.route) } + ModuleEntry("Control Center / Quick Settings", "Tiles, rows and color controls") { navController.navigate(Screen.ControlCenter.route) } + ModuleEntry("Status Bar", "Coming soon", enabled = false) { } + ModuleEntry("Extra / Misc", "Coming soon", enabled = false) { } + } + } +} + +@Composable +private fun ModuleEntry(title: String, subtitle: String, enabled: Boolean = true, onClick: () -> Unit) { + GlassCard( + modifier = Modifier + .fillMaxWidth() + .clickable(enabled = enabled, onClick = onClick) + ) { + Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) + Text(subtitle, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) + } +} + +@Composable +fun ModuleSectionScreen(section: FeatureSection, vm: ModulesViewModel = hiltViewModel()) { + val state by vm.uiState.collectAsState() + + DeadZonBackground { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + SectionTitle(section.title, section.subtitle) + state.message?.let { GlassCard { Text(it) } } + section.options.forEach { option -> + FeatureOptionCard( + option = option, + value = state.values[option.key].orEmpty(), + busy = state.busy, + onValue = { vm.setValue(option.key, it) }, + onAction = { vm.triggerRootAction(option.key) } + ) + } + } + } +} + +@Composable +private fun FeatureOptionCard( + option: FeatureOption, + value: String, + busy: Boolean, + onValue: (String) -> Unit, + onAction: () -> Unit +) { + GlassCard { + Text(option.title, style = MaterialTheme.typography.titleMedium) + option.summary?.let { Text(it, color = MaterialTheme.colorScheme.onSurfaceVariant) } + when (option.type) { + FeatureControlType.TOGGLE -> { + Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { + Text(if (value.toBoolean()) "Enabled" else "Disabled") + Switch(checked = value.toBoolean(), onCheckedChange = { onValue(it.toString()) }) + } + } + FeatureControlType.SLIDER -> { + val current = value.toFloatOrNull() ?: 0f + Text(current.toInt().toString()) + Slider(value = current, onValueChange = { onValue(it.toInt().toString()) }, valueRange = 0f..255f) + } + FeatureControlType.LIST -> ListPicker(value = value, options = option.choices, onValue = onValue) + FeatureControlType.COLOR -> OutlinedTextField(value = value, onValueChange = onValue, label = { Text("HEX") }, modifier = Modifier.fillMaxWidth()) + FeatureControlType.ACTION -> { + Button(onClick = onAction, enabled = !busy) { + if (busy) { + CircularProgressIndicator(strokeWidth = 2.dp) + } else { + Text(if (option.requiresRootAction) "Grant Root & Run" else "Run") + } + } + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun ListPicker(value: String, options: List, onValue: (String) -> Unit) { + var expanded by remember { mutableStateOf(false) } + ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = !expanded }) { + OutlinedTextField( + value = value, + onValueChange = {}, + readOnly = true, + label = { Text("Selection") }, + trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }, + modifier = Modifier + .menuAnchor() + .fillMaxWidth() + ) + ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { + options.forEach { option -> + DropdownMenuItem(text = { Text(option) }, onClick = { + expanded = false + onValue(option) + }) + } + } + } +} diff --git a/app/src/main/java/com/deadzon/app/feature/modules/ModulesViewModel.kt b/app/src/main/java/com/deadzon/app/feature/modules/ModulesViewModel.kt new file mode 100644 index 00000000..72f7eeb9 --- /dev/null +++ b/app/src/main/java/com/deadzon/app/feature/modules/ModulesViewModel.kt @@ -0,0 +1,59 @@ +package com.deadzon.app.feature.modules + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.deadzon.app.core.root.RootShell +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +data class ModuleUiState( + val values: Map = emptyMap(), + val busy: Boolean = false, + val message: String? = null +) + +@HiltViewModel +class ModulesViewModel @Inject constructor( + private val rootShell: RootShell +) : ViewModel() { + private val defaults = ( + ModuleCatalog.aboutPhone.options + + ModuleCatalog.notifications.options + + ModuleCatalog.notificationAnimations.options + + ModuleCatalog.lockscreen.options + + ModuleCatalog.controlCenter.options + ).associate { it.key to it.defaultValue } + + private val _uiState = MutableStateFlow(ModuleUiState(values = defaults)) + val uiState: StateFlow = _uiState.asStateFlow() + + fun setValue(key: String, value: String) { + _uiState.update { it.copy(values = it.values.toMutableMap().apply { put(key, value) }) } + } + + fun triggerRootAction(actionKey: String) { + viewModelScope.launch { + _uiState.update { it.copy(busy = true, message = null) } + val hasRoot = rootShell.isRootAvailable() + if (!hasRoot) { + _uiState.update { it.copy(busy = false, message = "This action requires root access.") } + return@launch + } + val result = when (actionKey) { + "restart_systemui" -> rootShell.run("pkill -f com.android.systemui") + else -> rootShell.run("true") + } + _uiState.update { + it.copy( + busy = false, + message = if (result.exitCode == 0) "Action completed." else "Action failed: ${result.stderr.ifBlank { "unknown error" }}" + ) + } + } + } +} diff --git a/app/src/main/java/com/deadzon/app/feature/monet/MonetScreen.kt b/app/src/main/java/com/deadzon/app/feature/monet/MonetScreen.kt index 9c6e35a2..c173d3ae 100644 --- a/app/src/main/java/com/deadzon/app/feature/monet/MonetScreen.kt +++ b/app/src/main/java/com/deadzon/app/feature/monet/MonetScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text @@ -46,7 +47,10 @@ fun MonetScreen(vm: MonetViewModel = hiltViewModel()) { item { SectionTitle("Monet Control", "Temporary redesign pass inspired by red glass mockup") } - item { RootStatusCard(rootAvailable = state.rootAvailable) } + item { + RootStatusCard(rootAvailable = state.rootAvailable) + } + item { OutlinedButton(onClick = vm::refreshRootStatus, modifier = Modifier.fillMaxWidth()) { Text("Check Root Status") } } item { QuickSwitch("Master Monet Switch", state.monetEnabled, vm::onMonetToggle) } item { QuickSwitch("Apply to Dead Zon App", state.applyToApp, vm::onApplyToAppToggle) } item { diff --git a/app/src/main/java/com/deadzon/app/feature/monet/MonetViewModel.kt b/app/src/main/java/com/deadzon/app/feature/monet/MonetViewModel.kt index 96f6fe5f..335ef0e9 100644 --- a/app/src/main/java/com/deadzon/app/feature/monet/MonetViewModel.kt +++ b/app/src/main/java/com/deadzon/app/feature/monet/MonetViewModel.kt @@ -33,7 +33,6 @@ class MonetViewModel @Inject constructor( bindStored(stored) } } - refreshRootStatus() } fun refreshRootStatus() { @@ -42,22 +41,6 @@ class MonetViewModel @Inject constructor( } } - fun requestRootFromOnboarding() { - viewModelScope.launch { - _uiState.update { it.copy(busy = true, lastResult = null) } - val hasRoot = controller.hasRoot() - prefs.update { it.copy(rootGateCompleted = hasRoot) } - _uiState.update { - it.copy( - busy = false, - rootAvailable = hasRoot, - rootGateCompleted = hasRoot, - lastResult = if (hasRoot) "Root access granted" else "Root access is required to continue" - ) - } - } - } - fun onMonetToggle(value: Boolean) = persist { copy(monetEnabled = value) } fun onApplyToAppToggle(value: Boolean) = persist { copy(applyToApp = value) } fun onSearchChange(query: String) { @@ -103,8 +86,7 @@ class MonetViewModel @Inject constructor( monetEnabled = false, applyToApp = true, presetId = "stock", - selectedTargets = emptySet(), - rootGateCompleted = it.rootGateCompleted + selectedTargets = emptySet() ) } _uiState.update { @@ -130,7 +112,6 @@ class MonetViewModel @Inject constructor( applyToApp = stored.applyToApp, selectedPresetId = stored.presetId, selectedTargets = stored.selectedTargets, - rootGateCompleted = stored.rootGateCompleted ) } } diff --git a/app/src/main/java/com/deadzon/app/feature/settings/SettingsScreen.kt b/app/src/main/java/com/deadzon/app/feature/settings/SettingsScreen.kt index 3cecf1e2..e1a9eaac 100644 --- a/app/src/main/java/com/deadzon/app/feature/settings/SettingsScreen.kt +++ b/app/src/main/java/com/deadzon/app/feature/settings/SettingsScreen.kt @@ -3,9 +3,8 @@ package com.deadzon.app.feature.settings import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -26,17 +25,14 @@ fun SettingsScreen() { ) { GlassCard { DeadZonLogoMark() - SectionTitle("Settings", "Temporary premium visual pass") - Text("Dead Zon keeps Monet and overlay controls fully in-app without ThemePicker or ThemesStub runtime dependency.") + SectionTitle("Settings", "DeadZon preferences and app behavior") + Text("This app centralizes ROM customizations in one premium interface.") } GlassCard { - Text("Profiles", style = androidx.compose.material3.MaterialTheme.typography.titleMedium) - Button(onClick = {}, modifier = Modifier.fillMaxWidth()) { Text("Export Profile (placeholder)") } - Button(onClick = {}, modifier = Modifier.fillMaxWidth()) { Text("Import Profile (placeholder)") } - } - GlassCard { - Text("About") - Text("Version 1 visual redesign preview") + Text("Behavior", style = MaterialTheme.typography.titleMedium) + Text("• Root is requested only for privileged actions.") + Text("• Monet is available under Tools.") + Text("• Module pages map to existing ROM preference keys.") } } } diff --git a/app/src/main/java/com/deadzon/app/feature/tools/ToolsScreen.kt b/app/src/main/java/com/deadzon/app/feature/tools/ToolsScreen.kt new file mode 100644 index 00000000..f901df5e --- /dev/null +++ b/app/src/main/java/com/deadzon/app/feature/tools/ToolsScreen.kt @@ -0,0 +1,45 @@ +package com.deadzon.app.feature.tools + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.deadzon.app.app.Screen +import com.deadzon.app.core.ui.DeadZonBackground +import com.deadzon.app.core.ui.GlassCard +import com.deadzon.app.core.ui.SectionTitle + +@Composable +fun ToolsScreen(navController: NavController) { + DeadZonBackground { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + SectionTitle("Tools", "Action-driven utilities and overlays") + GlassCard(modifier = Modifier.clickable { navController.navigate(Screen.Monet.route) }) { + Text("Monet & Overlays", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) + Text("Palette presets, target apps, apply/reset actions", color = MaterialTheme.colorScheme.onSurfaceVariant) + } + GlassCard { + Text("Root is requested only when you run privileged actions.") + Text("No root prompt is shown at app launch.", color = MaterialTheme.colorScheme.onSurfaceVariant) + } + GlassCard(modifier = Modifier.fillMaxWidth()) { + Text("More tools coming soon", style = MaterialTheme.typography.titleMedium) + Text("Status bar packs, icon packs, and quick repair actions.") + } + } + } +}