Debug Dev
Debug Dev

Reputation: 1

How to display a custom full-screen view like WhatsApp call popup when the screen is locked?

I am trying to display a custom view/screen similar to WhatsApp’s incoming call popup, which appears whether the screen is locked or unlocked.

What I Tried:

Used Notification Channels, different flags, and services with a BroadcastReceiver. Implemented an overlay (SYSTEM_ALERT_WINDOW), which works fine when the screen is unlocked. It shows the notification and launches a full-screen popup.

However, when the screen is locked, I only receive the notification, but the full-screen popup does not appear.

I want to display a custom full-screen UI (like WhatsApp's call popup) even when the device screen is locked.

I also want simier functionality in iOS side, please provide me idea or code snippet for the same. Thank you.

Upvotes: 0

Views: 36

Answers (1)

Tejas Soni
Tejas Soni

Reputation: 593

You need to use a combination of a BroadcastReceiver, a Service, and a WindowManager with the appropriate flags. Here is a step-by-step guide:

  1. Create a BroadcastReceiver to listen for the incoming call event:
class CallReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val serviceIntent = Intent(context, CallService::class.java)
        context.startService(serviceIntent)
    }
}
  1. Create a Service to handle the display of the full-screen view:
class CallService : Service() {
    private lateinit var windowManager: WindowManager
    private lateinit var callView: View

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        callView = LayoutInflater.from(this).inflate(R.layout.call_layout, null)

        val params = WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
                    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
                    WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
            PixelFormat.TRANSLUCENT
        )

        windowManager.addView(callView, params)
    }

    override fun onDestroy() {
        super.onDestroy()
        if (::callView.isInitialized) {
            windowManager.removeView(callView)
        }
    }
}
  1. Declare the Service and BroadcastReceiver in your AndroidManifest.xml:
<service android:name=".CallService" android:exported="true" />
<receiver android:name=".CallReceiver">
    <intent-filter>
        <action android:name="com.example.INCOMING_CALL" />
    </intent-filter>
</receiver>
  1. Request the necessary permissions in your AndroidManifest.xml:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
  1. Trigger the BroadcastReceiver when an incoming call is detected:
val intent = Intent("com.example.INCOMING_CALL")
sendBroadcast(intent)

For iOS, you can use CallKit to achieve similar functionality. Here is a basic example:

  1. Create a new CXProvider and configure it:
import CallKit

class CallManager {
    private let provider: CXProvider

    init() {
        let providerConfiguration = CXProviderConfiguration(localizedName: "YourApp")
        providerConfiguration.supportsVideo = true
        providerConfiguration.maximumCallsPerCallGroup = 1
        providerConfiguration.supportedHandleTypes = [.phoneNumber]

        provider = CXProvider(configuration: providerConfiguration)
        provider.setDelegate(self, queue: nil)
    }

    func reportIncomingCall(uuid: UUID, handle: String) {
        let update = CXCallUpdate()
        update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
        update.hasVideo = true

        provider.reportNewIncomingCall(with: uuid, update: update) { error in
            if let error = error {
                print("Error reporting incoming call: \(error.localizedDescription)")
            }
        }
    }
}

extension CallManager: CXProviderDelegate {
    func providerDidReset(_ provider: CXProvider) {
        // Handle provider reset
    }

    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
        // Handle call answer
        action.fulfill()
    }

    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
        // Handle call end
        action.fulfill()
    }
}
  1. Use the CallManager to report an incoming call:
let callManager = CallManager()
let uuid = UUID()
callManager.reportIncomingCall(uuid: uuid, handle: "1234567890")

This setup will allow you to display a custom full-screen UI for incoming calls on both Android and iOS, even when the screen is locked.

Upvotes: 0

Related Questions