diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 63993e8..b6efb38 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,7 +11,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.EnableVoLTE"
- tools:targetApi="31"
+ tools:targetApi="33"
android:localeConfig="@xml/locales_config">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+ },
+ )
+ qsTile.updateTile()
+ }
+
+ // Called when your app can no longer update your tile.
+ override fun onStopListening() {
+ super.onStopListening()
+ }
+
+ // Called when the user removes your tile.
+ override fun onTileRemoved() {
+ super.onTileRemoved()
+ }
+}
diff --git a/app/src/main/java/dev/bluehouse/enablevolte/Moder.kt b/app/src/main/java/dev/bluehouse/enablevolte/Moder.kt
index 70a4f78..b07e846 100644
--- a/app/src/main/java/dev/bluehouse/enablevolte/Moder.kt
+++ b/app/src/main/java/dev/bluehouse/enablevolte/Moder.kt
@@ -77,6 +77,11 @@ open class Moder {
}
class CarrierModer(private val context: Context) : Moder() {
+ fun getActiveSubscriptionInfoForSimSlotIndex(index: Int): SubscriptionInfo? {
+ val sub = this.loadCachedInterface { sub }
+ return sub.getActiveSubscriptionInfoForSimSlotIndex(index, null, null)
+ }
+
val subscriptions: List
get() {
val sub = this.loadCachedInterface { sub }
@@ -260,6 +265,9 @@ class SubscriptionModer(val subscriptionId: Int) : Moder() {
return config.get(key)
}
+ val simSlotIndex: Int
+ get() = this.loadCachedInterface { sub }.getSlotIndex(subscriptionId)
+
val isVoLteConfigEnabled: Boolean
get() = this.getBooleanValue(CarrierConfigManager.KEY_CARRIER_VOLTE_AVAILABLE_BOOL)
diff --git a/app/src/main/java/dev/bluehouse/enablevolte/Utils.kt b/app/src/main/java/dev/bluehouse/enablevolte/Utils.kt
index cee7991..97ae7d1 100644
--- a/app/src/main/java/dev/bluehouse/enablevolte/Utils.kt
+++ b/app/src/main/java/dev/bluehouse/enablevolte/Utils.kt
@@ -33,7 +33,7 @@ fun checkShizukuPermission(code: Int): ShizukuStatus {
}
val SubscriptionInfo.uniqueName: String
- get() = "${this.subscriptionId} - ${this.displayName}"
+ get() = "${this.displayName} (SIM ${this.simSlotIndex+1})"
fun getLatestAppVersion(handler: (String) -> Unit) {
"https://api.github.com/repos/kyujin-cho/pixel-volte-patch/releases"
diff --git a/app/src/main/java/dev/bluehouse/enablevolte/VoLTEConfigToggleQSTileService.kt b/app/src/main/java/dev/bluehouse/enablevolte/VoLTEConfigToggleQSTileService.kt
new file mode 100644
index 0000000..2c6e21a
--- /dev/null
+++ b/app/src/main/java/dev/bluehouse/enablevolte/VoLTEConfigToggleQSTileService.kt
@@ -0,0 +1,92 @@
+package dev.bluehouse.enablevolte
+
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+import android.telephony.CarrierConfigManager
+import android.util.Log
+import java.lang.IllegalStateException
+
+class SIM1VoLTEConfigToggleQSTileService : VoLTEConfigToggleQSTileService(0)
+class SIM2VoLTEConfigToggleQSTileService : VoLTEConfigToggleQSTileService(1)
+
+open class VoLTEConfigToggleQSTileService(private val simSlotIndex: Int) : TileService() {
+ val TAG = "SIM${simSlotIndex}VoLTEConfigToggleQSTileService"
+
+ private val moder: SubscriptionModer? get() {
+ val carrierModer = CarrierModer(this.applicationContext)
+
+ if (checkShizukuPermission(0) == ShizukuStatus.GRANTED && carrierModer.deviceSupportsIMS) {
+ val subscriptions = carrierModer.subscriptions
+ val sub = carrierModer.getActiveSubscriptionInfoForSimSlotIndex(this.simSlotIndex) ?: return null
+ return SubscriptionModer(sub.subscriptionId)
+ }
+ return null
+ }
+ private val volteEnabled: Boolean? get() {
+ /*
+ * true: VoLTE enabled
+ * false: VoLTE disabled
+ * null: cannot determine status (Shizuku not running or permission not granted, SIM slot not active, ...)
+ */
+ val moder = this.moder ?: return null
+ try {
+ return moder.isVoLteConfigEnabled
+ } catch (_: IllegalStateException) {}
+ return null
+ }
+
+ override fun onTileAdded() {
+ super.onTileAdded()
+ if (this.volteEnabled == null) {
+ qsTile.state = Tile.STATE_UNAVAILABLE
+ }
+ }
+
+ override fun onStartListening() {
+ super.onStartListening()
+ Log.d(TAG, "onStartListening()")
+ qsTile.state = when (this.volteEnabled) {
+ true -> Tile.STATE_ACTIVE
+ false -> Tile.STATE_INACTIVE
+ null -> Tile.STATE_UNAVAILABLE
+ }
+ qsTile.subtitle = getString(
+ when (this.volteEnabled) {
+ true -> R.string.enabled
+ false -> R.string.disabled
+ null -> R.string.unknown
+ },
+ )
+ qsTile.updateTile()
+ }
+
+ // Called when your app can no longer update your tile.
+ override fun onStopListening() {
+ super.onStopListening()
+ }
+
+ 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
+ qsTile.subtitle = getString(if (volteEnabled) R.string.disabled else R.string.enabled)
+ qsTile.updateTile()
+ }
+
+ // Called when the user taps on your tile in an active or inactive state.
+ override fun onClick() {
+ super.onClick()
+ if (!isSecure) {
+ unlockAndRun { toggleVoLTEStatus() }
+ } else {
+ toggleVoLTEStatus()
+ }
+ }
+
+ // Called when the user removes your tile.
+ override fun onTileRemoved() {
+ super.onTileRemoved()
+ }
+}
diff --git a/app/src/main/java/dev/bluehouse/enablevolte/pages/Config.kt b/app/src/main/java/dev/bluehouse/enablevolte/pages/Config.kt
index ce3ae1e..c22386f 100644
--- a/app/src/main/java/dev/bluehouse/enablevolte/pages/Config.kt
+++ b/app/src/main/java/dev/bluehouse/enablevolte/pages/Config.kt
@@ -1,6 +1,11 @@
package dev.bluehouse.enablevolte.pages
+import android.app.StatusBarManager
+import android.content.ComponentName
+import android.content.Context
+import android.graphics.drawable.Icon
import android.os.Build
+import android.os.Build.VERSION
import android.telephony.CarrierConfigManager
import android.util.Log
import android.widget.Toast
@@ -24,6 +29,7 @@ import dev.bluehouse.enablevolte.BooleanPropertyView
import dev.bluehouse.enablevolte.CarrierModer
import dev.bluehouse.enablevolte.ClickablePropertyView
import dev.bluehouse.enablevolte.HeaderText
+import dev.bluehouse.enablevolte.IMSStatusQSTileService
import dev.bluehouse.enablevolte.InfiniteLoadingDialog
import dev.bluehouse.enablevolte.KeyValueEditView
import dev.bluehouse.enablevolte.R
@@ -31,6 +37,7 @@ import dev.bluehouse.enablevolte.ShizukuStatus
import dev.bluehouse.enablevolte.SubscriptionModer
import dev.bluehouse.enablevolte.UserAgentPropertyView
import dev.bluehouse.enablevolte.ValueType
+import dev.bluehouse.enablevolte.VoLTEConfigToggleQSTileService
import dev.bluehouse.enablevolte.checkShizukuPermission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -71,6 +78,8 @@ fun Config(navController: NavController, subId: Int) {
var reversedConfigurableItems by rememberSaveable { mutableStateOf