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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum class ConstraintDependency {
DEVICE_LOCKED_STATE,
LOCK_SCREEN_SHOWING,
PHONE_STATE,
RINGER_MODE,
CHARGING_STATE,
HINGE_STATE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ enum class ConstraintId {
NOT_IN_PHONE_CALL,
PHONE_RINGING,

RINGER_MODE_NORMAL,
RINGER_MODE_VIBRATE,
RINGER_MODE_SILENT,

CHARGING,
DISCHARGING,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand All @@ -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<BluetoothDeviceInfo> by lazy {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -53,6 +55,7 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor(
phoneAdapter,
powerAdapter,
foldableAdapter,
volumeAdapter,
)

override fun onDependencyChanged(dependency: ConstraintDependency): Flow<ConstraintDependency> {
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions base/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@
<string name="constraint_in_phone_call">In phone call</string>
<string name="constraint_not_in_phone_call">Not in phone call</string>
<string name="constraint_phone_ringing">Phone ringing</string>
<string name="constraint_ringer_mode_normal">Ringer mode: Ring</string>
<string name="constraint_ringer_mode_vibrate">Ringer mode: Vibrate</string>
<string name="constraint_ringer_mode_silent">Ringer mode: Silent</string>

<string name="constraint_charging">Charging</string>
<string name="constraint_discharging">Discharging</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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<RingerMode> by lazy {
MutableStateFlow(ringerMode)
}
override val ringerModeFlow: StateFlow<RingerMode> 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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<RingerMode>

fun raiseVolume(stream: VolumeStream? = null, showVolumeUi: Boolean): KMResult<*>
fun lowerVolume(stream: VolumeStream? = null, showVolumeUi: Boolean): KMResult<*>
Expand Down
Loading