Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incoming call on lock screen for a VOIP app like Whatsapp/Viber/Line/Skype on Marshmallow

We are planning to release an update to our Android app that is based on the Twilio Voice SDK. Our customers want a more native like experience where they can directly see a screen to accept or reject call (just like Skype/Whatsapp/Viber/Line etc), instead of clicking on the notification and then the dialog box. Moreover this should also work on the lock screen as well.

As of now, I am successful in opening up an activity in my app and show the accept or reject buttons. It works both when app is in foreground or background. Here's the piece of code which is achieving this. I have modified the notify() method in VoiceFirebaseMessagingService.java to show an activity whenever onMessageRecived is called for incoming call notification.

private void notify(CallInvite callInvite, int notificationId) {
        String callSid = callInvite.getCallSid();

        if (callInvite.getState() == CallInvite.State.PENDING) {
            soundPoolManager.playRinging();

            System.out.println("Disabling keyguard and accquiring wake lock");



            Intent intent = new Intent(this, OnCallActivityNew.class);
            intent.setAction(OnCallActivityNew.ACTION_INCOMING_CALL);
            intent.putExtra(OnCallActivityNew.INCOMING_CALL_NOTIFICATION_ID, notificationId);
            intent.putExtra(OnCallActivityNew.INCOMING_CALL_INVITE, callInvite);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
            PendingIntent pendingIntent =
                    PendingIntent.getActivity(this, notificationId, intent, PendingIntent.FLAG_ONE_SHOT);
            /*
             * Pass the notification id and call sid to use as an identifier to cancel the
             * notification later
             */
            Bundle extras = new Bundle();
            extras.putInt(NOTIFICATION_ID_KEY, notificationId);
            extras.putString(CALL_SID_KEY, callSid);

            NotificationCompat.Builder notificationBuilder =
                    new NotificationCompat.Builder(this)
                            .setSmallIcon(R.drawable.ic_call_end_white_24px)
                            .setContentTitle(getString(R.string.app_name))
                            .setContentText(callInvite.getFrom() + " is calling.")
                            .setAutoCancel(true)
                            .setExtras(extras)
                            .setContentIntent(pendingIntent)
                            .setGroup("test_app_notification")
                            .setOngoing(true)
                            .setColor(Color.rgb(214, 10, 37));
            notificationManager.notify(notificationId, notificationBuilder.build());
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtras(extras);
            globalintent =  intent;

            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    startActivity(globalintent);
                }
            },2000);

        } else {
            SoundPoolManager.getInstance(this).stopRinging();
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
                /*
                 * If the incoming call was cancelled then remove the notification by matching
                 * it with the call sid from the list of notifications in the notification drawer.
                 */
                StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();
                for (StatusBarNotification statusBarNotification : activeNotifications) {
                    Notification notification = statusBarNotification.getNotification();
                    Bundle extras = notification.extras;
                    String notificationCallSid = extras.getString(CALL_SID_KEY);

                    if (callSid.equals(notificationCallSid)) {
                        notificationManager.cancel(extras.getInt(NOTIFICATION_ID_KEY));
                    } else {
                        sendCallInviteToActivity(callInvite, notificationId);
                    }
                }
            } else {
                /*
                 * Prior to Android M the notification manager did not provide a list of
                 * active notifications so we lazily clear all the notifications when
                 * receiving a cancelled call.
                 *
                 * In order to properly cancel a notification using
                 * NotificationManager.cancel(notificationId) we should store the call sid &
                 * notification id of any incoming calls using shared preferences or some other form
                 * of persistent storage.
                 */
                notificationManager.cancelAll();
            }
        }
    }

Moreover, in the onCreate() of the OnCallActivityNew.java I have mentioned the following code.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        System.out.println("on create of activity is called for oncallactivitynew");

        super.onCreate(savedInstanceState);
        KeyguardManager kgm = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
        boolean isKeyguardUp = kgm.inKeyguardRestrictedInputMode();
        KeyguardManager.KeyguardLock kgl = kgm.newKeyguardLock("OnCallActivityNew");

        if(isKeyguardUp){
            kgl.disableKeyguard();
            isKeyguardUp = false;
        }

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "My Tag");
        wl.acquire();
        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
                        WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
                        WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
                WindowManager.LayoutParams.FLAG_FULLSCREEN |
                        WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
                        WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
                        WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

        setContentView(R.layout.activity_on_call);


        coordinatorLayout = (CoordinatorLayout) fin
       ..... ///more code below to add listener to different buttons
}

The only problem now is that when the phone is locked, this activity opens and onDestroy() is called and I am unable to show the screen to accept and reject buttons.

The desired behaviour I want is to have a mechanism where one can take calls even on the lock screen just like the apps I have mentioned above.

I know this is a problem which is more related to how Android works, but any help in this regard from you guys will be really appreciated. I am sure people will benefit out from this discussion.

like image 229
prateek31 Avatar asked Sep 21 '17 16:09

prateek31


Video Answer


1 Answers

I was able to make video call work with the steps in this answer.

As to your code to open the screen, please try mine and let me know if it works for you. If it doesn't you'll know the issue is elsewhere since this code works on the android devices I tested on:

// These flags ensure that the activity can be launched when the screen is locked.
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

// to wake up screen
PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = pm.newWakeLock((PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP), "TAG");
wakeLock.acquire();

// to release screen lock
KeyguardManager keyguardManager = (KeyguardManager) getApplicationContext().getSystemService(Context.KEYGUARD_SERVICE);
KeyguardManager.KeyguardLock keyguardLock = keyguardManager.newKeyguardLock("TAG");
keyguardLock.disableKeyguard();
like image 169
ousanmaz Avatar answered Sep 21 '22 13:09

ousanmaz