package com.dmc.callcrm.service import android.Manifest import android.annotation.SuppressLint import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings import android.telecom.PhoneAccountHandle import android.telecom.TelecomManager import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import com.dmc.callcrm.R import com.dmc.callcrm.data.Prefs import com.dmc.callcrm.data.SimUtil /** * Places a Quick Call (number clicked in the Chrome extension) from the background service. * * WHY: the old code did startActivity(ACTION_CALL) from the foreground service. Since * Android 10, apps may not open activities from the background, and Android drops such a * startActivity SILENTLY (no exception) — so the click did nothing and the fallback never * ran. It only worked on phones where "Display over other apps" happened to be allowed. * * Now, in order: * 1. TelecomManager.placeCall() — asks Android's telephony service to dial. Not an * activity start, so background limits don't apply. Needs CALL_PHONE (already asked * for). Uses the SIM registered in the app on dual-SIM phones. * 2. If "Display over other apps" is allowed: open the call screen directly. * 3. Otherwise: a high-priority "Tap to call …" notification (one tap opens the dialer). */ object QuickCaller { private const val TAG = "QuickCaller" private const val CHANNEL = "dmc_quick_call" @SuppressLint("MissingPermission") // CALL_PHONE is checked just below fun call(ctx: Context, rawNumber: String) { val number = rawNumber.trim() if (number.isEmpty()) return val uri = Uri.fromParts("tel", number, null) // 1) Telecom service (works from the background) if (hasPerm(ctx, Manifest.permission.CALL_PHONE)) { try { val tm = ctx.getSystemService(TelecomManager::class.java) val extras = Bundle() companySimHandle(ctx)?.let { extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, it) } tm.placeCall(uri, extras) Log.d(TAG, "placeCall($number) sent to telecom") return } catch (e: Exception) { Log.w(TAG, "placeCall failed: ${e.message}") } } // 2) Allowed to draw over other apps -> background activity start is permitted if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(ctx)) { try { val action = if (hasPerm(ctx, Manifest.permission.CALL_PHONE)) Intent.ACTION_CALL else Intent.ACTION_DIAL ctx.startActivity(Intent(action, uri).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) return } catch (e: Exception) { Log.w(TAG, "startActivity failed: ${e.message}") } } // 3) Last resort: a notification the employee taps to dial notifyTapToCall(ctx, number, uri) } /** PhoneAccountHandle of the SIM chosen at registration (dual-SIM), or null = phone's default. */ @SuppressLint("MissingPermission") // READ_PHONE_STATE checked on the first line private fun companySimHandle(ctx: Context): PhoneAccountHandle? { if (!hasPerm(ctx, Manifest.permission.READ_PHONE_STATE)) return null return try { val id = SimUtil.resolvePhoneAccountId(ctx, Prefs(ctx).simSlot) ?: return null val tm = ctx.getSystemService(TelecomManager::class.java) tm.callCapablePhoneAccounts.firstOrNull { it.id == id } } catch (_: Exception) { null } } @SuppressLint("MissingPermission") // POST_NOTIFICATIONS: SecurityException is caught private fun notifyTapToCall(ctx: Context, number: String, uri: Uri) { val nm = ctx.getSystemService(NotificationManager::class.java) ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { nm.createNotificationChannel( NotificationChannel(CHANNEL, "Quick Call from browser", NotificationManager.IMPORTANCE_HIGH) ) } val pi = PendingIntent.getActivity( ctx, number.hashCode(), Intent(Intent.ACTION_DIAL, uri).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0) ) val n = NotificationCompat.Builder(ctx, CHANNEL) .setSmallIcon(R.drawable.ic_call) .setContentTitle("Tap to call $number") .setContentText("Quick Call from your browser") .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_CALL) .setAutoCancel(true) .setContentIntent(pi) .build() try { nm.notify(number.hashCode(), n) } catch (e: SecurityException) { Log.w(TAG, "no notification permission") } } private fun hasPerm(ctx: Context, p: String) = ContextCompat.checkSelfPermission(ctx, p) == PackageManager.PERMISSION_GRANTED }