Skip to content

Commit

Permalink
feat: Quick Settings tiles to instantly control VoLTE setting (#253)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyujin-cho authored Feb 16, 2024
1 parent e3a03f4 commit 4090fa6
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 3 deletions.
46 changes: 45 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<activity
android:name=".HomeActivity"
Expand All @@ -23,6 +23,50 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".SIM1VoLTEConfigToggleQSTileService"
android:exported="true"
android:label="@string/qs_toggle_tile_title_sim_1"
android:icon="@drawable/ic_launcher_foreground"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" android:value="true" />
</service>
<service
android:name=".SIM2VoLTEConfigToggleQSTileService"
android:exported="true"
android:label="@string/qs_toggle_tile_title_sim_2"
android:icon="@drawable/ic_launcher_foreground"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" android:value="true" />
</service>
<service
android:name=".SIM1IMSStatusQSTileService"
android:exported="true"
android:label="@string/qs_status_tile_title_sim_1"
android:icon="@drawable/ic_launcher_foreground"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" android:value="true" />
</service>
<service
android:name=".SIM2IMSStatusQSTileService"
android:exported="true"
android:label="@string/qs_status_tile_title_sim_2"
android:icon="@drawable/ic_launcher_foreground"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE" android:value="true" />
</service>

<provider
android:name="rikka.shizuku.ShizukuProvider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ fun PixelIMSApp() {
}
}
},
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = MaterialTheme.colorScheme.primary),
colors = TopAppBarDefaults.topAppBarColors(containerColor = MaterialTheme.colorScheme.primary),
)
},
bottomBar = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package dev.bluehouse.enablevolte

import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import org.lsposed.hiddenapibypass.HiddenApiBypass
import java.lang.IllegalStateException

class SIM1IMSStatusQSTileService : IMSStatusQSTileService(0)
class SIM2IMSStatusQSTileService : IMSStatusQSTileService(1)

open class IMSStatusQSTileService(private val simSlotIndex: Int) : TileService() {
private val TAG = "SIM${simSlotIndex}IMSStatusQSTileService"

init {
HiddenApiBypass.addHiddenApiExemptions("L")
HiddenApiBypass.addHiddenApiExemptions("I")
}

private val moder: SubscriptionModer? get() {
val carrierModer = CarrierModer(this.applicationContext)

try {
if (checkShizukuPermission(0) == ShizukuStatus.GRANTED && carrierModer.deviceSupportsIMS) {
val sub = carrierModer.getActiveSubscriptionInfoForSimSlotIndex(this.simSlotIndex)
?: return null
return SubscriptionModer(sub.subscriptionId)
}
} catch (_: IllegalStateException) {}
return null
}
private val imsActivated: 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.isIMSRegistered
} catch (_: IllegalStateException) {}
return null
}

override fun onTileAdded() {
super.onTileAdded()
if (this.imsActivated == null) {
qsTile.state = Tile.STATE_UNAVAILABLE
}
}

private fun refreshStatus() {
val imsActivated = this.imsActivated
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
},
)
qsTile.updateTile()
}

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

override fun onClick() {
super.onClick()
moder?.restartIMSRegistration()
this.refreshStatus()
}
}
8 changes: 8 additions & 0 deletions app/src/main/java/dev/bluehouse/enablevolte/Moder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<SubscriptionInfo>
get() {
val sub = this.loadCachedInterface { sub }
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/dev/bluehouse/enablevolte/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package dev.bluehouse.enablevolte

import android.service.quicksettings.Tile
import android.service.quicksettings.TileService
import android.telephony.CarrierConfigManager
import org.lsposed.hiddenapibypass.HiddenApiBypass
import java.lang.IllegalStateException

class SIM1VoLTEConfigToggleQSTileService : VoLTEConfigToggleQSTileService(0)
class SIM2VoLTEConfigToggleQSTileService : VoLTEConfigToggleQSTileService(1)

open class VoLTEConfigToggleQSTileService(private val simSlotIndex: Int) : TileService() {
private val TAG = "SIM${simSlotIndex}VoLTEConfigToggleQSTileService"

init {
HiddenApiBypass.addHiddenApiExemptions("L")
HiddenApiBypass.addHiddenApiExemptions("I")
}

private val moder: SubscriptionModer? get() {
val carrierModer = CarrierModer(this.applicationContext)

try {
if (checkShizukuPermission(0) == ShizukuStatus.GRANTED && carrierModer.deviceSupportsIMS) {
carrierModer.subscriptions
val sub = carrierModer.getActiveSubscriptionInfoForSimSlotIndex(this.simSlotIndex)
?: return null
return SubscriptionModer(sub.subscriptionId)
}
} catch (_: IllegalStateException) {}
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()
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()
}

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 (isLocked) {
unlockAndRun { toggleVoLTEStatus() }
} else {
toggleVoLTEStatus()
}
}
}
40 changes: 40 additions & 0 deletions app/src/main/java/dev/bluehouse/enablevolte/pages/Config.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package dev.bluehouse.enablevolte.pages

import android.app.StatusBarManager
import android.content.ComponentName
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
Expand Down Expand Up @@ -71,6 +75,8 @@ fun Config(navController: NavController, subId: Int) {
var reversedConfigurableItems by rememberSaveable { mutableStateOf<Map<String, String>>(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")
Expand Down Expand Up @@ -337,6 +343,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),
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
<string name="permissions_capabilities"><![CDATA[权限与兼容性]]></string>
<string name="registered">已注册</string>
<string name="unregistered">未注册</string>
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
<string name="newer_version_available">有新版本 %1$s 可用!</string>
<string name="feature_toggles">启用/禁用功能</string>
<string name="cosmetic_toggles">显示设置</string>
Expand Down Expand Up @@ -67,6 +69,15 @@
<string name="loaded">已加载 %1$s 共 %2$s</string>
<string name="edit_value">更改值</string>
<string name="search">搜索</string>
<string name="qstile">Quick Settings Tile</string>
<string name="qs_status_tile_title">IMS Status (SIM %1$s)</string>
<string name="qs_status_tile_title_sim_1">IMS Status (SIM 1)</string>
<string name="qs_status_tile_title_sim_2">IMS Status (SIM 2)</string>
<string name="qs_toggle_tile_title">VoLTE Config (SIM %1$s)</string>
<string name="qs_toggle_tile_title_sim_1">VoLTE Config (SIM 1)</string>
<string name="qs_toggle_tile_title_sim_2">VoLTE Config (SIM 2)</string>
<string name="add_status_tile">Add IMS status display tile</string>
<string name="add_toggle_tile">Add VoLTE config toggle tile</string>
<string name="sim_config">SIM 配置</string>
<string name="config_dump_viewer">配置转储查看器</string>
</resources>
11 changes: 11 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
<string name="permissions_capabilities"><![CDATA[Permissions & Capabilities]]></string>
<string name="registered">Registered</string>
<string name="unregistered">Unregistered</string>
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
<string name="newer_version_available">Newer version %1$s available!</string>
<string name="feature_toggles">Enable/Disable Feature</string>
<string name="cosmetic_toggles">Cosmetic Toggles</string>
Expand Down Expand Up @@ -66,6 +68,15 @@
<string name="loaded">Loaded %1$s of %2$s</string>
<string name="edit_value">Edit Value</string>
<string name="search">Search</string>
<string name="qstile">Quick Settings Tile</string>
<string name="qs_status_tile_title">IMS Status (SIM %1$s)</string>
<string name="qs_status_tile_title_sim_1">IMS Status (SIM 1)</string>
<string name="qs_status_tile_title_sim_2">IMS Status (SIM 2)</string>
<string name="qs_toggle_tile_title">VoLTE Config (SIM %1$s)</string>
<string name="qs_toggle_tile_title_sim_1">VoLTE Config (SIM 1)</string>
<string name="qs_toggle_tile_title_sim_2">VoLTE Config (SIM 2)</string>
<string name="add_status_tile">Add IMS status display tile</string>
<string name="add_toggle_tile">Add VoLTE config toggle tile</string>
<string name="sim_config">SIM Config</string>
<string name="config_dump_viewer">Config Dump Viewer</string>
</resources>

0 comments on commit 4090fa6

Please sign in to comment.