学习日记(二)——自定义来电界面,监听来电广播,悬浮窗

这两天在为项目下一版本的功能做准备,刚好有一个功能是自定义来电界面,目的是显示更多的自定义信息。作为新手的我,就开始上网找啊找,看啊看,刚开始的思路是:监听来电广播——弹出自定义的activity界面——点击自定义界面的接听按钮或者挂断按钮。然后问题就来了:1.不能屏蔽系统默认的来电界面。2.接听和挂断的权限在不同手机有不同的结果。

解决办法:1.实现来电界面可以用弹出activity的方式,全屏弹窗的方式和半屏弹窗的方式。而弹出activity和全屏弹窗的方式都需要面临自定义接听和挂断按钮的问题。如果是半屏弹窗的话就只需要显示自定义的内容,然后可是使用系统自带界面的接听和挂断按钮进行通话。

一.监听来电广播:

新建一个继承BroadcastReceiver的类PhoneStateReceiver

package com.example.telephone;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class PhoneStateReceiver extends BroadcastReceiver {
	
	private Context context;
	private TextView txNumber;
    private RelativeLayout mFloatLayout;//定义悬浮窗口布局  
    private WindowManager.LayoutParams wmParams;
    private WindowManager mWindowManager;//创建悬浮窗口设置布局参数的对象
    private boolean firstNew = false;//判断弹窗是否已经显示
    
	@Override
	public void onReceive(Context context, Intent intent) {
		this.context = context;
		
		if(intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)){
			//监听拨打电话
		}
		else{
			//监听接听电话
			TelephonyManager phoneManager = (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);
			phoneManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
			
		}
	}
	
	/**
	 * 监听手机来电状态
	 */
	PhoneStateListener listener=new PhoneStateListener(){
		

        @Override
        public void onCallStateChanged(int state, final String incomingNumber) {
                super.onCallStateChanged(state, incomingNumber);
                switch(state){
                case TelephonyManager.CALL_STATE_IDLE://电话挂断状态
                	
                     Intent i = new Intent();
                     i.setAction("com.likebamboo.phoneshow.ACTION_END_CALL");//发送电话处于挂断状态的广播
                     context.sendBroadcast(i);
                     
                	 popPhoneRemove();//关闭来电悬浮窗界面
                     break;
                case TelephonyManager.CALL_STATE_OFFHOOK://电话接听状态
                     break;
                case TelephonyManager.CALL_STATE_RINGING://电话铃响状态
                	
                	 telRinging(incomingNumber);//打开来电悬浮窗界面,传递来电号码
                	 break;
                default:
                	
                    break;
                }
        }

	};
	
	/**
	 * 打开来电悬浮窗界面
	 * @param number
	 */
	private void telRinging(final String number){
		
		if(!firstNew){
			//第一次启动悬浮窗,延迟两秒后显示界面,并且把firstNew设置为true
    		firstNew = true;
    	 new Handler().postDelayed(new Runnable() {
             @Override
             public void run() {
            	 popPhone(number);
             }
         }, 2000);
    	}
    	else{
    		//如果悬浮窗已经显示
    	}
	}
	
	
	/**
	 * 悬浮窗的具体实现
	 * @param phone
	 */
    private void popPhone(String phone) {
    	
    	wmParams = new WindowManager.LayoutParams();
    	wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;//定义WindowManager.LayoutParams类型,TYPE_SYSTEM_ERROR为系统内部错误提示,显示于所有内容之上
    	mWindowManager = (WindowManager)context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);//系统服务,注意这里必须加getApplicationContext(),否则无法把悬浮窗显示在最上层
    	wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; //不能抢占聚焦点  
    	wmParams.flags = wmParams.flags | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;  
    	wmParams.flags = wmParams.flags | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; //排版不受限制  
        wmParams.width = WindowManager.LayoutParams.MATCH_PARENT;
        wmParams.height = getWindowHeight()/2;//屏幕的一半高度
        wmParams.gravity = Gravity.TOP;//居上显示
        
        LayoutInflater inflater = LayoutInflater.from(context);
        mFloatLayout = (RelativeLayout) inflater.inflate(R.layout.activity_main, null);//获取浮动窗口视图所在布局  
        txNumber = (TextView)mFloatLayout.findViewById(R.id.txNumber);
        txNumber.setText(phone);
        
        mWindowManager.addView(mFloatLayout, wmParams); //创建View
    }
    
    /**
     * 移除悬浮窗
     */
    private void popPhoneRemove(){
    	
    	 if(mFloatLayout != null)  
         {  
             mWindowManager.removeView(mFloatLayout); 
             firstNew = false;
         } 
    	 mFloatLayout=null;//必须加入此语句,否则会windowManager会找不到view
    }
    
    /**
     * 获取屏幕高度
     * @return
     */
    private int getWindowHeight(){
    	 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//系统服务
    	 DisplayMetrics metric = new DisplayMetrics();
         wm.getDefaultDisplay().getMetrics(metric);
         return metric.heightPixels;   // 屏幕高度(像素)
    }
}
	

 二.接听电话和挂断电话:

先要添加ITelephony.aidl文件

/**
	 * 接听电话
	 * @return
	 */
	private void answerCall(){
		TelephonyManager telManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
		Class<TelephonyManager> c = TelephonyManager.class;
		try {
			Method getTelMethod = c.getDeclaredMethod("getITelephony", (Class[])null);
			getTelMethod.setAccessible(true);
            ITelephony iTelephony = null;
            iTelephony = (ITelephony)getTelMethod.invoke(telManager, (Object[])null);
            iTelephony.answerRingingCall();
		} catch (Exception e) {
            e.printStackTrace();
        }
	} 
	
	
	/**
     * 挂断电话
     */
    public void endCall() {
        TelephonyManager mTelMgr = (TelephonyManager)getSystemService(Service.TELEPHONY_SERVICE);
        Class<TelephonyManager> c = TelephonyManager.class;
        try {
            Method getITelephonyMethod = c.getDeclaredMethod("getITelephony", (Class[])null);
            getITelephonyMethod.setAccessible(true);
            ITelephony iTelephony = null;
            iTelephony = (ITelephony)getITelephonyMethod.invoke(mTelMgr, (Object[])null);
            iTelephony.endCall();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Fail to answer ring call.");
        }
    }

  /**
     * 电话挂断广播接收器,接收在PhoneStateReceiver发送过来的广播,只是为了挂断后进行关闭来电界面
     */
    public BroadcastReceiver mEndCallReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO Auto-generated method stub
            if (intent != null && intent.getAction().equals("com.likebamboo.phoneshow.ACTION_END_CALL")) {
                
                finish();
            }
        }
    };

 三.添加监听来电状态的广播:

有两种方法,第一种是在AndroidManifest.xml里注册,这里注册的话 如果程序退出了还能监听到来电。

<!-- 注册监听手机状态 -->
        <receiver android:name="com.likebamboo.phoneshow.PhoneStateReceiver" >
            <intent-filter android:priority="1000" >
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
        </receiver>

<!-- 挂断手机的权限 --> <uses-permission android:name="android.permission.CALL_PHONE" /> <!-- 接电话的权限 --> <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <!-- 读取手机状态的权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 系统级弹窗权限 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

 第二种是在activity里注册,这样的话如果程序退出能手动关闭监听。

public class MainActivity extends Activity {
	
	private PhoneStateReceiver phoneReceiver = new PhoneStateReceiver();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		//注册广播
		IntentFilter filter = new IntentFilter();
		filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
		filter.setPriority(Integer.MAX_VALUE);
		registerReceiver(phoneReceiver, filter);
	}

	/**
	 * 注销广播
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if(keyCode==KeyEvent.KEYCODE_BACK){  
            
			getApplication().onTerminate();
			android.os.Process.killProcess(android.os.Process.myPid());
			unregisterReceiver(phoneReceiver);//注销广播
            return true;  
        }  
		return super.onKeyDown(keyCode, event);
	}

}
原文地址:https://www.cnblogs.com/lostbird/p/3645509.html