Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can anybody help me in making the incoming call whatsapp like? Similar to Callkit integration of Twilio with iOS #95

Closed
khurana3192 opened this issue Sep 20, 2017 · 64 comments
Assignees

Comments

@khurana3192
Copy link

No description provided.

@khurana3192
Copy link
Author

I am able to open an activity to show the hangup and accept buttons whenever there is an incoming call. But I am still wondering, how I can do this when my keyguard is locked. Any ideas guys?

@khurana3192
Copy link
Author

Ok, so I found the reason, basically when my screen is locked, and if try opening the activity with the incoming fcm notification, onDestroy gets called and destroys the activity. Any suggestions how can I overcome this problem?

@kbagchiGWC
Copy link
Contributor

@khurana3192 does onNewIntent() get called at all? Can you share how you have setup the PendingIntent that gets passed with the Notification?

@khurana3192
Copy link
Author

@kbagchiGWC Thanks for getting back to me. 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.

@kbagchiGWC
Copy link
Contributor

kbagchiGWC commented Sep 22, 2017

@khurana3192
Copy link
Author

@kbagchiGWC many questions on Stackoverflow points to ConnectionService to show native like incoming calls. First of all, the documentation in this regard is not that great. Secondly, this method is not always guaranteed to work on different devices (got to know from Stackoverflow under different posts). Moreover, I think companies like Whatsapp, Skype, Line, Viber are not following the ConnectionService route.

Is Twilio planning to include the support for this, just like the above said companies? Can you point me in the right direction or a working example apart from that Android developers link?

@idelgado
Copy link
Contributor

We don't have a sample for this at the moment but as you've pointed out there are other apps that do this so the support is available on the Android platform. We will consider adding a sample for this in the future but I don't have a timeline for when.

It sounds like your close to a solution. My impression is that the attributes associated with your Activity may be causing the issue. I would review settings associated with https://developer.android.com/guide/topics/manifest/activity-element.html#lmode or https://developer.android.com/reference/android/R.attr.html#configChanges are resulting in onDestroy() being called.

@khurana3192
Copy link
Author

@idelgado Thanks for considering this request, I am sure if you guys can do the same for your Android SDK, it will really help devs like us to cut down time required to ship a working VOIP app with Twilio. I would love to contribute in any such development if I get success with this problem of mine.

Even if I ensure that onDestroy() is not called by handling config changes or openinig the activity with a specific mode, bypassing keyguard and showing the in-call ui is not that easy. I think as @kbagchiGWC I need to dig deeper into how ConnectionService works in Android and show an incoming call ui accordingly.

Again, I am limited by time and was wondering if I can get a working solution to implement the same.

@abhinay-w3bminds
Copy link

@khurana3192 -- try this
orverride this method in your call activity class

@OverRide //to turn on screen while its lock when call is arriving
public void onAttachedToWindow() {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
}

get required permissions in manifest

@khurana3192
Copy link
Author

Hey @abhinay-w3bminds

I have added that method and also permissions in manifest. The screen does light up but somehow the activity gets destroyed and doesn't appear on the lock screen

@abhinay-w3bminds
Copy link

@khurana3192 If you are getting call while screen is awake then check if you are registering broadcast receiver in java class then do it in manifest and then try..

@khurana3192
Copy link
Author

@abhinay-w3bminds are you referring to the registration listener function of Twilio SDK?

@khurana3192
Copy link
Author

@abhinay-w3bminds is there a way I can share my complete code with you after leaving comments so that you can help me better?

@abhinay-w3bminds
Copy link

@khurana3192 share it on github

@rochapablo
Copy link

@khurana3192, have you found a solution for this or for ConnectionService?

@khurana3192
Copy link
Author

I am still looking for a pre-baked solution to cut down my research time :(

@chakri1vr1
Copy link

I am also facing the exact same issue, the screen lights up and then the activity gets destroyed. I have already tried all the answers present on SO like setting windows flags, manifest permissions, acquiring wake locks. Nothing seem to work surprisingly. @khurana3192 Any help/ progress from the situation? @abhinay-w3bminds Any other pointers?

@kbagchiGWC
Copy link
Contributor

kbagchiGWC commented Feb 9, 2018

@khurana3192 , @chakri1vr1, @rochapablo

We have updated voice-quickstart-android
to demonstrate how to enable users to directly see a screen to accept or reject call , instead of clicking on the notification and then the dialog box. This works when the screen is locked as well.

It took us sometime to get to it, apologies for delay. Hope this helps. Please let us know if you have any questions.

@idelgado
Copy link
Contributor

idelgado commented Feb 9, 2018

The PR with the changes can be found here: #143

@khurana3192
Copy link
Author

This is awesome.
Thanks for the update @kbagchiGWC @idelgado, will try to implement these changes in my current app. I was looking for someone from Twilio team to review our code, as many of our existing users are facing problems around our app. Can this be a possibility?

@kbagchiGWC
Copy link
Contributor

@khurana3192 Is your code in github? What kind of issues are you having? If you provide details, we will try to help you as much as possible.

@khurana3192
Copy link
Author

khurana3192 commented Feb 16, 2018

@kbagchiGWC
The problem that I am facing at the moment - once the activity opens up it gets called again somehow and canels the flow of call.
My app compromises of many other features apart from calling. One of the activity that handles an ongoing call is called when I receive an incoming call Invite, but as soon as the call is accepted or answer method is called, the activity is triggered again causing the current operation to be affected.
Since my app is being used by more than 3k customers, can I share this with you privately via a gist or something?

@kbagchiGWC
Copy link
Contributor

kbagchiGWC commented Feb 16, 2018

@khurana3192 You can send the gist link to my email.

@kbagchiGWC
Copy link
Contributor

@khurana3192

I am glad that your app is working as expected. Please close the ticket if you don't have further questions. Feel free to open a new issue for any future needs.

@khurana3192
Copy link
Author

@kbagchiGWC Thanks for all your help :) I will be glad to answer here, if anyone is facing this issue or trying to achieve the same thing

@basitsattar
Copy link

Hi @khurana3192 , How did you achieved this functionality? did you end up using connectionService?

@banshee
Copy link

banshee commented Jan 15, 2019

Secondly, this method is not always guaranteed to work on different devices (got to know from Stackoverflow under different posts).

Speaking as someone who's worked on inbound call notifications working across multiple handset manufacturers - trying to roll your own is much, much worse than just using ConnectionService. Be prepared to do extensive testing across Samsung devices, in particular. And pay attention to how they behave when they're locked and your app hasn't been launched and you get an inbound FCM message. Then add bluetooth headsets to your test matrix. And don't forget to test when the devices are showing the Samsung low-power always-on lockscreen. Don't forget about the new battery management stuff. (And then finally give up in disgust and just implement a ConnectionService, IMHO.)

@dougritter
Copy link

Hi, @banshee
Your comment is very useful.
Do you know if there's any alternative to ConnectionService for API <23?

@banshee
Copy link

banshee commented Jan 22, 2019

Not that I know of. We just said 23 is our new minimum version.

@banshee
Copy link

banshee commented Jan 22, 2019

Add one more thing to my list - do extensive testing with both native cell calls and your own calls simultaneously. Ordering matters, so test your call + incoming cell call, and vice versa. Test on multiple Samsung devices. Note that Samsung and Pixels will never, under any circumstances, do the same thing. If you don't hate samsung with a burning passion by the time you're done, you're not testing on enough samsung devices. Oh, and if you care, make sure you test on samsung devices that aren't sold in your country. The ones you have a hard time getting are going to have different problems than the ones you already have.

@gopipeddisetti
Copy link

@basitsattar
We have demonstrated how to achieve this in voice-quickstart-android code. The specific changes you need can be found here.

Give that a try and let me know if you have any questions.

is it working when the app forcefully killed from multitasking.?

@ffdez
Copy link

ffdez commented Jun 18, 2019

@basitsattar
We have demonstrated how to achieve this in voice-quickstart-android code. The specific changes you need can be found here.

Give that a try and let me know if you have any questions.

Actually, VoiceActivity.java file does not exist

I have tried to perform searches in the project with:

  • INCOMING_CALL_NOTIFICATION_ID
  • handleIncomingCallIntent
  • getWindow()

There is no trace of lines of code that appear in the PR.
What solution exists in the current code?

@shanky-gupta
Copy link

shanky-gupta commented Aug 19, 2019

@banshee I am not getting understand which way is the best to implement calling app like through FCM or ConnectionService. If ConnectionService is good to integrate calling app then Could you please demonstrate the connectionservice example.

@shanky-gupta
Copy link

Hi @khurana3192 , How did you achieved this functionality? did you end up using connectionService?

Did you implement this by connectionService?

@Ranaamin0322
Copy link

I have added this line of code in my app
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
for receive call when mobile is locked but when i tried to call is being cancelled automatically.

@Ranaamin0322
Copy link

@khurana3192 can you please explain that how you solved this on your side.

@CaptainJeff
Copy link

@Ranaamin0322 did you ever find a solution to this? I have the same issue

@lakshaydulani
Copy link

@khurana3192 pls help if u were able to solve this
@banshee as much as i agree with u, but i have used two apps with voip calling - Whatsapp and MS Teams.. both of them doesnt seem to be using ConnectionService

@mhtan88
Copy link

mhtan88 commented Dec 14, 2020

i'm having the same problem too. can't wake the app when caller call using twilio.

@KodyKendall
Copy link

Has anyone actually implemented this using ConnectionService?

I'm in the process of trying to implement ConnectionService into twilio-quickstart-android, but I don't want to duplicate work if anyone else has already successfully done this. I'm also open to collaborating with others if they want to help implement ConnectionService with me.

@lakshaydulani
Copy link

Has anyone actually implemented this using ConnectionService?

I'm in the process of trying to implement ConnectionService into twilio-quickstart-android, but I don't want to duplicate work if anyone else has already successfully done this. I'm also open to collaborating with others if they want to help implement ConnectionService with me.

hi Kody

I have implemented a basic version of ConnectionService. Ready to collab for implementing with twilio.

@KodyKendall
Copy link

Ok that works! I can add you as a contributor to my project, do you want to do the same for me?

@lakshaydulani
Copy link

Ok that works! I can add you as a contributor to my project, do you want to do the same for me?

sorry for replying late..yes sure u can add me or i can tell u how i m implemented it, in a call if it works for u

@lakshaydulani
Copy link

Ok that works! I can add you as a contributor to my project, do you want to do the same for me?

again sorry for replying late

here is the repo for bare basic code in Kotlin which implements connection service
https://github.com/lakshaydulani/android-connectionservice-kotlin-basic

@softsan
Copy link

softsan commented Mar 25, 2021

@lakshaydulani @KodyKendall Do you guys have any progress on Handling incoming call and showing native UI for incoming phone?

@tronku
Copy link

tronku commented Sep 28, 2021

@kbagchiGWC is there any way to listen to incoming call (non VoIP) without READ_PHONE_STATE permission? Uber and WhatsApp are able to listen to it. I tried to implement ConnectionService but even that is not working without READ_PHONE_STATE.

@4brunu
Copy link

4brunu commented Sep 28, 2021

I think this link can help you with this question.
https://stackoverflow.com/a/33434529/976628
You can use the PhoneStateListener with the permission <uses-permission android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="22"/>.

@tronku
Copy link

tronku commented Sep 28, 2021

but Uber and Whatsapp do that without asking for runtime permission

@4brunu
Copy link

4brunu commented Sep 28, 2021

If you use the PhoneStateListener you only need that permission from API level 22 and lower, so you don't need to ask the runtime permission on the API level 23.

@tronku
Copy link

tronku commented Sep 28, 2021

Nope, we still need READ_PHONE_STATE otherwise we won't be able to listen to the receiver - https://stackoverflow.com/a/63159603/8483843

@4brunu
Copy link

4brunu commented Sep 28, 2021

That's because in that stack overflow answer, they are trying to use a BroadcastReceiver instead of using the PhoneStateListener.

@tronku
Copy link

tronku commented Sep 29, 2021

@4brunu this listener needs to be attached with something right? how can this work?

private val phoneStateListener = object: PhoneStateListener() {

    override fun onCallStateChanged(state: Int, phoneNumber: String?) {
        when (state) {
            TelephonyManager.CALL_STATE_OFFHOOK -> {
                  log("CALL_STATE", "UNAVAILABLE")
            }
        }
        super.onCallStateChanged(state, phoneNumber)
    }
}

@4brunu
Copy link

4brunu commented Sep 29, 2021

Yes, to a context.

// attach
val telephonyManager = getApplication<Application>().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE)

// dettach
val telephonyManager = getApplication<Application>().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)

@4brunu
Copy link

4brunu commented Oct 7, 2021

Unfortunately, looks like this no longer works on Android S, without the permission READ_PHONE_STATE. 🙁

@cybex-dev
Copy link

A bit late to the party - check out this impl for the twilio_voice Flutter package: cybex-dev/twilio_voice#151

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests