android 通話狀態監聽(自定義接聽掛斷按鈕與通話界面,根據公司的業務邏輯能夠實現本身的來電秀功能)

前言:

由於公司需求,要自定義一款來電秀的app當作周邊產品來配合主營的app業務。 以前由於趕項目,沒時間整理這塊,如今項目告一段落了,如今回頭看看感受這個功能仍是挺有意思的,比較有針對性。電話呼入或者呼出的時候,結合公司的業務顯示出對應的界面還有挺nice的。然而網上關於這方面的文章比較少,仍是挺有必要整理一下。 來電秀主要功能是監聽通話(包括呼出和呼入電話)來實現本身想要的界面效果android

正文:

** 實現手機電話狀態的監聽,主要依靠兩個類:TelephoneManger和PhoneStateListener。 ** 今天主要用到的是PhoneStateListener來實現通話狀態監聽的功能,使用起來比較簡單,步驟也比較明瞭app

第一步:固然是要添加權限

<uses-permission Android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
複製代碼

第二步:自定義一個類,並繼承PhoneStateListener類。

class MyPhoneStateListener extends PhoneStateListener {

		@Override
		public void onCallStateChanged(int state, String incomingNumber) {
			super.onCallStateChanged(state, incomingNumber);
			
			}
		}
	}
複製代碼

他的state值有以下幾種ide

TelephonyManager.CALL_STATE_IDLE:  空閒狀態
    TelephonyManager.CALL_STATE_RINGING:  響鈴狀態
    TelephonyManager.CALL_STATE_OFFHOOK:  通話狀態
複製代碼

操蛋的是,state只有通話狀態(CALL_STATE_OFFHOOK),可是並無區分是呼入,仍是呼出的。並且空閒狀態(CALL_STATE_IDLE)也並無區分是掛斷空閒,仍是沒有通話的空閒。不過,仍是有解決方法滴,這個稍後在說。ui

第三步:定義一個service,並將MyPhoneStateListener這個類放到service中,分別在onCreat和onDestroy調用開啓監聽和關閉監聽的方法。

懶癌又犯了,直接上代碼了,代碼中的註釋比較清晰,若是要是有什麼不明白的,能夠給我留言。^^ /** * 獲取來電號碼服務 / public class CallService extends Service { /* * 電話服務管理器 / private TelephonyManager telephonyManager; /* * 電話狀態監聽器 */ private MyPhoneStateListener myPhoneStateListener;spa

@Override
    	public IBinder onBind(Intent intent) {
    		return null;
    	}
    
    	@Override
    	public void onCreate() {
    
    		super.onCreate();
    		// 獲取來電號碼
    		LogUtils.e("state","開啓服務");
    		getIncomingCall();
    	}
    
    	@Override
    	public void onDestroy() {
    		super.onDestroy();
    		// 不獲取來電號碼
    		LogUtils.e("state","關閉服務");
    		getIncomingCallCancel();
    	}
    
    	/**
    	 * 獲取來電號碼
    	 */
    	private void getIncomingCall() {
    		// 獲取電話系統服務
    		telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    		myPhoneStateListener = new MyPhoneStateListener();
    		telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    	}
    
    	/**
    	 * 不獲取來電號碼
    	 */
    	private void getIncomingCallCancel() {
    		telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_NONE);
    	}
    
    	/**
    	 * 電話狀態監聽器
    	 */
    	class MyPhoneStateListener extends PhoneStateListener {
    
    		@Override
    		public void onCallStateChanged(int state, String incomingNumber) {
    			super.onCallStateChanged(state, incomingNumber);
    
    			incomingNumberFinal=incomingNumber;
    			switch (state) {
    				case TelephonyManager.CALL_STATE_RINGING://響鈴
    					break;
    
    				case TelephonyManager.CALL_STATE_OFFHOOK://通話狀態
    					break;
    
    				case TelephonyManager.CALL_STATE_IDLE://空閒狀態
    					break;
    
    				default:
    					break;
    			}
    
    		}
    	}
    }
複製代碼
  • 接下來,咱們來區分通話狀態是呼入仍是呼出的,以及空閒狀態是掛斷空閒,仍是沒有通話的空閒code

    1.思考:要想區分呼入和呼出的狀態,就要考慮呼入和呼出的狀態的區別是什麼。 2.回答:呼入和呼出的狀態的區別就是一個是別人打進來的,一個是本身主動撥出去的(廢話),那麼呼入狀態是否是就要有響鈴呢,呼出狀態沒有響鈴的呢。 **3.豁然開朗:**那麼是否是區分出是否有響鈴就能區分呼入和呼出呢,區分是否有響鈴,只須要記錄上一次的狀態就能夠了。 **4.聯想:(不是廣告)**空閒狀態是掛斷空閒仍是沒有通話的空閒,也能夠判斷上一次狀態是否是空閒狀態來區分。繼承

    思考到此結束了,仍是直接奉上代碼,若是要是有什麼不明白的,能夠給我留言。事件

    /**
       * 獲取來電號碼服務
       */
      public class IncomingCallService extends Service {
      
          /**
           * 記錄上一個電話狀態
           */
      	private int lastCallState  = TelephonyManager.CALL_STATE_IDLE;
      
      	/**
      	 * 電話服務管理器
      	 */
      	private TelephonyManager telephonyManager;
      
      	/**
      	 * 電話狀態監聽器
      	 */
      	private MyPhoneStateListener myPhoneStateListener;
      
      	@Override
      	public IBinder onBind(Intent intent) {
      		return null;
      	}
      
      	@Override
      	public void onCreate() {
      
      		super.onCreate();
      		// 獲取來電號碼
      		LogUtils.e("state","開啓服務");
      		getIncomingCall();
      	}
      
      	@Override
      	public void onDestroy() {
      		super.onDestroy();
      		// 不獲取來電號碼
      		LogUtils.e("state","關閉服務");
      		getIncomingCallCancel();
      	}
      
      	/**
      	 * 獲取來電號碼
      	 */
      	private void getIncomingCall() {
      		// 獲取電話系統服務
      		telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
      		myPhoneStateListener = new MyPhoneStateListener();
      		telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
      	}
      
      	/**
      	 * 不獲取來電號碼
      	 */
      	private void getIncomingCallCancel() {
      		telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_NONE);
      	}
      
      	/**
      	 * 電話狀態監聽器
      	 */
      	class MyPhoneStateListener extends PhoneStateListener {
      
      		@Override
      		public void onCallStateChanged(int state, String incomingNumber) {
      			super.onCallStateChanged(state, incomingNumber);
      
      			incomingNumberFinal=incomingNumber;
      			switch (state) {
      				case TelephonyManager.CALL_STATE_RINGING://響鈴
      			        	//以前這裏寫的有誤感謝@Bug_liu的細心閱讀並指出問題
                                            lastCallState=TelephonyManager.CALL_STATE_RINGING
                                             //自定義來電界面
      					break;
      
      				case TelephonyManager.CALL_STATE_OFFHOOK://通話狀態
      
      					//呼入電話
      					if (lastCallState==TelephonyManager.CALL_STATE_RINGING){
      					   
      					    //自定義呼入界面
      					
      					}
      					//呼出電話
      					else{
      					   
      					    //自定義呼出界面
      				
      					}
      					lastCallState = TelephonyManager.CALL_STATE_OFFHOOK;
      
      					break;
      
      				case TelephonyManager.CALL_STATE_IDLE://無狀態
      				    //無通話
      					if (lastCallState==TelephonyManager.CALL_STATE_IDLE){
      						
      					}
      					//通話掛斷
      					else{
      						lastCallState = TelephonyManager.CALL_STATE_IDLE;
      						//自定義通話掛斷界面
      					}
      					break;
      
      				default:
      					break;
      			}
      
      		}
      	}
      }
    複製代碼

第四步:掛斷電話和接聽電話

由於android4.0以上版本把接聽電話事件設置爲系統權限,因此原來接聽電話的方法已經無論用了 系統掛斷電話的方法須要ITelephony這個類裏的方法,可是這個方法是系統私有的,不能直接調用,因此在本身的工程裏建立一個ITelephony.aidl文件(直接上網下載一個就好),放到com.android.internal.telephony這個包下(本身建立一個包)。ITelephony須要依賴NeighboringCellInfo這個類,一樣的方式建立一個NeighboringCellInfo.aidl文件(網上下載),放到android.telephony這個包下(本身建立一個包)。 準備工做作好了get

首先就是掛斷電話功能:input

/**
 * 拒絕接聽
 */
public static void rejectCall(Context context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        try {
            Method method = Class.forName("android.os.ServiceManager")
                    .getMethod("getService", String.class);
            IBinder binder = (IBinder) method.invoke(null, new Object[]{Context.TELEPHONY_SERVICE});
            ITelephony telephony = ITelephony.Stub.asInterface(binder);
            telephony.endCall();
        } catch (NoSuchMethodException e) {
            Log.d("TAG", "", e);
        } catch (ClassNotFoundException e) {
            Log.d("TAG", "", e);
        } catch (Exception e) {

        }
    }else{
        TelephonyManager mTelMgr = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
        Class<TelephonyManager> c = TelephonyManager.class;
        try {
            Method getITelephonyMethod = c.getDeclaredMethod("getITelephony", (Class[]) null);
            getITelephonyMethod.setAccessible(true);
            ITelephony iTelephony = null;
            System.out.println("End call.");
            iTelephony = (ITelephony) getITelephonyMethod.invoke(mTelMgr, (Object[]) null);
            iTelephony.endCall();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Fail to answer ring call.");
        }
    }
}
複製代碼

接下來就是接聽電話功能,由於android4.0以上版本把接聽電話事件設置爲系統權限,可是4.0以上版本有個方法,那就是能夠經過耳機來接聽電話的方法沒有設置爲系統權限(不知道算不算是一個bug)。因此能夠模擬耳機接聽電話的方式來接通電話,代碼以下 注:部分手機(例如小米)用此方法無效,由於手機廠商修改了耳機接聽電話的功能權限,好比小米手機,只能用小米特有的耳機接聽,若是誰有解決的方法,請在下面留言,萬萬萬分感謝^^

/**
 * 接聽電話
 */
public static void acceptCall(Context context) {
    try {
        Method method = Class.forName("android.os.ServiceManager")
                .getMethod("getService", String.class);
        IBinder binder = (IBinder) method.invoke(null, new Object[]{Context.TELEPHONY_SERVICE});
        ITelephony telephony = ITelephony.Stub.asInterface(binder);
        telephony.answerRingingCall();
    } catch (Exception e) {
        LogUtils.e("TAG", "for version 4.1 or larger");
        acceptCall_4_1(context);
    }
}

/**
 * 4.1版本以上接聽電話
 */
public static void acceptCall_4_1(Context context) {
    AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    ;
    //模擬無線耳機的按鍵來接聽電話
    // for HTC devices we need to broadcast a connected headset
    boolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER)
            && !audioManager.isWiredHeadsetOn();
    if (broadcastConnected) {
        broadcastHeadsetConnected(context, false);
    }
    try {
        try {
            Runtime.getRuntime().exec("input keyevent " +
                    Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));
        } catch (IOException e) {
            // Runtime.exec(String) had an I/O problem, try to fall back
            String enforcedPerm = "android.permission.CALL_PRIVILEGED";
            Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
                    Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,
                            KeyEvent.KEYCODE_HEADSETHOOK));
            Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(
                    Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,
                            KeyEvent.KEYCODE_HEADSETHOOK));
            context.sendOrderedBroadcast(btnDown, enforcedPerm);
            context.sendOrderedBroadcast(btnUp, enforcedPerm);
        }
    } finally {
        if (broadcastConnected) {
            broadcastHeadsetConnected(context, false);
        }
    }
}

private static void broadcastHeadsetConnected(Context context, boolean connected) {
    Intent i = new Intent(Intent.ACTION_HEADSET_PLUG);
    i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
    i.putExtra("state", connected ? 1 : 0);
    i.putExtra("name", "mysms");
    try {
        context.sendOrderedBroadcast(i, null);
    } catch (Exception e) {
    }
}
複製代碼

第五步:打開/關閉揚聲器功能

android系統揚聲器須要使用到音頻管理器(AudioManager),具體實現方式以下:

添加權限:<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
複製代碼

代碼以下(工做項目中沒有用到揚聲器,可是親測一下,是有效的)

/**
 * 打開揚聲器
 */
public static void openSpeaker(Context context) {
	try {
		AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
		audioManager.setMode(AudioManager.ROUTE_SPEAKER);
		currVolume = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
		if (!audioManager.isSpeakerphoneOn()) {
			audioManager.setMode(AudioManager.MODE_IN_CALL);
			audioManager.setSpeakerphoneOn(true);
			audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
					audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL),
					AudioManager.STREAM_VOICE_CALL);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

public static void toggleSpeaker(Context context) {
	AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
	am.setMode(AudioManager.MODE_IN_CALL);
	am.setSpeakerphoneOn(!am.isSpeakerphoneOn());
}

/**
 * 關閉揚聲器
 */
public static void closeSpeaker(Context context) {
	try {
		AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
		if (audioManager != null) {
			if (audioManager.isSpeakerphoneOn()) {
				audioManager.setSpeakerphoneOn(false);
				audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, currVolume,
						AudioManager.STREAM_VOICE_CALL);
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}
複製代碼

結束語

謝謝你們的閱讀,若是有不足之處請你們指出,斧正。

相關文章
相關標籤/搜索