Skip to content
Merged
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
Expand Up @@ -4,8 +4,12 @@ import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Icon
import android.os.Build
import android.provider.Settings
import java.util.Locale
import android.telecom.PhoneAccount
import android.telecom.PhoneAccountHandle
import android.telecom.TelecomManager
Expand Down Expand Up @@ -60,27 +64,126 @@ object TelecomManagerExtension {
}

fun TelecomManager.openPhoneAccountSettings(activity: Activity) {
if (Build.MANUFACTURER.equals("Samsung", ignoreCase = true)|| Build.MANUFACTURER.equals("OnePlus", ignoreCase = true)) {
try {
val intent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS)
intent.component = ComponentName(
"com.android.server.telecom",
"com.android.server.telecom.settings.EnableAccountPreferenceActivity"
)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
activity.startActivity(intent, null)
} catch (e: Exception) {
Log.e("TelecomManager", "openPhoneAccountSettings: ${e.message}")

// use fallback method
val intent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS)
activity.startActivity(intent, null)
if (launchTelecomEnablePreference(activity)) {
return
}

val packageManager = activity.packageManager
val candidateIntents = buildList {
addAll(manufacturerSpecificIntents(activity))
add(Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
add(Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS))
}
add(Intent(Settings.ACTION_SETTINGS))
}

candidateIntents.forEach { baseIntent ->
val intent = Intent(baseIntent).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
if (component == null) {
resolveSystemComponent(packageManager, baseIntent)?.let { component = it }
}
}

if (canHandleIntent(packageManager, intent)) {
try {
activity.startActivity(intent)
return
} catch (error: Exception) {
Log.w("TelecomManager", "openPhoneAccountSettings: failed to launch ${intent.action}: ${error.message}")
}
}
}

Log.e("TelecomManager", "openPhoneAccountSettings: Unable to find compatible settings activity.")
}

private fun TelecomManager.launchTelecomEnablePreference(activity: Activity): Boolean {
val manufacturer = Build.MANUFACTURER?.lowercase(Locale.US).orEmpty()
val brand = Build.BRAND?.lowercase(Locale.US).orEmpty()
val targets = setOf("samsung", "oneplus", "realme", "oppo")

if (manufacturer !in targets && brand !in targets) {
return false
}

val baseIntent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS).apply {
component = ComponentName(
"com.android.server.telecom",
"com.android.server.telecom.settings.EnableAccountPreferenceActivity"
)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}

return try {
activity.startActivity(baseIntent)
true
} catch (error: Exception) {
Log.w(
"TelecomManager",
"launchTelecomEnablePreference: primary component failed: ${error.message}"
)

tryLegacyTelecomIntent(activity)
}
}

private fun tryLegacyTelecomIntent(activity: Activity): Boolean {
return try {
val fallbackIntent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
activity.startActivity(fallbackIntent)
true
} catch (fallbackError: Exception) {
Log.w(
"TelecomManager",
"tryLegacyTelecomIntent: fallback intent failed: ${fallbackError.message}"
)
false
}
}

private fun manufacturerSpecificIntents(activity: Activity): List<Intent> {
val manufacturer = Build.MANUFACTURER?.lowercase(Locale.US).orEmpty()
val brand = Build.BRAND?.lowercase(Locale.US).orEmpty()

if (manufacturer !in setOf("oppo", "realme") && brand !in setOf("oppo", "realme")) {
return emptyList()
}

val components = listOf(
ComponentName("com.android.settings", "com.android.settings.Settings\$DefaultAppSettingsActivity"),
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The escaped dollar sign \$ in the class name string should be a regular dollar sign $ for proper ComponentName resolution. The backslash escaping is unnecessary in Kotlin strings and may cause the ComponentName lookup to fail.

Suggested change
ComponentName("com.android.settings", "com.android.settings.Settings\$DefaultAppSettingsActivity"),
ComponentName("com.android.settings", "com.android.settings.Settings$DefaultAppSettingsActivity"),

Copilot uses AI. Check for mistakes.
ComponentName("com.coloros.phonemanager", "com.coloros.phonemanager.defaultapp.DefaultAppManagerActivity"),
ComponentName("com.coloros.phonemanager", "com.coloros.phonemanager.defaultapp.DefaultAppListActivity"),
ComponentName("com.coloros.phonemanager", "com.coloros.phonemanager.defaultapp.DefaultAppEntryActivity")
)

} else {
val intent = Intent(TelecomManager.ACTION_CHANGE_PHONE_ACCOUNTS)
activity.startActivity(intent, null)
val explicitIntents = components.map { componentName ->
Intent(Intent.ACTION_VIEW).apply { component = componentName }
}

val packagedDefaultAppsIntent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS).apply {
`package` = "com.android.settings"
}

return explicitIntents + packagedDefaultAppsIntent
Comment on lines +167 to +171
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

Setting an explicit package name on an Intent may cause it to fail on devices where the settings app has a different package name (e.g., OEM-customized ROMs). Consider adding a version check or fallback mechanism since Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS is only available on API 24+ (Android N) and this intent is used without a version guard here.

Suggested change
val packagedDefaultAppsIntent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS).apply {
`package` = "com.android.settings"
}
return explicitIntents + packagedDefaultAppsIntent
val packagedDefaultAppsIntent: Intent? =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// Try to resolve the intent with explicit package, fallback to generic if not resolvable
val intent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
val pm = activity.packageManager
intent.`package` = "com.android.settings"
val resolved = pm.resolveActivity(intent, 0)
if (resolved != null) {
intent
} else {
// Remove explicit package, try generic intent
Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
}
} else {
null
}
return if (packagedDefaultAppsIntent != null) {
explicitIntents + packagedDefaultAppsIntent
} else {
explicitIntents
}

Copilot uses AI. Check for mistakes.
}

private fun resolveSystemComponent(packageManager: PackageManager, intent: Intent): ComponentName? {
val matches = packageManager.queryIntentActivities(intent, 0)
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

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

The flags parameter should use PackageManager.MATCH_DEFAULT_ONLY or appropriate flags instead of 0 to ensure only activities that can handle the intent are returned. Using 0 may return activities that aren't actually launchable, leading to potential failures.

Suggested change
val matches = packageManager.queryIntentActivities(intent, 0)
val matches = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)

Copilot uses AI. Check for mistakes.
val preferred = matches.firstOrNull { resolveInfo ->
resolveInfo.activityInfo?.applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) != 0
} ?: matches.firstOrNull()

return preferred?.activityInfo?.let { activityInfo ->
ComponentName(activityInfo.packageName, activityInfo.name)
}
}

private fun canHandleIntent(packageManager: PackageManager, intent: Intent): Boolean {
return intent.resolveActivity(packageManager) != null
}


Expand Down