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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.bluehouse.enablevolte

import android.os.Build
import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import org.lsposed.hiddenapibypass.HiddenApiBypass
Expand All @@ -12,6 +13,9 @@ class SIM2IMSStatusQSTileService : IMSStatusQSTileService(1)
open class IMSStatusQSTileService(
private val simSlotIndex: Int,
) : TileService() {
@Suppress("ktlint:standard:property-naming")
private val TAG = "SIM${simSlotIndex}IMSStatusQSTileService"

init {
HiddenApiBypass.addHiddenApiExemptions("L")
HiddenApiBypass.addHiddenApiExemptions("I")
Expand All @@ -31,10 +35,11 @@ open class IMSStatusQSTileService(
}
return null
}

private val imsActivated: Boolean? get() {
/*
* true: VoLTE enabled
* false: VoLTE disabled
* true: IMS registered
* false: IMS unregistered
* null: cannot determine status (Shizuku not running or permission not granted, SIM slot not active, ...)
*/
val moder = this.moder ?: return null
Expand All @@ -52,33 +57,35 @@ open class IMSStatusQSTileService(
}
}

private fun refreshStatus() {
val imsActivated = this.imsActivated
private fun refreshStatus(imsActivated: Boolean?) {
qsTile.state =
when (imsActivated) {
true -> Tile.STATE_ACTIVE
false -> Tile.STATE_INACTIVE
null -> Tile.STATE_UNAVAILABLE
}
qsTile.subtitle =
getString(
when (imsActivated) {
true -> R.string.registered
false -> R.string.unregistered
null -> R.string.unknown
},
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
qsTile.subtitle =
getString(
when (imsActivated) {
true -> R.string.registered
false -> R.string.unregistered
null -> R.string.unknown
},
)
}
qsTile.updateTile()
}

override fun onStartListening() {
super.onStartListening()
this.refreshStatus()
this.refreshStatus(this.imsActivated)
}

// Called when the user taps on your tile in an active or inactive state.
override fun onClick() {
super.onClick()
moder?.restartIMSRegistration()
this.refreshStatus()
this.refreshStatus(this.imsActivated)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ open class VoLTEConfigToggleQSTileService(

try {
if (checkShizukuPermission(0) == ShizukuStatus.GRANTED && carrierModer.deviceSupportsIMS) {
carrierModer.subscriptions
val sub =
carrierModer.getActiveSubscriptionInfoForSimSlotIndex(this.simSlotIndex)
?: return null
Expand Down Expand Up @@ -59,18 +58,17 @@ open class VoLTEConfigToggleQSTileService(
}
}

override fun onStartListening() {
super.onStartListening()
private fun refreshStatus(volteEnabled: Boolean?) {
qsTile.state =
when (this.volteEnabled) {
when (volteEnabled) {
true -> Tile.STATE_ACTIVE
false -> Tile.STATE_INACTIVE
null -> Tile.STATE_UNAVAILABLE
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
qsTile.subtitle =
getString(
when (this.volteEnabled) {
Comment on lines -62 to -73
Copy link
Copy Markdown
Contributor Author

@yhkee0404 yhkee0404 Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't here a race condition when the second read value of volteEnabled may get different from the first one and become a non-repeatable read? On the contrary, the similar updates from volteEnabled inside toggleVoLTEStatus or those from imsActivated inside IMSStatusQSTileService.refreshStatus have no bugs like this one.

when (volteEnabled) {
true -> R.string.enabled
false -> R.string.disabled
null -> R.string.unknown
Expand All @@ -80,16 +78,17 @@ open class VoLTEConfigToggleQSTileService(
qsTile.updateTile()
}

override fun onStartListening() {
super.onStartListening()
this.refreshStatus(this.volteEnabled)
}

private fun toggleVoLTEStatus() {
val moder = this.moder ?: return
val volteEnabled = this.volteEnabled ?: return
moder.updateCarrierConfig(CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL, !volteEnabled)
moder.restartIMSRegistration()
qsTile.state = if (volteEnabled) Tile.STATE_INACTIVE else Tile.STATE_ACTIVE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
qsTile.subtitle = getString(if (volteEnabled) R.string.disabled else R.string.enabled)
}
qsTile.updateTile()
this.refreshStatus(!volteEnabled)
}

// Called when the user taps on your tile in an active or inactive state.
Expand Down