package com.dmc.callcrm.util import android.app.AlertDialog import android.content.ComponentName import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build import android.os.PowerManager import android.provider.Settings import android.widget.Toast import java.util.Locale /** * Several brands kill background apps and block their broadcasts unless the user allows * it: Xiaomi / Redmi / POCO (Autostart + Battery saver "No restrictions"), Vivo / iQOO * (Auto-start / High background power), Oppo / Realme / OnePlus (Auto-launch / Allow * background activity). These can't be granted from code — we can only open the right * screens. This helper does that, with fallbacks for different OS versions. On other * phones it only asks for "battery optimization: don't optimize" when that is missing. */ object BackgroundHelper { private const val PREFS = "bg_helper" private const val KEY_SHOWN = "miui_setup_shown_v1" private const val KEY_SHOWN_OTHER = "bg_setup_shown_v1" private fun brand(): String = (Build.MANUFACTURER.orEmpty() + " " + Build.BRAND.orEmpty()).lowercase(Locale.ROOT) fun isVivo(): Boolean = brand().let { "vivo" in it || "iqoo" in it } fun isOppoFamily(): Boolean = brand().let { "oppo" in it || "realme" in it || "oneplus" in it } fun isXiaomi(): Boolean { val m = Build.MANUFACTURER?.lowercase(Locale.ROOT).orEmpty() val b = Build.BRAND?.lowercase(Locale.ROOT).orEmpty() return m in setOf("xiaomi", "redmi", "poco") || b in setOf("xiaomi", "redmi", "poco") } fun isIgnoringBatteryOptimizations(ctx: Context): Boolean { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true val pm = ctx.getSystemService(Context.POWER_SERVICE) as PowerManager return pm.isIgnoringBatteryOptimizations(ctx.packageName) } /** * Show the setup dialog once (call from HomeActivity): always on Xiaomi / Vivo / * Oppo-family phones, and on any other phone only if battery optimization is still on. */ fun showOnceIfNeeded(ctx: Context) { val sp = ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE) val key = if (isXiaomi()) KEY_SHOWN else KEY_SHOWN_OTHER if (sp.getBoolean(key, false)) return val needed = isXiaomi() || isVivo() || isOppoFamily() || !isIgnoringBatteryOptimizations(ctx) if (!needed) return sp.edit().putBoolean(key, true).apply() showSetupDialog(ctx) } /** Dialog with buttons for each required setting. Also reachable from More tab. */ fun showSetupDialog(ctx: Context) { val batteryOk = isIgnoringBatteryOptimizations(ctx) val autostartName = when { isVivo() -> "AUTO-START (i Manager / Settings → Apps)" isOppoFamily() -> "AUTO-LAUNCH / Allow background activity" else -> "AUTOSTART" } val saverName = when { isVivo() -> "BATTERY → High background power usage → allow" isOppoFamily() -> "BATTERY → Allow background activity / Don't optimize" else -> "BATTERY SAVER → choose \"No restrictions\"" } val msg = buildString { append("To sync calls automatically (without opening the app), please do these on this phone:\n\n") append("1. $autostartName → turn ON for this app\n\n") append("2. $saverName\n\n") append("3. Battery optimization → ") append(if (batteryOk) "✓ already done\n\n" else "Allow / Don't optimize\n\n") append("4. Open Recent apps, long-press (or swipe down on) this app and lock it 🔒 so the phone doesn't close it.") } AlertDialog.Builder(ctx) .setTitle("Allow background sync") .setMessage(msg) .setPositiveButton("1. Autostart") { _, _ -> openAutostart(ctx) } .setNeutralButton("2. Battery saver") { _, _ -> openBatterySaver(ctx) } .setNegativeButton("3. Optimization") { _, _ -> openBatteryOptimization(ctx) } .show() } fun openAutostart(ctx: Context) { val intents = listOf( Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")), // Vivo / iQOO Intent().setComponent(ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")), Intent().setComponent(ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")), // Oppo / Realme / OnePlus (ColorOS) Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")), Intent().setComponent(ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")), Intent().setComponent(ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")), Intent("miui.intent.action.OP_AUTO_START").addCategory(Intent.CATEGORY_DEFAULT), Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity")) .putExtra("extra_pkgname", ctx.packageName) ) if (!tryStart(ctx, intents)) openAppDetails(ctx, "Open \"Autostart\" and turn it ON.") else toast(ctx, "Find this app in the list and turn Autostart ON.") } fun openBatterySaver(ctx: Context) { val label = try { ctx.packageManager.getApplicationLabel(ctx.applicationInfo).toString() } catch (_: Exception) { "" } val intents = listOf( Intent().setComponent(ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity")) .putExtra("package_name", ctx.packageName) .putExtra("package_label", label), Intent().setComponent(ComponentName("com.miui.securitycenter", "com.miui.powercenter.PowerSettings")) ) if (!tryStart(ctx, intents)) openAppDetails(ctx, "Open \"Battery saver\" and choose \"No restrictions\".") else toast(ctx, "Choose \"No restrictions\".") } fun openBatteryOptimization(ctx: Context) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return val intents = listOf( Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:${ctx.packageName}")), Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS) ) if (!tryStart(ctx, intents)) openAppDetails(ctx, "Set Battery to \"No restrictions\".") } private fun openAppDetails(ctx: Context, hint: String) { tryStart(ctx, listOf( Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:${ctx.packageName}")) )) toast(ctx, hint) } private fun tryStart(ctx: Context, intents: List): Boolean { for (i in intents) { try { if (ctx !is android.app.Activity) i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ctx.startActivity(i) return true } catch (_: Exception) { /* try next */ } } return false } private fun toast(ctx: Context, s: String) = Toast.makeText(ctx, s, Toast.LENGTH_LONG).show() }