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>(mapOf()) } var loading by rememberSaveable { mutableStateOf(true) } val scope = rememberCoroutineScope() + val simSlotIndex = moder.simSlotIndex + val statusBarManager: StatusBarManager = context.getSystemService(StatusBarManager::class.java) fun loadFlags() { Log.d(TAG, "loadFlags") @@ -337,6 +346,40 @@ fun Config(navController: NavController, subId: Int) { } } + if (VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + HeaderText(text = stringResource(R.string.qstile)) + ClickablePropertyView( + label = stringResource(R.string.add_status_tile), + value = "", + ) { + statusBarManager.requestAddTileService( + ComponentName( + context, + // TODO: what happens if someone tries to use this feature from a triple(or even dual)-SIM phone? + Class.forName("dev.bluehouse.enablevolte.SIM${simSlotIndex + 1}IMSStatusQSTileService"), + ), + context.getString(R.string.qs_status_tile_title, (simSlotIndex + 1).toString()), + Icon.createWithResource(context, R.drawable.ic_launcher_foreground), + {}, + {}, + ) + } + ClickablePropertyView( + label = stringResource(R.string.add_toggle_tile), + value = "", + ) { + statusBarManager.requestAddTileService( + ComponentName( + context, + Class.forName("dev.bluehouse.enablevolte.SIM${simSlotIndex + 1}VoLTEConfigToggleQSTileService"), + ), + context.getString(R.string.qs_toggle_tile_title, (simSlotIndex + 1).toString()), + Icon.createWithResource(context, R.drawable.ic_launcher_foreground), + {}, + {}, + ) + } + } HeaderText(text = stringResource(R.string.miscellaneous)) ClickablePropertyView( label = stringResource(R.string.reset_all_settings), diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 8cdeddd..fa0c55b 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -33,6 +33,8 @@ 已注册 未注册 + Enabled + Disabled 有新版本 %1$s 可用! 启用/禁用功能 显示设置 @@ -67,4 +69,13 @@ 已加载 %1$s 共 %2$s 更改值 搜索 + Quick Settings Tile + IMS Status (SIM %1$s) + IMS Status (SIM 1) + IMS Status (SIM 2) + VoLTE Config (SIM %1$s) + VoLTE Config (SIM 1) + VoLTE Config (SIM 2) + Add IMS status display tile + Add VoLTE config toggle tile \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bcedb1d..49a9505 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,6 +32,8 @@ Registered Unregistered + Enabled + Disabled Newer version %1$s available! Enable/Disable Feature Cosmetic Toggles @@ -66,4 +68,13 @@ Loaded %1$s of %2$s Edit Value Search + Quick Settings Tile + IMS Status (SIM %1$s) + IMS Status (SIM 1) + IMS Status (SIM 2) + VoLTE Config (SIM %1$s) + VoLTE Config (SIM 1) + VoLTE Config (SIM 2) + Add IMS status display tile + Add VoLTE config toggle tile