diff --git a/CHANGELOG.md b/CHANGELOG.md index b4ca051484..b3cc9aa9b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## Added +- #2163 Add ringer mode constraints (Ring, Vibrate, Silent). - #2140 Add monochrome app icon layer for themed icon support on Android 13+. ## Fixed diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt index a2aa471fbd..e8cf0a8361 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt @@ -189,6 +189,21 @@ sealed class ConstraintData { override val id: ConstraintId = ConstraintId.PHONE_RINGING } + @Serializable + data object RingerModeNormal : ConstraintData() { + override val id: ConstraintId = ConstraintId.RINGER_MODE_NORMAL + } + + @Serializable + data object RingerModeVibrate : ConstraintData() { + override val id: ConstraintId = ConstraintId.RINGER_MODE_VIBRATE + } + + @Serializable + data object RingerModeSilent : ConstraintData() { + override val id: ConstraintId = ConstraintId.RINGER_MODE_SILENT + } + @Serializable data object Charging : ConstraintData() { override val id: ConstraintId = ConstraintId.CHARGING @@ -382,6 +397,10 @@ object ConstraintEntityMapper { ConstraintEntity.IN_PHONE_CALL -> ConstraintData.InPhoneCall ConstraintEntity.NOT_IN_PHONE_CALL -> ConstraintData.NotInPhoneCall + ConstraintEntity.RINGER_MODE_NORMAL -> ConstraintData.RingerModeNormal + ConstraintEntity.RINGER_MODE_VIBRATE -> ConstraintData.RingerModeVibrate + ConstraintEntity.RINGER_MODE_SILENT -> ConstraintData.RingerModeSilent + ConstraintEntity.CHARGING -> ConstraintData.Charging ConstraintEntity.DISCHARGING -> ConstraintData.Discharging @@ -673,6 +692,21 @@ object ConstraintEntityMapper { ConstraintEntity.PHONE_RINGING, ) + is ConstraintData.RingerModeNormal -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_NORMAL, + ) + + is ConstraintData.RingerModeVibrate -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_VIBRATE, + ) + + is ConstraintData.RingerModeSilent -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.RINGER_MODE_SILENT, + ) + is ConstraintData.Charging -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.CHARGING, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt index 4bbbcf1721..afab1a49ca 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt @@ -16,6 +16,7 @@ enum class ConstraintDependency { DEVICE_LOCKED_STATE, LOCK_SCREEN_SHOWING, PHONE_STATE, + RINGER_MODE, CHARGING_STATE, HINGE_STATE, } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt index 06463c6fd7..1118a9d186 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt @@ -54,6 +54,10 @@ enum class ConstraintId { NOT_IN_PHONE_CALL, PHONE_RINGING, + RINGER_MODE_NORMAL, + RINGER_MODE_VIBRATE, + RINGER_MODE_SILENT, + CHARGING, DISCHARGING, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt index 00ac3781ee..9ee0521dcb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt @@ -21,6 +21,8 @@ import io.github.sds100.keymapper.system.network.NetworkAdapter import io.github.sds100.keymapper.system.phone.CallState import io.github.sds100.keymapper.system.phone.PhoneAdapter import io.github.sds100.keymapper.system.power.PowerAdapter +import io.github.sds100.keymapper.system.volume.RingerMode +import io.github.sds100.keymapper.system.volume.VolumeAdapter import java.time.LocalTime /** @@ -38,6 +40,7 @@ class LazyConstraintSnapshot( phoneAdapter: PhoneAdapter, powerAdapter: PowerAdapter, private val foldableAdapter: FoldableAdapter, + volumeAdapter: VolumeAdapter, ) : ConstraintSnapshot { private val appInForeground: String? by lazy { accessibilityService.rootNode?.packageName } private val connectedBluetoothDevices: Set by lazy { @@ -66,6 +69,7 @@ class LazyConstraintSnapshot( } private val callState: CallState by lazy { phoneAdapter.getCallState() } private val isCharging: Boolean by lazy { powerAdapter.isCharging.value } + private val ringerMode: RingerMode by lazy { volumeAdapter.ringerMode } private val isLocked: Boolean by lazy { lockScreenAdapter.isLocked() @@ -165,6 +169,10 @@ class LazyConstraintSnapshot( callState == CallState.RINGING || audioVolumeStreams.contains(AudioManager.STREAM_RING) + is ConstraintData.RingerModeNormal -> ringerMode == RingerMode.NORMAL + is ConstraintData.RingerModeVibrate -> ringerMode == RingerMode.VIBRATE + is ConstraintData.RingerModeSilent -> ringerMode == RingerMode.SILENT + is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt index 94b02aebc9..e9bb6064f4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt @@ -163,6 +163,9 @@ class ConstraintUiHelper( is ConstraintData.InPhoneCall -> getString(R.string.constraint_in_phone_call) is ConstraintData.NotInPhoneCall -> getString(R.string.constraint_not_in_phone_call) is ConstraintData.PhoneRinging -> getString(R.string.constraint_phone_ringing) + is ConstraintData.RingerModeNormal -> getString(R.string.constraint_ringer_mode_normal) + is ConstraintData.RingerModeVibrate -> getString(R.string.constraint_ringer_mode_vibrate) + is ConstraintData.RingerModeSilent -> getString(R.string.constraint_ringer_mode_silent) is ConstraintData.Charging -> getString(R.string.constraint_charging) is ConstraintData.Discharging -> getString(R.string.constraint_discharging) is ConstraintData.HingeClosed -> getString(R.string.constraint_hinge_closed_description) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt index 80cd56c1c7..7bb26b672f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt @@ -15,6 +15,9 @@ import androidx.compose.material.icons.outlined.KeyboardHide import androidx.compose.material.icons.outlined.Lock import androidx.compose.material.icons.outlined.LockOpen import androidx.compose.material.icons.outlined.MobileOff +import androidx.compose.material.icons.outlined.Notifications +import androidx.compose.material.icons.outlined.NotificationsOff +import androidx.compose.material.icons.outlined.Vibration import androidx.compose.material.icons.outlined.PlayArrow import androidx.compose.material.icons.outlined.RingVolume import androidx.compose.material.icons.outlined.ScreenLockPortrait @@ -101,6 +104,9 @@ object ConstraintUtils { ConstraintId.IN_PHONE_CALL, ConstraintId.NOT_IN_PHONE_CALL, ConstraintId.PHONE_RINGING, + ConstraintId.RINGER_MODE_NORMAL, + ConstraintId.RINGER_MODE_VIBRATE, + ConstraintId.RINGER_MODE_SILENT, -> ConstraintCategory.PHONE ConstraintId.CHARGING, @@ -180,6 +186,9 @@ object ConstraintUtils { ConstraintId.IN_PHONE_CALL -> ComposeIconInfo.Vector(Icons.Outlined.Call) ConstraintId.NOT_IN_PHONE_CALL -> ComposeIconInfo.Vector(Icons.Outlined.CallEnd) ConstraintId.PHONE_RINGING -> ComposeIconInfo.Vector(Icons.Outlined.RingVolume) + ConstraintId.RINGER_MODE_NORMAL -> ComposeIconInfo.Vector(Icons.Outlined.Notifications) + ConstraintId.RINGER_MODE_VIBRATE -> ComposeIconInfo.Vector(Icons.Outlined.Vibration) + ConstraintId.RINGER_MODE_SILENT -> ComposeIconInfo.Vector(Icons.Outlined.NotificationsOff) ConstraintId.CHARGING -> ComposeIconInfo.Vector(Icons.Outlined.BatteryChargingFull) ConstraintId.DISCHARGING -> ComposeIconInfo.Vector(Icons.Outlined.Battery2Bar) @@ -236,6 +245,9 @@ object ConstraintUtils { ConstraintId.IN_PHONE_CALL -> R.string.constraint_in_phone_call ConstraintId.NOT_IN_PHONE_CALL -> R.string.constraint_not_in_phone_call ConstraintId.PHONE_RINGING -> R.string.constraint_phone_ringing + ConstraintId.RINGER_MODE_NORMAL -> R.string.constraint_ringer_mode_normal + ConstraintId.RINGER_MODE_VIBRATE -> R.string.constraint_ringer_mode_vibrate + ConstraintId.RINGER_MODE_SILENT -> R.string.constraint_ringer_mode_silent ConstraintId.CHARGING -> R.string.constraint_charging ConstraintId.DISCHARGING -> R.string.constraint_discharging ConstraintId.HINGE_CLOSED -> R.string.constraint_hinge_closed diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt index 8bf75d6f24..23da542247 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt @@ -16,6 +16,7 @@ import io.github.sds100.keymapper.system.media.MediaAdapter import io.github.sds100.keymapper.system.network.NetworkAdapter import io.github.sds100.keymapper.system.phone.PhoneAdapter import io.github.sds100.keymapper.system.power.PowerAdapter +import io.github.sds100.keymapper.system.volume.VolumeAdapter import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map @@ -34,6 +35,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( private val phoneAdapter: PhoneAdapter, private val powerAdapter: PowerAdapter, private val foldableAdapter: FoldableAdapter, + private val volumeAdapter: VolumeAdapter, ) : DetectConstraintsUseCase { @AssistedFactory @@ -53,6 +55,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( phoneAdapter, powerAdapter, foldableAdapter, + volumeAdapter, ) override fun onDependencyChanged(dependency: ConstraintDependency): Flow { @@ -92,6 +95,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ).map { dependency } ConstraintDependency.PHONE_STATE -> phoneAdapter.callStateFlow.map { dependency } + ConstraintDependency.RINGER_MODE -> volumeAdapter.ringerModeFlow.map { dependency } ConstraintDependency.CHARGING_STATE -> powerAdapter.isCharging.map { dependency } ConstraintDependency.HINGE_STATE -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index e6561e8dc0..a10a98b4e1 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -297,6 +297,9 @@ In phone call Not in phone call Phone ringing + Ringer mode: Ring + Ringer mode: Vibrate + Ringer mode: Silent Charging Discharging diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt index be77315302..9039b9823a 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt @@ -11,6 +11,7 @@ import io.github.sds100.keymapper.system.foldable.HingeState import io.github.sds100.keymapper.system.foldable.isClosed import io.github.sds100.keymapper.system.foldable.isOpen import io.github.sds100.keymapper.system.phone.CallState +import io.github.sds100.keymapper.system.volume.RingerMode import java.time.LocalTime import timber.log.Timber @@ -26,6 +27,7 @@ class TestConstraintSnapshot( val chosenImeId: String? = null, val isKeyboardShowing: Boolean = false, val callState: CallState = CallState.NONE, + val ringerMode: RingerMode = RingerMode.NORMAL, val isCharging: Boolean = false, val isLocked: Boolean = false, val isBackFlashlightOn: Boolean = false, @@ -107,6 +109,9 @@ class TestConstraintSnapshot( is ConstraintData.InPhoneCall -> callState == CallState.IN_PHONE_CALL is ConstraintData.NotInPhoneCall -> callState == CallState.NONE is ConstraintData.PhoneRinging -> callState == CallState.RINGING + is ConstraintData.RingerModeNormal -> ringerMode == RingerMode.NORMAL + is ConstraintData.RingerModeVibrate -> ringerMode == RingerMode.VIBRATE + is ConstraintData.RingerModeSilent -> ringerMode == RingerMode.SILENT is ConstraintData.Charging -> isCharging is ConstraintData.Discharging -> !isCharging is ConstraintData.LockScreenShowing -> isLockscreenShowing diff --git a/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt b/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt index 3deebac611..d81bdddddf 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt @@ -88,6 +88,10 @@ data class ConstraintEntity( const val NOT_IN_PHONE_CALL = "not_in_phone_call" const val PHONE_RINGING = "phone_ringing" + const val RINGER_MODE_NORMAL = "ringer_mode_normal" + const val RINGER_MODE_VIBRATE = "ringer_mode_vibrate" + const val RINGER_MODE_SILENT = "ringer_mode_silent" + const val CHARGING = "charging" const val DISCHARGING = "discharging" diff --git a/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt index 8a7e3ea0eb..3e6017648e 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/volume/AndroidVolumeAdapter.kt @@ -1,10 +1,14 @@ package io.github.sds100.keymapper.system.volume import android.app.NotificationManager +import android.content.BroadcastReceiver import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.media.AudioManager import android.os.Build import android.provider.Settings +import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext import io.github.sds100.keymapper.common.utils.KMResult @@ -18,6 +22,9 @@ import io.github.sds100.keymapper.system.SystemError import io.github.sds100.keymapper.system.permissions.Permission import javax.inject.Inject import javax.inject.Singleton +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import timber.log.Timber @Singleton @@ -30,6 +37,30 @@ class AndroidVolumeAdapter @Inject constructor( private val audioManager: AudioManager by lazy { ctx.getSystemService()!! } private val notificationManager: NotificationManager by lazy { ctx.getSystemService()!! } + private val ringerModeReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + intent ?: return + if (intent.action == AudioManager.RINGER_MODE_CHANGED_ACTION) { + _ringerModeFlow.value = ringerMode + } + } + } + + private val _ringerModeFlow: MutableStateFlow by lazy { + MutableStateFlow(ringerMode) + } + override val ringerModeFlow: StateFlow by lazy { _ringerModeFlow.asStateFlow() } + + init { + val filter = IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION) + ContextCompat.registerReceiver( + ctx, + ringerModeReceiver, + filter, + ContextCompat.RECEIVER_NOT_EXPORTED, + ) + } + override val ringerMode: RingerMode get() { // Get the current ringer mode with the setting because the AudioManager diff --git a/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt index 7bae48cc79..c5c88f8961 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/volume/VolumeAdapter.kt @@ -1,9 +1,11 @@ package io.github.sds100.keymapper.system.volume import io.github.sds100.keymapper.common.utils.KMResult +import kotlinx.coroutines.flow.Flow interface VolumeAdapter { val ringerMode: RingerMode + val ringerModeFlow: Flow fun raiseVolume(stream: VolumeStream? = null, showVolumeUi: Boolean): KMResult<*> fun lowerVolume(stream: VolumeStream? = null, showVolumeUi: Boolean): KMResult<*>