Add five independent Android incoming call preferences
This commit is contained in:
1 parent
97b537c005
commit
7d5399cd62
3 files changed
+42
-20
No files matched your search
@@ -85,12 +85,17 @@ class ConfigProvider(private val context: Context) {
|
||||
private val serviceEnabledKey = booleanPreferencesKey("service_enabled")
|
||||
private val startOnBootKey = booleanPreferencesKey("start_on_boot")
|
||||
private val soundEnabledKey = booleanPreferencesKey("sound_enabled")
|
||||
private val phoneStatusEnabledKey = booleanPreferencesKey("phone_status_enabled")
|
||||
val phoneStatusEnabled: Flow<Boolean> = context.dataStore.data.map { it[phoneStatusEnabledKey] ?: false }.distinctUntilChanged()
|
||||
// Порядок совпадает с CALL_POLICY_* ядра: показ, предупреждение, отказ при занятости, запрет звонков, запрет рации.
|
||||
private val incomingCallKeys = listOf("phone_status_enabled", "phone_notify_enabled", "phone_reject_enabled",
|
||||
"block_incoming_calls", "block_incoming_radio").map(::booleanPreferencesKey)
|
||||
val incomingCallOptions: Flow<List<Boolean>> = context.dataStore.data.map { prefs ->
|
||||
incomingCallKeys.map { prefs[it] ?: false }
|
||||
}.distinctUntilChanged()
|
||||
|
||||
suspend fun setPhoneStatusEnabled(enabled: Boolean) {
|
||||
context.dataStore.edit { it[phoneStatusEnabledKey] = enabled }
|
||||
LogManager.addLog("INFO", "PhoneState", "Phone status monitoring enabled=$enabled")
|
||||
suspend fun setIncomingCallOption(option: Int, enabled: Boolean) {
|
||||
require(option in incomingCallKeys.indices)
|
||||
context.dataStore.edit { it[incomingCallKeys[option]] = enabled }
|
||||
LogManager.addLog("INFO", "PhoneState", "${incomingCallKeys[option].name}=$enabled")
|
||||
}
|
||||
private val mutedChannelsKey = stringSetPreferencesKey("muted_channels")
|
||||
private val callAudioRouteKey = stringPreferencesKey("call_audio_route")
|
||||
@@ -706,6 +711,9 @@ class ConfigProvider(private val context: Context) {
|
||||
appendLine("enabled=no")
|
||||
appendLine()
|
||||
appendLine("[chat]")
|
||||
val incoming = runBlocking { incomingCallOptions.first() }
|
||||
val policy = incoming.withIndex().fold(0) { mask, (index, value) -> mask or if (value) (1 shl index) else 0 }
|
||||
appendLine("incoming_call_policy=$policy")
|
||||
val chatAutoload = getInt("chat.storage_autoload")
|
||||
val chatAutoloadMaxMb = getInt("chat.storage_autoload_maxsize_mb")
|
||||
val chatMaxGb = getInt("chat.storage_maxsize_gb")
|
||||
|
||||
@@ -25,6 +25,7 @@ object PhoneStateMonitor {
|
||||
private val watches = mutableListOf<Watch>()
|
||||
private val states = mutableMapOf<Int, Int>()
|
||||
private var enabled = false
|
||||
private var policy = 0
|
||||
private var generation = 0
|
||||
private var lastState = -1
|
||||
private var started = false
|
||||
@@ -46,12 +47,10 @@ object PhoneStateMonitor {
|
||||
if (started) return
|
||||
started = true
|
||||
val app = ChatApplication.instance
|
||||
app.packageManager.addOnPermissionsChangeListener { uid ->
|
||||
if (uid == app.applicationInfo.uid) main.post { refresh() }
|
||||
}
|
||||
scope.launch {
|
||||
app.configProvider.phoneStatusEnabled.collect { value ->
|
||||
enabled = value
|
||||
app.configProvider.incomingCallOptions.collect { options ->
|
||||
policy = options.withIndex().fold(0) { mask, (index, value) -> mask or if (value) (1 shl index) else 0 }
|
||||
enabled = policy and 7 != 0
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
@@ -110,6 +109,7 @@ object PhoneStateMonitor {
|
||||
|
||||
private fun changed(token: Int, id: Int, state: Int) {
|
||||
if (token != generation || !enabled || !states.containsKey(id)) return
|
||||
if (!granted()) { refresh(); return }
|
||||
states[id] = state
|
||||
publishAggregate()
|
||||
}
|
||||
@@ -122,6 +122,6 @@ object PhoneStateMonitor {
|
||||
private fun publish(state: Int) {
|
||||
if (lastState != state) LogManager.addLog("INFO", "PhoneState", "State=$state enabled=$enabled subscriptions=${states.size}")
|
||||
lastState = state
|
||||
if (NativeLib.isReady()) NativeLib.setPhoneState(state)
|
||||
if (NativeLib.isReady()) NativeLib.setPhoneState(state, policy)
|
||||
}
|
||||
}
|
||||
@@ -1046,26 +1046,40 @@ private fun AudioSettingsTab(vm: ChatViewModel) {
|
||||
|
||||
/* ── Входящие звонки поверх блокировки ── */
|
||||
Text(stringResource(R.string.incoming_calls), style = MaterialTheme.typography.titleMedium)
|
||||
val phoneStatusEnabled by provider.phoneStatusEnabled.collectAsState(initial = false)
|
||||
val phoneOptions by provider.incomingCallOptions.collectAsState(initial = List(5) { false })
|
||||
var requestedPhoneOption by rememberSaveable { mutableStateOf(0) }
|
||||
var phonePermission by remember { mutableStateOf(PhoneStateMonitor.granted()) }
|
||||
val phonePermissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
|
||||
phonePermission = granted
|
||||
LogManager.addLog(if (granted) "INFO" else "WARN", "PhoneState", "Phone permission result granted=$granted")
|
||||
scope.launch { provider.setPhoneStatusEnabled(granted) }
|
||||
scope.launch {
|
||||
provider.setIncomingCallOption(requestedPhoneOption, granted)
|
||||
}
|
||||
}
|
||||
LifecycleResumeEffect(Unit) {
|
||||
phonePermission = PhoneStateMonitor.granted()
|
||||
onPauseOrDispose { }
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
val phoneTitles = listOf(R.string.phone_status_setting, R.string.phone_notify_setting, R.string.phone_reject_setting,
|
||||
R.string.block_incoming_calls, R.string.block_incoming_radio)
|
||||
val phoneDescriptions = listOf(R.string.phone_status_description, R.string.phone_notify_description,
|
||||
R.string.phone_reject_description, R.string.block_incoming_calls_description, R.string.block_incoming_radio_description)
|
||||
for (option in phoneTitles.indices) Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
val selected = phoneOptions[option]
|
||||
val needsPermission = option < 3
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(stringResource(R.string.phone_status_setting))
|
||||
Text(stringResource(if (phoneStatusEnabled && !phonePermission) R.string.phone_status_permission_missing
|
||||
else R.string.phone_status_description), style = MaterialTheme.typography.bodySmall)
|
||||
Text(stringResource(phoneTitles[option]))
|
||||
Text(stringResource(if (selected && needsPermission && !phonePermission) R.string.phone_status_permission_missing
|
||||
else phoneDescriptions[option]),
|
||||
style = MaterialTheme.typography.bodySmall)
|
||||
}
|
||||
Switch(checked = phoneStatusEnabled && phonePermission, onCheckedChange = { value ->
|
||||
if (value && !PhoneStateMonitor.granted()) phonePermissionLauncher.launch(Manifest.permission.READ_PHONE_STATE)
|
||||
else scope.launch { provider.setPhoneStatusEnabled(value) }
|
||||
Switch(checked = selected, onCheckedChange = { value ->
|
||||
if (value && needsPermission && !PhoneStateMonitor.granted()) {
|
||||
requestedPhoneOption = option
|
||||
phonePermissionLauncher.launch(Manifest.permission.READ_PHONE_STATE)
|
||||
} else scope.launch {
|
||||
provider.setIncomingCallOption(option, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
var fsiEnabled by remember { mutableStateOf(IncomingCallManager.canFullScreenIntent()) }
|
||||
|
||||
Reference in new issue
Block a user