diff --git a/app/src/main/java/com/brittytino/patchwork/FeatureSettingsActivity.kt b/app/src/main/java/com/brittytino/patchwork/FeatureSettingsActivity.kt index e3a91e3..dca2866 100644 --- a/app/src/main/java/com/brittytino/patchwork/FeatureSettingsActivity.kt +++ b/app/src/main/java/com/brittytino/patchwork/FeatureSettingsActivity.kt @@ -74,6 +74,11 @@ import com.brittytino.patchwork.ui.composables.configs.ScreenLockedSecuritySetti import com.brittytino.patchwork.ui.composables.configs.KeyboardSettingsUI import com.brittytino.patchwork.utils.HapticUtil import com.brittytino.patchwork.domain.registry.FeatureRegistry +import com.brittytino.patchwork.ui.screens.AppBehaviorScreen +import com.brittytino.patchwork.ui.screens.AppCooldownScreen +import com.brittytino.patchwork.ui.screens.IdleAppScreen +import com.brittytino.patchwork.ui.screens.ActionHistoryScreen +import com.brittytino.patchwork.ui.screens.SystemSnapshotsScreen @OptIn(ExperimentalMaterial3Api::class) class FeatureSettingsActivity : FragmentActivity() { @@ -178,6 +183,7 @@ class FeatureSettingsActivity : FragmentActivity() { val isOverlayPermissionGranted by viewModel.isOverlayPermissionGranted val isNotificationLightingAccessibilityEnabled by viewModel.isNotificationLightingAccessibilityEnabled val isNotificationListenerEnabled by viewModel.isNotificationListenerEnabled + val isUsageStatsPermissionGranted by viewModel.isUsageStatsPermissionGranted // FAB State for Notification Lighting var fabExpanded by remember { mutableStateOf(true) } @@ -190,7 +196,7 @@ class FeatureSettingsActivity : FragmentActivity() { } // Show permission sheet if feature has missing permissions - LaunchedEffect(featureId, isAccessibilityEnabled, isWriteSecureSettingsEnabled, isOverlayPermissionGranted, isNotificationLightingAccessibilityEnabled, isNotificationListenerEnabled) { + LaunchedEffect(featureId, isAccessibilityEnabled, isWriteSecureSettingsEnabled, isOverlayPermissionGranted, isNotificationLightingAccessibilityEnabled, isNotificationListenerEnabled, isUsageStatsPermissionGranted) { val hasMissingPermissions = when (featureId) { "Screen off widget" -> !isAccessibilityEnabled "Statusbar icons" -> !isWriteSecureSettingsEnabled @@ -203,6 +209,13 @@ class FeatureSettingsActivity : FragmentActivity() { "Freeze" -> !com.brittytino.patchwork.utils.ShellUtils.hasPermission(context) "Location reached" -> !viewModel.isLocationPermissionGranted.value || !viewModel.isBackgroundLocationPermissionGranted.value "Quick settings tiles" -> !viewModel.isWriteSettingsEnabled.value + + // New Features Check + "App Behavior Controller" -> !isAccessibilityEnabled + "Smart App Cooldown" -> !isAccessibilityEnabled || !isOverlayPermissionGranted + "Idle App Auto-Action" -> !isUsageStatsPermissionGranted + "System State Snapshots" -> !isWriteSecureSettingsEnabled + else -> false } showPermissionSheet = hasMissingPermissions @@ -290,9 +303,95 @@ class FeatureSettingsActivity : FragmentActivity() { action = { context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) }, + isGranted = isAccessibilityEnabled ), + PermissionItem( + iconRes = R.drawable.rounded_security_24, + title = R.string.perm_write_secure_title, + description = R.string.perm_write_secure_desc_night_light, + dependentFeatures = PermissionRegistry.getFeatures("WRITE_SECURE_SETTINGS"), + actionLabel = R.string.perm_action_copy_adb, + action = { + val adbCommand = "adb shell pm grant com.brittytino.patchwork android.permission.WRITE_SECURE_SETTINGS" + val clipboard = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("adb_command", adbCommand) + clipboard.setPrimaryClip(clip) + }, + secondaryActionLabel = R.string.perm_action_check, + secondaryAction = { + viewModel.isWriteSecureSettingsEnabled.value = viewModel.canWriteSecureSettings(context) + }, + isGranted = isWriteSecureSettingsEnabled + ) + ) + "App Behavior Controller" -> listOf( + PermissionItem( + iconRes = R.drawable.rounded_settings_accessibility_24, + title = R.string.perm_accessibility_title, + description = R.string.perm_accessibility_desc_common, + dependentFeatures = PermissionRegistry.getFeatures("ACCESSIBILITY"), + actionLabel = R.string.perm_action_enable, + action = { context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) }, isGranted = isAccessibilityEnabled ) ) + "Smart App Cooldown" -> listOf( + PermissionItem( + iconRes = R.drawable.rounded_settings_accessibility_24, + title = R.string.perm_accessibility_title, + description = R.string.perm_accessibility_desc_common, + dependentFeatures = PermissionRegistry.getFeatures("ACCESSIBILITY"), + actionLabel = R.string.perm_action_enable, + action = { context.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) }, + isGranted = isAccessibilityEnabled + ), + PermissionItem( + iconRes = R.drawable.rounded_magnify_fullscreen_24, + title = R.string.perm_overlay_title, + description = R.string.perm_overlay_desc, + dependentFeatures = PermissionRegistry.getFeatures("DRAW_OVERLAYS"), + actionLabel = R.string.perm_action_grant, + action = { + val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${context.packageName}")) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + }, + isGranted = isOverlayPermissionGranted + ) + ) + "Idle App Auto-Action" -> listOf( + PermissionItem( + iconRes = R.drawable.rounded_info_24, + title = R.string.feat_idle_app_title, + description = R.string.feat_idle_app_desc, + dependentFeatures = PermissionRegistry.getFeatures("USAGE_STATS"), + actionLabel = R.string.perm_action_grant, + action = { + val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + }, + isGranted = isUsageStatsPermissionGranted + ) + ) + "System State Snapshots" -> listOf( + PermissionItem( + iconRes = R.drawable.rounded_security_24, + title = R.string.perm_write_secure_title, + description = R.string.perm_write_secure_desc_common, + dependentFeatures = PermissionRegistry.getFeatures("WRITE_SECURE_SETTINGS"), + actionLabel = R.string.perm_action_copy_adb, + action = { + val adbCommand = "adb shell pm grant com.brittytino.patchwork android.permission.WRITE_SECURE_SETTINGS" + val clipboard = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("adb_command", adbCommand) + clipboard.setPrimaryClip(clip) + }, + secondaryActionLabel = R.string.perm_action_check, + secondaryAction = { + viewModel.isWriteSecureSettingsEnabled.value = viewModel.canWriteSecureSettings(context) + }, + isGranted = isWriteSecureSettingsEnabled ) + ) "Dynamic night light" -> listOf( PermissionItem( iconRes = R.drawable.rounded_settings_accessibility_24, @@ -462,150 +561,168 @@ class FeatureSettingsActivity : FragmentActivity() { } val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState()) - Scaffold( - contentWindowInsets = androidx.compose.foundation.layout.WindowInsets(0, 0, 0, 0), - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), - containerColor = MaterialTheme.colorScheme.surfaceContainer, - topBar = { - ReusableTopAppBar( - title = if (featureObj != null) stringResource(featureObj.title) else featureId, - hasBack = true, - hasSearch = false, - onBackClick = { finish() }, - scrollBehavior = scrollBehavior, - subtitle = if (featureObj != null) stringResource(featureObj.description) else "", - isBeta = featureObj?.isBeta ?: false - ) - }, - floatingActionButton = { - if (featureId == "Notification lighting") { - ExtendedFloatingActionButton( - onClick = { - HapticUtil.performVirtualKeyHaptic(view) - viewModel.triggerNotificationLighting(context) - }, - expanded = fabExpanded, - icon = { Icon(painter = painterResource(id = R.drawable.rounded_play_arrow_24), contentDescription = null) }, - text = { Text(stringResource(R.string.action_preview)) }, - modifier = Modifier.height(64.dp) - ) - } + + // Check if this is a full-screen feature that manages its own Scaffold + val isFullScreenFeature = featureId == "App Behavior Controller" || + featureId == "Smart App Cooldown" || + featureId == "Idle App Auto-Action" || + featureId == "Action History Timeline" || + featureId == "System State Snapshots" + + if (isFullScreenFeature) { + when (featureId) { + "App Behavior Controller" -> AppBehaviorScreen() + "Smart App Cooldown" -> AppCooldownScreen() + "Idle App Auto-Action" -> IdleAppScreen() + "Action History Timeline" -> ActionHistoryScreen(onNavigateBack = { finish() }) + "System State Snapshots" -> SystemSnapshotsScreen(onNavigateBack = { finish() }) } - ) { innerPadding -> - val hasScroll = featureId != "Sound mode tile" - Column( - modifier = Modifier - .padding(innerPadding) - .fillMaxSize() - .then(if (hasScroll) Modifier.verticalScroll(rememberScrollState()) else Modifier) - ) { - when (featureId) { - "Screen off widget" -> { - ScreenOffWidgetSettingsUI( - viewModel = viewModel, - selectedHaptic = selectedHaptic, - onHapticSelected = { type -> selectedHaptic = type }, - vibrator = vibrator, - prefs = prefs, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Statusbar icons" -> { - StatusBarIconSettingsUI( - viewModel = statusBarViewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Caffeinate" -> { - CaffeinateSettingsUI( - viewModel = caffeinateViewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Notification lighting" -> { - NotificationLightingSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Sound mode tile" -> { - SoundModeTileSettingsUI( - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Button remap" -> { - ButtonRemapSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Dynamic night light" -> { - DynamicNightLightSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Snooze system notifications" -> { - SnoozeNotificationsSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Screen locked security" -> { - ScreenLockedSecuritySettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "App lock" -> { - AppLockSettingsUI( - viewModel = viewModel, - highlightKey = highlightSetting - ) - } - "Freeze" -> { - com.brittytino.patchwork.ui.composables.configs.FreezeSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightKey = highlightSetting - ) - } - "Quick settings tiles" -> { - QuickSettingsTilesSettingsUI( - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "Location reached" -> { - LocationReachedSettingsUI( - mainViewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting - ) - } - "System Keyboard" -> { - KeyboardSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp), - highlightSetting = highlightSetting + } else { + Scaffold( + contentWindowInsets = androidx.compose.foundation.layout.WindowInsets(0, 0, 0, 0), + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + containerColor = MaterialTheme.colorScheme.surfaceContainer, + topBar = { + ReusableTopAppBar( + title = if (featureObj != null) stringResource(featureObj.title) else featureId, + hasBack = true, + hasSearch = false, + onBackClick = { finish() }, + scrollBehavior = scrollBehavior, + subtitle = if (featureObj != null) stringResource(featureObj.description) else "", + isBeta = featureObj?.isBeta ?: false + ) + }, + floatingActionButton = { + if (featureId == "Notification lighting") { + ExtendedFloatingActionButton( + onClick = { + HapticUtil.performVirtualKeyHaptic(view) + viewModel.triggerNotificationLighting(context) + }, + expanded = fabExpanded, + icon = { Icon(painter = painterResource(id = R.drawable.rounded_play_arrow_24), contentDescription = null) }, + text = { Text(stringResource(R.string.action_preview)) }, + modifier = Modifier.height(64.dp) ) } - "Batteries" -> { - BatteriesSettingsUI( - viewModel = viewModel, - modifier = Modifier.padding(top = 16.dp) - ) + } + ) { innerPadding -> + val hasScroll = featureId != "Sound mode tile" + Column( + modifier = Modifier + .padding(innerPadding) + .fillMaxSize() + .then(if (hasScroll) Modifier.verticalScroll(rememberScrollState()) else Modifier) + ) { + when (featureId) { + "Screen off widget" -> { + ScreenOffWidgetSettingsUI( + viewModel = viewModel, + selectedHaptic = selectedHaptic, + onHapticSelected = { type -> selectedHaptic = type }, + vibrator = vibrator, + prefs = prefs, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Statusbar icons" -> { + StatusBarIconSettingsUI( + viewModel = statusBarViewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Caffeinate" -> { + CaffeinateSettingsUI( + viewModel = caffeinateViewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Notification lighting" -> { + NotificationLightingSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Sound mode tile" -> { + SoundModeTileSettingsUI( + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Button remap" -> { + ButtonRemapSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Dynamic night light" -> { + DynamicNightLightSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Snooze system notifications" -> { + SnoozeNotificationsSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Screen locked security" -> { + ScreenLockedSecuritySettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "App lock" -> { + AppLockSettingsUI( + viewModel = viewModel, + highlightKey = highlightSetting + ) + } + "Freeze" -> { + com.brittytino.patchwork.ui.composables.configs.FreezeSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightKey = highlightSetting + ) + } + "Quick settings tiles" -> { + QuickSettingsTilesSettingsUI( + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Location reached" -> { + LocationReachedSettingsUI( + mainViewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "System Keyboard" -> { + KeyboardSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp), + highlightSetting = highlightSetting + ) + } + "Batteries" -> { + BatteriesSettingsUI( + viewModel = viewModel, + modifier = Modifier.padding(top = 16.dp) + ) + } + // else -> default UI (optional cleanup) } - // else -> default UI (optional cleanup) } } } diff --git a/app/src/main/java/com/brittytino/patchwork/MainActivity.kt b/app/src/main/java/com/brittytino/patchwork/MainActivity.kt index 3357659..27ed6b5 100644 --- a/app/src/main/java/com/brittytino/patchwork/MainActivity.kt +++ b/app/src/main/java/com/brittytino/patchwork/MainActivity.kt @@ -64,6 +64,11 @@ class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Install and configure the splash screen val splashScreen = installSplashScreen() + + // Force splash screen to dismiss after 2 seconds no matter what + // to prevent getting stuck if Compose has an issue on this device/OS + window.decorView.postDelayed({ isAppReady = true }, 2000) + splashScreen.setKeepOnScreenCondition { !isAppReady } WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) @@ -73,74 +78,6 @@ class MainActivity : FragmentActivity() { window.isNavigationBarContrastEnforced = false } - // Keep splash screen visible while app is loading - splashScreen.setKeepOnScreenCondition { !isAppReady } - - // Customize the exit animation - scale up and fade out - // Safe implementation for OEM devices that may not provide iconView - splashScreen.setOnExitAnimationListener { splashScreenViewProvider -> - try { - val splashScreenView = splashScreenViewProvider.view - val splashIcon = try { splashScreenViewProvider.iconView } catch (e: Exception) { null } - - // Animate the splash screen view fade out - val fadeOut = ObjectAnimator.ofFloat(splashScreenView, "alpha", 1f, 0f).apply { - interpolator = AnticipateInterpolator() - duration = 750 - } - fadeOut.doOnEnd { - splashScreenViewProvider.remove() - // Re-apply edge to edge AFTER the splash screen view is removed - // to ensure it's not overridden by splash screen cleanup - enableEdgeToEdge() - } - - // Safely animate the icon if it exists - // Known issue: Some OEM devices (Samsung One UI 8, Xiaomi on Android 16) - // may not provide iconView, causing NullPointerException - try { - @Suppress("SENSELESS_COMPARISON") - if (splashIcon != null) { - // Scale down animation - val scaleUp = ObjectAnimator.ofFloat(splashIcon, "scaleX", 1f, 0.5f).apply { - interpolator = AnticipateInterpolator() - duration = 750 - } - - val scaleUpY = ObjectAnimator.ofFloat(splashIcon, "scaleY", 1f, 0.5f).apply { - interpolator = AnticipateInterpolator() - duration = 750 - } - - // rotate - val rotate360 = ObjectAnimator.ofFloat(splashIcon, "rotation", 0f, -90f).apply { - interpolator = AnticipateInterpolator() - duration = 750 - } - - scaleUp.start() - scaleUpY.start() - rotate360.start() - } else { - Log.w("SplashScreen", "iconView is null - OEM device detected") - } - } catch (e: NullPointerException) { - // Handle the edge case where iconView becomes null between check and animation - Log.w("SplashScreen", "NullPointerException on iconView animation - likely OEM device", e) - } - - fadeOut.start() - } catch (e: Exception) { - // Fallback for any unexpected exceptions during animation - Log.e("SplashScreen", "Exception during splash screen animation", e) - try { - splashScreenViewProvider.remove() - } catch (e2: Exception) { - Log.e("SplashScreen", "Exception during splash screen removal", e2) - } - } - } - Log.d("MainActivity", "onCreate with action: ${intent?.action}") handleLocationIntent(intent) @@ -148,9 +85,17 @@ class MainActivity : FragmentActivity() { HapticUtil.initialize(this) // initialize permission registry initPermissionRegistry() - // Initialize viewModel state early for correct initial composition + + // viewModel.check is also called in LaunchedEffect inside setContent. viewModel.check(this) + setContent { + // Confirm composition started and mark app as ready + LaunchedEffect(Unit) { + isAppReady = true + Log.d("MainActivity", "Composition started") + } + val isPitchBlackThemeEnabled by viewModel.isPitchBlackThemeEnabled PatchworkTheme(pitchBlackTheme = isPitchBlackThemeEnabled) { val context = LocalContext.current @@ -286,12 +231,6 @@ class MainActivity : FragmentActivity() { } } } - - - // Mark app as ready after composing (happens very quickly) - LaunchedEffect(Unit) { - isAppReady = true - } } } } diff --git a/app/src/main/java/com/brittytino/patchwork/domain/registry/FeatureRegistry.kt b/app/src/main/java/com/brittytino/patchwork/domain/registry/FeatureRegistry.kt index eee2256..5b709f0 100644 --- a/app/src/main/java/com/brittytino/patchwork/domain/registry/FeatureRegistry.kt +++ b/app/src/main/java/com/brittytino/patchwork/domain/registry/FeatureRegistry.kt @@ -520,6 +520,70 @@ object FeatureRegistry { override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {} }, + object : Feature( + id = "App Behavior Controller", + title = R.string.feat_app_behavior_title, + iconRes = R.drawable.rounded_settings_accessibility_24, + category = R.string.cat_tools, + description = R.string.feat_app_behavior_desc, + permissionKeys = listOf("ACCESSIBILITY"), + showToggle = false + ) { + override fun isEnabled(viewModel: MainViewModel) = true + override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {} + }, + + object : Feature( + id = "Smart App Cooldown", + title = R.string.feat_app_cooldown_title, + iconRes = R.drawable.rounded_timer_24, + category = R.string.cat_tools, + description = R.string.feat_app_cooldown_desc, + permissionKeys = listOf("ACCESSIBILITY", "DRAW_OVERLAYS"), + showToggle = false + ) { + override fun isEnabled(viewModel: MainViewModel) = true + override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {} + }, + + object : Feature( + id = "Idle App Auto-Action", + title = R.string.feat_idle_app_title, + iconRes = R.drawable.rounded_av_timer_24, + category = R.string.cat_tools, + description = R.string.feat_idle_app_desc, + permissionKeys = listOf("USAGE_STATS"), + showToggle = false + ) { + override fun isEnabled(viewModel: MainViewModel) = true + override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {} + }, + + object : Feature( + id = "Action History Timeline", + title = R.string.feat_action_history_title, + iconRes = R.drawable.rounded_fiber_smart_record_24, + category = R.string.cat_tools, + description = R.string.feat_action_history_desc, + showToggle = false + ) { + override fun isEnabled(viewModel: MainViewModel) = true + override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {} + }, + + object : Feature( + id = "System State Snapshots", + title = R.string.feat_system_snapshots_title, + iconRes = R.drawable.rounded_save_24, + category = R.string.cat_tools, + description = R.string.feat_system_snapshots_desc, + permissionKeys = listOf("WRITE_SECURE_SETTINGS"), + showToggle = false + ) { + override fun isEnabled(viewModel: MainViewModel) = true + override fun onToggle(viewModel: MainViewModel, context: Context, enabled: Boolean) {} + }, + object : Feature( id = "Watermarks", title = R.string.feat_watermark_title, diff --git a/app/src/main/java/com/brittytino/patchwork/domain/registry/PermissionRegistry.kt b/app/src/main/java/com/brittytino/patchwork/domain/registry/PermissionRegistry.kt index f89ffb9..a85992b 100644 --- a/app/src/main/java/com/brittytino/patchwork/domain/registry/PermissionRegistry.kt +++ b/app/src/main/java/com/brittytino/patchwork/domain/registry/PermissionRegistry.kt @@ -68,4 +68,11 @@ fun initPermissionRegistry() { // Modify system settings permission PermissionRegistry.register("WRITE_SETTINGS", R.string.feat_qs_tiles_title) + + // New Features + PermissionRegistry.register("USAGE_STATS", R.string.feat_idle_app_title) + PermissionRegistry.register("ACCESSIBILITY", R.string.feat_app_behavior_title) + PermissionRegistry.register("ACCESSIBILITY", R.string.feat_app_cooldown_title) + PermissionRegistry.register("DRAW_OVERLAYS", R.string.feat_app_cooldown_title) + PermissionRegistry.register("WRITE_SECURE_SETTINGS", R.string.feat_system_snapshots_title) } \ No newline at end of file diff --git a/app/src/main/java/com/brittytino/patchwork/ui/components/dialogs/AboutSection.kt b/app/src/main/java/com/brittytino/patchwork/ui/components/dialogs/AboutSection.kt index 167b1d9..8026ccc 100644 --- a/app/src/main/java/com/brittytino/patchwork/ui/components/dialogs/AboutSection.kt +++ b/app/src/main/java/com/brittytino/patchwork/ui/components/dialogs/AboutSection.kt @@ -119,7 +119,7 @@ fun AboutSection( modifier = Modifier.padding(horizontal = 4.dp) ) { Icon( - painter = painterResource(id = R.drawable.brand_github), + painter = painterResource(id = R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp) ) @@ -161,7 +161,7 @@ fun AboutSection( modifier = Modifier.padding(horizontal = 4.dp) ) { Icon( - painter = painterResource(id = R.drawable.brand_telegram), + painter = painterResource(id = R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp) ) diff --git a/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/BugReportBottomSheet.kt b/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/BugReportBottomSheet.kt index 8034bc1..4a35357 100644 --- a/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/BugReportBottomSheet.kt +++ b/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/BugReportBottomSheet.kt @@ -224,7 +224,7 @@ fun BugReportBottomSheet( }, modifier = Modifier.fillMaxWidth() ) { - Icon(painter = painterResource(R.drawable.brand_github), contentDescription = null, modifier = Modifier.size(18.dp)) + Icon(painter = painterResource(R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp)) Spacer(modifier = Modifier.width(8.dp)) Text(stringResource(R.string.action_report_github)) } diff --git a/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/InstructionsBottomSheet.kt b/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/InstructionsBottomSheet.kt index 71caf58..90fd06e 100644 --- a/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/InstructionsBottomSheet.kt +++ b/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/InstructionsBottomSheet.kt @@ -182,7 +182,7 @@ fun InstructionsBottomSheet( modifier = Modifier.padding(horizontal = 4.dp) ) { Icon( - painter = painterResource(id = R.drawable.brand_github), + painter = painterResource(id = R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp) ) @@ -222,7 +222,7 @@ fun InstructionsBottomSheet( modifier = Modifier.padding(horizontal = 4.dp) ) { Icon( - painter = painterResource(id = R.drawable.brand_telegram), + painter = painterResource(id = R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp) ) @@ -336,7 +336,7 @@ fun ExpandableGuideSection(section: InstructionSection) { shape = RoundedCornerShape(12.dp) ) { Icon( - painter = painterResource(id = R.drawable.brand_github), + painter = painterResource(id = R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp) ) diff --git a/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/UpdateBottomSheet.kt b/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/UpdateBottomSheet.kt index f2f95cf..0d33b83 100644 --- a/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/UpdateBottomSheet.kt +++ b/app/src/main/java/com/brittytino/patchwork/ui/components/sheets/UpdateBottomSheet.kt @@ -157,7 +157,7 @@ fun UpdateBottomSheet( modifier = Modifier.fillMaxWidth() ) { Icon( - painter = painterResource(id = R.drawable.brand_github), + painter = painterResource(id = R.drawable.rounded_globe_24), contentDescription = null, modifier = Modifier.size(18.dp) ) diff --git a/app/src/main/java/com/brittytino/patchwork/ui/composables/watermark/WatermarkScreen.kt b/app/src/main/java/com/brittytino/patchwork/ui/composables/watermark/WatermarkScreen.kt index dd29d80..57bdc13 100644 --- a/app/src/main/java/com/brittytino/patchwork/ui/composables/watermark/WatermarkScreen.kt +++ b/app/src/main/java/com/brittytino/patchwork/ui/composables/watermark/WatermarkScreen.kt @@ -967,16 +967,7 @@ private fun LogoCarouselPicker( modifier: Modifier = Modifier ) { val logos = listOf( - R.drawable.apple, - R.drawable.cmf, - R.drawable.google, - R.drawable.moto, - R.drawable.nothing, - R.drawable.oppo, - R.drawable.samsung, - R.drawable.sony, - R.drawable.vivo, - R.drawable.xiaomi + R.mipmap.ic_launcher ) val carouselState = androidx.compose.material3.carousel.rememberCarouselState { logos.size } diff --git a/app/src/main/java/com/brittytino/patchwork/ui/ime/KeyboardInputView.kt b/app/src/main/java/com/brittytino/patchwork/ui/ime/KeyboardInputView.kt index 666d132..eb5f417 100644 --- a/app/src/main/java/com/brittytino/patchwork/ui/ime/KeyboardInputView.kt +++ b/app/src/main/java/com/brittytino/patchwork/ui/ime/KeyboardInputView.kt @@ -402,11 +402,11 @@ fun KeyboardInputView( content = { val functions = remember(isClipboardEnabled) { val list = mutableListOf( - R.drawable.ic_emoji to "Emoji", - R.drawable.ic_undo to "Undo" + R.drawable.rounded_heart_smile_24 to "Emoji", + R.drawable.rounded_arrow_back_24 to "Undo" ) if (isClipboardEnabled) { - list.add(1, R.drawable.ic_clipboard to "Clipboard") + list.add(1, R.drawable.rounded_content_paste_24 to "Clipboard") } list } @@ -644,7 +644,7 @@ fun KeyboardInputView( .fillMaxHeight() ) { Icon( - painter = painterResource(id = R.drawable.key_shift), + painter = painterResource(id = R.drawable.rounded_keyboard_arrow_up_24), contentDescription = "Shift", modifier = Modifier.size(24.dp), tint = if (shiftState != ShiftState.OFF) MaterialTheme.colorScheme.onPrimary diff --git a/app/src/main/java/com/brittytino/patchwork/utils/PermissionUtils.kt b/app/src/main/java/com/brittytino/patchwork/utils/PermissionUtils.kt index 23ca9cd..4d62cd8 100644 --- a/app/src/main/java/com/brittytino/patchwork/utils/PermissionUtils.kt +++ b/app/src/main/java/com/brittytino/patchwork/utils/PermissionUtils.kt @@ -51,6 +51,16 @@ object PermissionUtils { } } + fun hasUsageStatsPermission(context: Context): Boolean { + val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as android.app.AppOpsManager + val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + appOps.unsafeCheckOpNoThrow(android.app.AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName) + } else { + appOps.checkOpNoThrow(android.app.AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName) + } + return mode == android.app.AppOpsManager.MODE_ALLOWED + } + fun isDeviceAdminActive(context: Context): Boolean { val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val adminComponent = ComponentName(context, SecurityDeviceAdminReceiver::class.java) diff --git a/app/src/main/java/com/brittytino/patchwork/utils/RootUtils.kt b/app/src/main/java/com/brittytino/patchwork/utils/RootUtils.kt index fdb8578..6c374e8 100644 --- a/app/src/main/java/com/brittytino/patchwork/utils/RootUtils.kt +++ b/app/src/main/java/com/brittytino/patchwork/utils/RootUtils.kt @@ -8,8 +8,18 @@ object RootUtils { fun isRootAvailable(): Boolean { return try { val process = Runtime.getRuntime().exec(arrayOf("sh", "-c", "which su")) - val exitCode = process.waitFor() - exitCode == 0 + // Add a short timeout to prevent hanging the app on problematic devices + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + if (!process.waitFor(2, java.util.concurrent.TimeUnit.SECONDS)) { + process.destroyForcibly() + return false + } + } else { + // Fallback for older versions (unlikely to be used here but for safety) + val exitCode = process.waitFor() + return exitCode == 0 + } + process.exitValue() == 0 } catch (e: Exception) { false } @@ -19,8 +29,16 @@ object RootUtils { // In many root managers, 'su -c id' will return 0 if granted return try { val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "id")) - val exitCode = process.waitFor() - exitCode == 0 + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + if (!process.waitFor(2, java.util.concurrent.TimeUnit.SECONDS)) { + process.destroyForcibly() + return false + } + } else { + val exitCode = process.waitFor() + return exitCode == 0 + } + process.exitValue() == 0 } catch (e: Exception) { false } diff --git a/app/src/main/java/com/brittytino/patchwork/viewmodels/MainViewModel.kt b/app/src/main/java/com/brittytino/patchwork/viewmodels/MainViewModel.kt index dc7141b..4516a5f 100644 --- a/app/src/main/java/com/brittytino/patchwork/viewmodels/MainViewModel.kt +++ b/app/src/main/java/com/brittytino/patchwork/viewmodels/MainViewModel.kt @@ -86,6 +86,7 @@ class MainViewModel : ViewModel() { val isBackgroundLocationPermissionGranted = mutableStateOf(false) val isFullScreenIntentPermissionGranted = mutableStateOf(false) val isBluetoothPermissionGranted = mutableStateOf(false) + val isUsageStatsPermissionGranted = mutableStateOf(false) val isBluetoothDevicesEnabled = mutableStateOf(false) @@ -241,6 +242,7 @@ class MainViewModel : ViewModel() { isWriteSettingsEnabled.value = PermissionUtils.canWriteSystemSettings(context) isBluetoothPermissionGranted.value = PermissionUtils.hasBluetoothPermission(context) + isUsageStatsPermissionGranted.value = PermissionUtils.hasUsageStatsPermission(context) settingsRepository.registerOnSharedPreferenceChangeListener(preferenceChangeListener) @@ -263,12 +265,20 @@ class MainViewModel : ViewModel() { notificationLightingIndicatorY.value = settingsRepository.getFloat(SettingsRepository.KEY_EDGE_LIGHTING_INDICATOR_Y, 2f) isRootEnabled.value = settingsRepository.getBoolean(SettingsRepository.KEY_USE_ROOT) - if (isRootEnabled.value) { - isRootAvailable.value = com.brittytino.patchwork.utils.RootUtils.isRootAvailable() - isRootPermissionGranted.value = com.brittytino.patchwork.utils.RootUtils.isRootPermissionGranted() - } else { - isRootAvailable.value = false - isRootPermissionGranted.value = false + viewModelScope.launch(Dispatchers.IO) { + if (isRootEnabled.value) { + val available = com.brittytino.patchwork.utils.RootUtils.isRootAvailable() + val granted = com.brittytino.patchwork.utils.RootUtils.isRootPermissionGranted() + withContext(Dispatchers.Main) { + isRootAvailable.value = available + isRootPermissionGranted.value = granted + } + } else { + withContext(Dispatchers.Main) { + isRootAvailable.value = false + isRootPermissionGranted.value = false + } + } } notificationLightingIndicatorScale.value = settingsRepository.getFloat(SettingsRepository.KEY_EDGE_LIGHTING_INDICATOR_SCALE, 1.0f) diff --git a/app/src/main/java/com/brittytino/patchwork/viewmodels/WatermarkViewModel.kt b/app/src/main/java/com/brittytino/patchwork/viewmodels/WatermarkViewModel.kt index e7e2181..7a62624 100644 --- a/app/src/main/java/com/brittytino/patchwork/viewmodels/WatermarkViewModel.kt +++ b/app/src/main/java/com/brittytino/patchwork/viewmodels/WatermarkViewModel.kt @@ -80,22 +80,8 @@ class WatermarkViewModel( } private fun detectOemLogo(exif: ExifData): Int? { - val make = exif.make?.lowercase() ?: "" - val model = exif.model?.lowercase() ?: "" - - return when { - make.contains("apple") || model.contains("iphone") -> R.drawable.apple - make.contains("google") || model.contains("pixel") -> R.drawable.google - make.contains("samsung") -> R.drawable.samsung - make.contains("xiaomi") || make.contains("redmi") || make.contains("poco") -> R.drawable.xiaomi - make.contains("oppo") -> R.drawable.oppo - make.contains("vivo") -> R.drawable.vivo - make.contains("sony") -> R.drawable.sony - make.contains("nothing") -> R.drawable.nothing - make.contains("cmf") -> R.drawable.cmf - make.contains("motorola") || make.contains("moto") -> R.drawable.moto - else -> null - } + // Updated to use the new app logo for all watermarks as requested + return R.mipmap.ic_launcher } fun loadPreview(uri: Uri) { diff --git a/app/src/main/res/drawable/apple.xml b/app/src/main/res/drawable/apple.xml deleted file mode 100644 index 1c20b80..0000000 --- a/app/src/main/res/drawable/apple.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/brand_github.xml b/app/src/main/res/drawable/brand_github.xml deleted file mode 100644 index e26e25c..0000000 --- a/app/src/main/res/drawable/brand_github.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/brand_telegram.xml b/app/src/main/res/drawable/brand_telegram.xml deleted file mode 100644 index 6c3318e..0000000 --- a/app/src/main/res/drawable/brand_telegram.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/cmf.xml b/app/src/main/res/drawable/cmf.xml deleted file mode 100644 index 464772b..0000000 --- a/app/src/main/res/drawable/cmf.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/google.xml b/app/src/main/res/drawable/google.xml deleted file mode 100644 index c0496e4..0000000 --- a/app/src/main/res/drawable/google.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_clipboard.xml b/app/src/main/res/drawable/ic_clipboard.xml deleted file mode 100644 index 2fc6497..0000000 --- a/app/src/main/res/drawable/ic_clipboard.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_emoji.xml b/app/src/main/res/drawable/ic_emoji.xml deleted file mode 100644 index 7c66b44..0000000 --- a/app/src/main/res/drawable/ic_emoji.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_undo.xml b/app/src/main/res/drawable/ic_undo.xml deleted file mode 100644 index c7e9edd..0000000 --- a/app/src/main/res/drawable/ic_undo.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/key_shift.xml b/app/src/main/res/drawable/key_shift.xml deleted file mode 100644 index 0294fd9..0000000 --- a/app/src/main/res/drawable/key_shift.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/moto.xml b/app/src/main/res/drawable/moto.xml deleted file mode 100644 index e568364..0000000 --- a/app/src/main/res/drawable/moto.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/nothing.xml b/app/src/main/res/drawable/nothing.xml deleted file mode 100644 index b4153c1..0000000 --- a/app/src/main/res/drawable/nothing.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/oppo.xml b/app/src/main/res/drawable/oppo.xml deleted file mode 100644 index 1875ee4..0000000 --- a/app/src/main/res/drawable/oppo.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/samsung.xml b/app/src/main/res/drawable/samsung.xml deleted file mode 100644 index 7fc874f..0000000 --- a/app/src/main/res/drawable/samsung.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/sony.xml b/app/src/main/res/drawable/sony.xml deleted file mode 100644 index cded39a..0000000 --- a/app/src/main/res/drawable/sony.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/vivo.xml b/app/src/main/res/drawable/vivo.xml deleted file mode 100644 index 8d6174f..0000000 --- a/app/src/main/res/drawable/vivo.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/xiaomi.xml b/app/src/main/res/drawable/xiaomi.xml deleted file mode 100644 index 95cd9fc..0000000 --- a/app/src/main/res/drawable/xiaomi.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 50ec886..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 50ec886..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.jpg b/app/src/main/res/mipmap-hdpi/ic_launcher.jpg new file mode 100644 index 0000000..1d5815e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.jpg differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index f2874b5..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.jpg b/app/src/main/res/mipmap-hdpi/ic_launcher_round.jpg new file mode 100644 index 0000000..1d5815e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.jpg differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index 18eec59..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.jpg b/app/src/main/res/mipmap-mdpi/ic_launcher.jpg new file mode 100644 index 0000000..d68b1b6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.jpg differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 48acbd6..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.jpg b/app/src/main/res/mipmap-mdpi/ic_launcher_round.jpg new file mode 100644 index 0000000..d68b1b6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.jpg differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 0a0da75..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.jpg b/app/src/main/res/mipmap-xhdpi/ic_launcher.jpg new file mode 100644 index 0000000..aeecb36 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.jpg differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 4413e81..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.jpg b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.jpg new file mode 100644 index 0000000..aeecb36 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.jpg differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 3d814dd..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.jpg b/app/src/main/res/mipmap-xxhdpi/ic_launcher.jpg new file mode 100644 index 0000000..ec9b73e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.jpg differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index b03744e..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.jpg b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.jpg new file mode 100644 index 0000000..ec9b73e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.jpg differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 5fbbcff..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.jpg b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.jpg new file mode 100644 index 0000000..4bdb92c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.jpg differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index dd7c598..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.jpg b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.jpg new file mode 100644 index 0000000..4bdb92c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.jpg differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index f9eec84..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/values-night/splash.xml b/app/src/main/res/values-night/splash.xml index f97eb2e..b6ce686 100644 --- a/app/src/main/res/values-night/splash.xml +++ b/app/src/main/res/values-night/splash.xml @@ -7,7 +7,7 @@ false @color/app_window_background @mipmap/ic_launcher - 2000 + 1000 @style/Theme.Patchwork diff --git a/app/src/main/res/values-v31/splash.xml b/app/src/main/res/values-v31/splash.xml index b23ad4b..5400b74 100644 --- a/app/src/main/res/values-v31/splash.xml +++ b/app/src/main/res/values-v31/splash.xml @@ -5,8 +5,8 @@ false true false - @drawable/ic_launcher_foreground_light - 3000 + @mipmap/ic_launcher + 1000 @style/Theme.Patchwork diff --git a/app/src/main/res/values/splash.xml b/app/src/main/res/values/splash.xml index b0c1e72..b6ce686 100644 --- a/app/src/main/res/values/splash.xml +++ b/app/src/main/res/values/splash.xml @@ -6,8 +6,8 @@ true false @color/app_window_background - @drawable/ic_launcher_foreground_light - 3000 + @mipmap/ic_launcher + 1000 @style/Theme.Patchwork diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8a03472..0cd3a7d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -319,6 +319,23 @@ View all Button remap Remap hardware button actions + + + App Behavior Controller + Define per-app rules for volume, screen, etc. + + Smart App Cooldown + Prevent compulsive app reopening with delays + + Idle App Auto-Action + Auto-freeze or notify for unused apps + + Action History Timeline + View log of all Patchwork actions + + System State Snapshots + Save and restore system profiles + Dynamic night light Toggle night light based on app Screen locked security