Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,6 @@
</intent-filter>
</activity>

<activity
android:name=".ShutUpShortcutActivity"
android:exported="true"
android:excludeFromRecents="true"
android:noHistory="true"
android:taskAffinity=""
android:theme="@style/Theme.Essentials.Translucent" />

<activity
android:name=".ui.activities.FlashlightIntensityActivity"
android:exported="true"
Expand Down Expand Up @@ -903,6 +895,27 @@
android:value="android.service.quicksettings.CATEGORY_DISPLAY" />
</service>

<activity
android:name=".ShutUpShortcutActivity"
android:exported="true"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@style/Theme.Essentials.Translucent">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<service
android:name=".services.ShutUpForegroundService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="specialUse">
<property android:name="android.app.property.FOREGROUND_SERVICE_TYPE_SPECIAL_USE_DESCRIPTION"
android:value="Shut-Up! background monitoring service" />
</service>

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ import com.sameerasw.essentials.ui.composables.configs.NotificationLightingSetti
import com.sameerasw.essentials.ui.composables.configs.OtherCustomizationsSettingsUI
import com.sameerasw.essentials.ui.composables.configs.QuickSettingsTilesSettingsUI
import com.sameerasw.essentials.ui.composables.configs.RefreshRateSettingsUI
import com.sameerasw.essentials.ui.composables.configs.ShutUpSettingsUI
import com.sameerasw.essentials.ui.composables.configs.RemoteLockSettingsUI
import com.sameerasw.essentials.ui.composables.configs.ScreenLockedSecuritySettingsUI
import com.sameerasw.essentials.ui.composables.configs.ScreenOffWidgetSettingsUI
import com.sameerasw.essentials.ui.composables.configs.ShutUpSettingsUI
import com.sameerasw.essentials.ui.composables.configs.SnoozeNotificationsSettingsUI
import com.sameerasw.essentials.ui.composables.configs.SoundModeTileSettingsUI
import com.sameerasw.essentials.ui.composables.configs.StatusBarIconSettingsUI
Expand Down Expand Up @@ -215,6 +215,9 @@ class FeatureSettingsActivity : AppCompatActivity() {
val isNotificationListenerEnabled by viewModel.isNotificationListenerEnabled
val isReadPhoneStateEnabled by viewModel.isReadPhoneStateEnabled
val isShizukuPermissionGranted by viewModel.isShizukuPermissionGranted
val isWriteSettingsEnabled by viewModel.isWriteSettingsEnabled
val isUsageStatsPermissionGranted by viewModel.isUsageStatsPermissionGranted
val isPostNotificationsEnabled by viewModel.isPostNotificationsEnabled

// FAB State for Notification Lighting
var fabExpanded by remember { mutableStateOf(true) }
Expand Down Expand Up @@ -246,7 +249,10 @@ class FeatureSettingsActivity : AppCompatActivity() {
isNotificationLightingAccessibilityEnabled,
isNotificationListenerEnabled,
isReadPhoneStateEnabled,
isShizukuPermissionGranted
isShizukuPermissionGranted,
isWriteSettingsEnabled,
isUsageStatsPermissionGranted,
isPostNotificationsEnabled
) {
val hasMissingPermissions = when (featureId) {
"Screen off widget" -> !isAccessibilityEnabled
Expand All @@ -264,6 +270,7 @@ class FeatureSettingsActivity : AppCompatActivity() {
"Location reached" -> !viewModel.isLocationPermissionGranted.value || !viewModel.isBackgroundLocationPermissionGranted.value
"Quick settings tiles" -> !viewModel.isWriteSettingsEnabled.value
"Screen refresh rate" -> !viewModel.isShizukuPermissionGranted.value
"Shut-Up!" -> !isWriteSecureSettingsEnabled || !isWriteSettingsEnabled || !isUsageStatsPermissionGranted || !isPostNotificationsEnabled
// Top level checks for other features (rarely hit if they are children, but safe to add)
"Essentials On Display" -> !isAccessibilityEnabled || !isNotificationListenerEnabled
"Call vibrations" -> !isReadPhoneStateEnabled || !isNotificationListenerEnabled
Expand All @@ -280,7 +287,6 @@ class FeatureSettingsActivity : AppCompatActivity() {
context
)

"Shut-Up!" -> !isWriteSecureSettingsEnabled || !viewModel.isUsageStatsPermissionGranted.value
else -> false
}
if (hasMissingPermissions) {
Expand Down Expand Up @@ -497,7 +503,7 @@ class FeatureSettingsActivity : AppCompatActivity() {
"Text and animations" -> !viewModel.isWriteSettingsEnabled.value || !isWriteSecureSettingsEnabled
"Lock screen clock" -> !isWriteSecureSettingsEnabled
"Screen refresh rate" -> !viewModel.isShizukuPermissionGranted.value
"Shut-Up!" -> !isWriteSecureSettingsEnabled || !viewModel.isUsageStatsPermissionGranted.value
"Shut-Up!" -> !isWriteSecureSettingsEnabled || !isWriteSettingsEnabled || !isUsageStatsPermissionGranted || !isPostNotificationsEnabled
else -> false
}

Expand Down Expand Up @@ -743,6 +749,14 @@ class FeatureSettingsActivity : AppCompatActivity() {
)
}

"Shut-Up!" -> {
ShutUpSettingsUI(
viewModel = viewModel,
modifier = Modifier.padding(top = 16.dp),
highlightSetting = highlightSetting
)
}

"Always on Display" -> {
AlwaysOnDisplaySettingsUI(
viewModel = viewModel,
Expand Down Expand Up @@ -774,14 +788,6 @@ class FeatureSettingsActivity : AppCompatActivity() {
highlightSetting = highlightSetting
)
}

"Shut-Up!" -> {
ShutUpSettingsUI(
viewModel = viewModel,
modifier = Modifier.padding(top = 16.dp),
highlightKey = highlightSetting
)
}
}

}
Expand Down
142 changes: 6 additions & 136 deletions app/src/main/java/com/sameerasw/essentials/ShutUpShortcutActivity.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.sameerasw.essentials

import android.content.ContentResolver

import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import android.util.Log

import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
Expand All @@ -19,8 +18,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.lifecycle.lifecycleScope
import com.sameerasw.essentials.data.repository.SettingsRepository
import com.sameerasw.essentials.domain.model.ShutUpAppConfig

import com.sameerasw.essentials.ui.theme.EssentialsTheme

import com.sameerasw.essentials.utils.ShutUpManager
import com.sameerasw.essentials.utils.PermissionUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -77,7 +78,7 @@ class ShutUpShortcutActivity : ComponentActivity() {

if (config != null && config.isEnabled) {
if (PermissionUtils.canWriteSecureSettings(this@ShutUpShortcutActivity)) {
applyShutUpSettings(config, settingsRepository)
ShutUpManager.applyShutUpSettings(this@ShutUpShortcutActivity, config)
withContext(Dispatchers.Main) {
Toast.makeText(
this@ShutUpShortcutActivity,
Expand All @@ -96,137 +97,6 @@ class ShutUpShortcutActivity : ComponentActivity() {
}
}

private suspend fun applyShutUpSettings(
config: ShutUpAppConfig,
repository: SettingsRepository
) {
withContext(Dispatchers.IO) {
val originalSettings = mutableMapOf<String, String>()

if (config.disableDevOptions) {
// Backup all relevant dev settings because disabling the main toggle might reset them
val secureSettings = listOf(
"anr_show_background",
"bugreport_in_power_menu",
"display_density_forced",
"mock_location",
"secure_overlay_settings",
"usb_audio_automatic_routing_disabled"
)
val systemSettings = listOf("show_touches", "show_key_presses")
val globalSettings = listOf(
"adb_allowed_connection_time",
"adb_enabled",
"adb_wifi_enabled",
"always_finish_activities",
"animator_duration_scale",
"app_standby_enabled",
"cached_apps_freezer",
"default_install_location",
"development_settings_enabled",
"disable_window_blurs",
"enable_freeform_support",
"enable_non_resizable_multi_window",
"force_allow_on_external",
"force_desktop_mode_on_external_displays",
"force_resizable_activities",
"mobile_data_always_on",
"stay_on_while_plugged_in",
"usb_mass_storage_enabled",
"wait_for_debugger",
"wifi_display_certification_on",
"wifi_display_on",
"wifi_scan_always_enabled",
"window_animation_scale"
)

secureSettings.forEach { key ->
safeReadSetting(contentResolver, SettingsTable.SECURE, key)
?.let { originalSettings["secure:$key"] = it }
}
systemSettings.forEach { key ->
safeReadSetting(contentResolver, SettingsTable.SYSTEM, key)
?.let { originalSettings["system:$key"] = it }
}
globalSettings.forEach { key ->
safeReadSetting(contentResolver, SettingsTable.GLOBAL, key)
?.let { originalSettings["global:$key"] = it }
}

// Disable dev options
Settings.Global.putString(
contentResolver,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
"0"
)
}

// Always explicitly disable USB debugging if requested, even if dev options were already disabled
// as some apps check this specific setting directly.
if (config.disableUsbDebugging) {
val current =
safeReadSetting(contentResolver, SettingsTable.GLOBAL, Settings.Global.ADB_ENABLED)
?: "0"
if (current == "1") {
if (!originalSettings.containsKey("global:${Settings.Global.ADB_ENABLED}")) {
originalSettings["global:${Settings.Global.ADB_ENABLED}"] = "1"
}
Settings.Global.putString(contentResolver, Settings.Global.ADB_ENABLED, "0")
}
}

if (config.disableWirelessDebugging) {
val current =
safeReadSetting(contentResolver, SettingsTable.GLOBAL, "adb_wifi_enabled") ?: "0"
if (current == "1") {
if (!originalSettings.containsKey("global:adb_wifi_enabled")) {
originalSettings["global:adb_wifi_enabled"] = "1"
}
Settings.Global.putString(contentResolver, "adb_wifi_enabled", "0")
}
}

if (config.disableAccessibility) {
val current = safeReadSetting(
contentResolver,
SettingsTable.SECURE,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
)
if (!current.isNullOrEmpty()) {
originalSettings["secure:${Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES}"] =
current
Settings.Secure.putString(
contentResolver,
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
""
)
}
}

if (originalSettings.isNotEmpty()) {
repository.saveShutUpOriginalSettings(originalSettings)
}
}
}

private enum class SettingsTable { SYSTEM, SECURE, GLOBAL }

// Android 12+ throws SecurityException reading @hide settings that aren't
// @Readable (e.g. show_key_presses). WRITE_SECURE_SETTINGS doesn't cover reads.
private fun safeReadSetting(
resolver: ContentResolver,
table: SettingsTable,
key: String
): String? = try {
when (table) {
SettingsTable.SYSTEM -> Settings.System.getString(resolver, key)
SettingsTable.SECURE -> Settings.Secure.getString(resolver, key)
SettingsTable.GLOBAL -> Settings.Global.getString(resolver, key)
}
} catch (e: SecurityException) {
Log.w("ShutUpShortcut", "Skipping unreadable setting $table:$key", e)
null
}

private fun launchApp(packageName: String) {
val intent = packageManager.getLaunchIntentForPackage(packageName)
Expand Down
Loading