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


    这两天在为项目下一版本的功能做准备,刚好有一个功能是自定义来电界面,目的是显示更多的自定义信息。作为新手的我,就开始上网找啊找,看啊看,刚开始的思路是:监听来电广播——弹出自定义的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);
    	}
    
    }
    
  • 相关阅读:
    Ackerman 函数奇怪的函数
    HDU2871 Memory Control 线段树区间合并
    HDU3667 Hotel 线段树 经典空间合并
    图论
    HDU3016 Man Down 线段树
    HDU1878 欧拉回路 判定是否存在欧拉回路
    从今开始
    如何打开注册表编辑器
    ASP.NET中TextBox控件设置ReadOnly="true"后台取不到值
    学习笔记(2011年5月到9月)
  • 原文地址:https://www.cnblogs.com/lostbird/p/3645509.html
Copyright © 2020-2023  润新知