Dismissing a custom incoming call screen on Android when the call is answered or the phone stops ringing

耗尽温柔 提交于 2020-01-16 02:55:09

问题


I want to have a custom incoming call screen that shows up when there is an incoming call, and get dismissed if the call is answered or the phone stops ringing.

I've searched several posts on Stackoverflow to show me how can I implement that, and so far I am nearly there and after checking pros and cons of each approach in terms of how fast the screen gets shown, I've settled on the WindowManager approach.

Whenever I call wm.removeView(ly) I get the stack below

07-08 20:36:41.002  27547-27547/com.testtelephoney.customincomingcall E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.testtelephoney.customincomingcall, PID: 27547
java.lang.IllegalArgumentException: View=android.widget.LinearLayout{7e63aae V.E..... ......I. 0,0-0,0} not attached to window manager
        at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:402)
        at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:328)
        at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:84)
        at com.testtelephoney.customincomingcall.MyPhoneStateListener.onCallStateChanged(MyPhoneStateListener.java:43)
        at android.telephony.PhoneStateListener$2.handleMessage(PhoneStateListener.java:392)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:145)
        at android.app.ActivityThread.main(ActivityThread.java:5832)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)

Here is my code:

MyPhoneStateListener.java

import android.content.Context;
import android.graphics.PixelFormat;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;


public class MyPhoneStateListener extends PhoneStateListener{
    Context mContext;
    public MyPhoneStateListener(Context ct) {
        mContext = ct;
    }
    public void onCallStateChanged(int state,String incomingNumber){

        WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT|WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);

        params.height= WindowManager.LayoutParams.MATCH_PARENT;
        params.width= WindowManager.LayoutParams.MATCH_PARENT;
        params.format=PixelFormat.TRANSPARENT;
        params.gravity= Gravity.TOP;

        LinearLayout ly;

        final LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        ly=(LinearLayout)inflater.inflate(R.layout.activity_ic,null);


        switch (state){
            case TelephonyManager.CALL_STATE_IDLE:
                Log.d("DEBUG","IDLE");
                if(ly.getVisibility()== View.VISIBLE){
                    wm.removeView(ly);
                }
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                Log.d("DEBUG","OFFHOOK");
                break;
            case TelephonyManager.CALL_STATE_RINGING:
                Log.d("DEBUG","RINGING");
                wm.addView(ly,params);
                break;
        }
    }
}

ServiceReceiver

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class ServiceReceiver extends BroadcastReceiver {
    public ServiceReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        MyPhoneStateListener phoneListener = new MyPhoneStateListener(context);
        TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
    }
}

AndroidManifest

    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.READ_PROFILE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <receiver android:name=".ServiceReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
     </receiver>

回答1:


I've added a flag to mark that the view has been added. It does need to be improved off course.

 switch (state){
        case TelephonyManager.CALL_STATE_IDLE:
            Log.d("DEBUG","IDLE");
            if (viewCreated) {
                wm.removeView(ly);
                viewCreated = false;
            }
            break;
        case TelephonyManager.CALL_STATE_OFFHOOK:
            Log.d("DEBUG","OFFHOOK");
            break;
        case TelephonyManager.CALL_STATE_RINGING:
            Log.d("DEBUG","RINGING");
            if (!viewCreated){
                WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT|WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
                params.height= WindowManager.LayoutParams.MATCH_PARENT;
                params.width= WindowManager.LayoutParams.MATCH_PARENT;
                params.format=PixelFormat.TRANSPARENT;
                params.gravity= Gravity.TOP;

                ly=(LinearLayout)inflater.inflate(R.layout.activity_login,null);
                wm.addView(ly, params);
                viewCreated=true;
            }
            break;
    }



回答2:


ly is a local variable. You're creating a new one each time that onCallStateChanged is called. You need to store the layout you added to the window manager in a class level variable, so you can remove the instance of the layout you actually added, not a newly created instance.



来源:https://stackoverflow.com/questions/31302883/dismissing-a-custom-incoming-call-screen-on-android-when-the-call-is-answered-or

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!