Android 實(shí)現(xiàn)手機(jī)接通電話(huà)后振動(dòng)提示的功能
有些手機(jī)在電話(huà)接通后會(huì)有振動(dòng)提示,這有個(gè)好處就是可以等到接通后再放到耳邊接聽(tīng),減少輻射。本文就講講如何在Android手機(jī)中實(shí)現(xiàn)這種接通電話(huà)后的振動(dòng)提示功能,這里主要針對(duì)撥出的電話(huà)。
Android SDK提供的通話(huà)狀態(tài)
很明顯,要在電話(huà)接通的時(shí)候產(chǎn)生振動(dòng)提示,首先需要知道電話(huà)在何時(shí)被接通。而Android SDK并沒(méi)有給出直接讀取這種狀態(tài)的方法。下面是Android SDK的電話(huà)服務(wù)類(lèi)TelephonyManager提供的三種電話(huà)狀態(tài):
CALL_STATE_IDLE 空閑狀態(tài)
CALL_STATE_OFFHOOK 摘機(jī)狀態(tài)
CALL_STATE_RINGING 響鈴狀態(tài)
這幾個(gè)狀態(tài)很容易理解:摘機(jī)狀態(tài)即拿起話(huà)筒(對(duì)于座機(jī)電話(huà)而言的動(dòng)作),但這個(gè)狀態(tài)可能發(fā)生在撥入電話(huà)接通時(shí),也可能是撥出電話(huà)時(shí),但是卻不能說(shuō)明撥出電話(huà)接通時(shí)。通過(guò)以上3種狀態(tài)我們僅能組合出掛機(jī)和來(lái)電接通這兩個(gè)狀態(tài)。而今天我們要實(shí)現(xiàn)的功能卻無(wú)法做到。
看來(lái)我們需要尋找其他方法來(lái)實(shí)現(xiàn)了,SDK靠不住啊……
Android運(yùn)行l(wèi)og分析
還好Android在運(yùn)行時(shí)會(huì)有大量的log產(chǎn)生,看看我們能不能從這上面找到突波口呢?我們選擇Android的Radio模塊的日志來(lái)分析。首先我們需要寫(xiě)一段代碼來(lái)讀取Radio相關(guān)的log,讀取log就不得不用到logcat了。
Process process; InputStream inputstream; BufferedReader bufferedreader; try { process = Runtime.getRuntime().exec("logcat -v time -b radio"); inputstream = process.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader( inputstream); bufferedreader = new BufferedReader(inputstreamreader); String str = ""; while ((str = bufferedreader.readLine()) != null) { log.i("mLogcat",str); } } catch (Exception e) { }
另外,要讓程序能夠讀取系統(tǒng)log需要指定權(quán)限,在AndroidManifest.xml文件中加入一下內(nèi)容。
XML/HTML代碼
<uses-permission android:name="android.permission.READ_LOGS"></uses-permission>
通過(guò)上面這段代碼我們就可以將Radio的log輸出到了,這樣我們就可以通過(guò)在DDMS中查看這些log,分析其中的通話(huà)過(guò)程。具體抓到的log就不貼出來(lái)了,大家可以自己編寫(xiě)程序通過(guò)上面的代碼來(lái)抓取和分析。我只說(shuō)一下我的分析結(jié)果。
通過(guò)分析log發(fā)現(xiàn)了一些蛛絲馬跡。其中有幾條日志很有用:
GET_CURRENT_CALLS id=1,DIALING
GET_CURRENT_CALLS id=1,ALERTING
GET_CURRENT_CALLS id=1,ACTIVE
由于log較長(zhǎng)我只拿了每條log的開(kāi)頭部分,真實(shí)的會(huì)多很多內(nèi)容。當(dāng)我們撥出電話(huà)的時(shí)候,會(huì)輸入這么幾條log。
撥號(hào)->提醒->活動(dòng)
大致是這么個(gè)過(guò)程。經(jīng)過(guò)幾次測(cè)試發(fā)現(xiàn),電話(huà)接通時(shí)會(huì)進(jìn)入活動(dòng)狀態(tài),并會(huì)輸出:GET_CURRENT_CALLS id=1,ACTIVE 這條log,至此我們已經(jīng)接近成功了。
不過(guò)之后我又發(fā)現(xiàn)在撥號(hào)開(kāi)始到電話(huà)接通這段時(shí)間內(nèi)會(huì)經(jīng)過(guò)多次的“撥號(hào)->提醒->活動(dòng)”這樣的狀態(tài)變化,僅當(dāng)話(huà)筒中嘟聲響起后GET_CURRENT_CALLS這條日志會(huì)鎖定在ALERTING。在電話(huà)接通前便不再出現(xiàn)GET_CURRENT_CALLS日志了。
可能上面的這段表述大家不是很清楚,換句話(huà)說(shuō)在通話(huà)接通之前會(huì)出現(xiàn)多次的GET_CURRENT_CALLS ACTIVE 這樣的日志,而僅有一次是電話(huà)接通產(chǎn)生的。這就給我們?cè)斐闪寺闊2荒苤皇菃渭兊淖トET_CURRENT_CALLS ACTIVE 這樣的信息來(lái)判斷了。
我們只能通過(guò)一些邏輯上的判斷來(lái)實(shí)現(xiàn)了。
實(shí)例代碼講解
下面看我的代碼:
class TestThread implements Runnable { //振動(dòng)器 Vibrator mVibrator; //電話(huà)服務(wù) TelephonyManager telManager; public TestThread(Vibrator mVibrator, TelephonyManager telManager) { this.mVibrator = mVibrator; this.telManager = telManager; } @Override public void run() { //獲取當(dāng)前話(huà)機(jī)狀態(tài) int callState = telManager.getCallState(); Log.i("TestService", "開(kāi)始.........." + Thread.currentThread().getName()); //記錄撥號(hào)開(kāi)始時(shí)間 long threadStart = System.currentTimeMillis(); Process process; InputStream inputstream; BufferedReader bufferedreader; try { process = Runtime.getRuntime().exec("logcat -v time -b radio"); inputstream = process.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader( inputstream); bufferedreader = new BufferedReader(inputstreamreader); String str = ""; long dialingStart = 0; boolean enableVibrator = false; boolean isAlert = false; while ((str = bufferedreader.readLine()) != null) { //如果話(huà)機(jī)狀態(tài)從摘機(jī)變?yōu)榭臻e,銷(xiāo)毀線(xiàn)程 if (callState == TelephonyManager.CALL_STATE_OFFHOOK && telManager.getCallState() == TelephonyManager.CALL_STATE_IDLE) { break; } // 線(xiàn)程運(yùn)行5分鐘自動(dòng)銷(xiāo)毀 if (System.currentTimeMillis() - threadStart > 300000) { break; } Log.i("TestService", Thread.currentThread().getName() + ":" + str); // 記錄GSM狀態(tài)DIALING if (str.contains("GET_CURRENT_CALLS") && str.contains("DIALING")) { // 當(dāng)DIALING開(kāi)始并且已經(jīng)經(jīng)過(guò)ALERTING或者首次DIALING if (!isAlert || dialingStart == 0) { //記錄DIALING狀態(tài)產(chǎn)生時(shí)間 dialingStart = System.currentTimeMillis(); isAlert = false; } continue; } if (str.contains("GET_CURRENT_CALLS") && str.contains("ALERTING")&&!enableVibrator) { long temp = System.currentTimeMillis() - dialingStart; isAlert = true; //這個(gè)是關(guān)鍵,當(dāng)?shù)谝淮蜠IALING狀態(tài)的時(shí)間,與當(dāng)前的ALERTING間隔時(shí)間在1.5秒以上并且在20秒以?xún)?nèi)的話(huà) //那么認(rèn)為下次的ACTIVE狀態(tài)為通話(huà)接通. if (temp > 1500 && temp < 20000) { enableVibrator = true; Log.i("TestService", "間隔時(shí)間....." + temp + "....." + Thread.currentThread().getName()); } continue; } if (str.contains("GET_CURRENT_CALLS") && str.contains("ACTIVE") && enableVibrator) { mVibrator.vibrate(100); enableVibrator = false; break; } } Log.i("TestService", "結(jié)束.........." + Thread.currentThread().getName()); } catch (Exception e) { // TODO: handle exception } } }
我的這個(gè)方法比較牽強(qiáng),是通過(guò)判斷第一次DIALING與每一次ALERTING之間的間隔,如果間隔大于1.5秒,那么認(rèn)為已經(jīng)進(jìn)入了“嘟”聲提示的時(shí)候了,那么下一個(gè)ACTIVE將是電話(huà)接通。這個(gè)1.5秒是通過(guò)分析日志得出的。但是這種方法我始終覺(jué)得不太靠譜。如果大家有好的方法可以交流交流。
剩下的就是讓這個(gè)線(xiàn)程在電話(huà)撥出時(shí)觸發(fā),并且常駐在電話(huà)中時(shí)候準(zhǔn)備這就可以了??梢圆捎肧ervice配合Receiver來(lái)實(shí)現(xiàn)。Service來(lái)實(shí)現(xiàn)常駐,Receiver來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)撥出電話(huà)?;揪涂梢酝瓿晌覀兿胍墓δ芰恕?/p>
以上代碼我都測(cè)試過(guò),99%有效,哈哈。這里面提到了一些Android的基礎(chǔ)內(nèi)容,像logcat、Service、Receiver,這些如果大家不了解的話(huà)可以找相關(guān)文章資料學(xué)習(xí)下。
通過(guò)此文希望能幫助Android 開(kāi)發(fā)的朋友,謝謝大家對(duì)本站的支持!
相關(guān)文章
Android用戶(hù)界面開(kāi)發(fā)之:TextView的使用實(shí)例
Android用戶(hù)界面開(kāi)發(fā)之:TextView的使用實(shí)例,需要的朋友可以參考一下2013-05-05flutter?Bloc?實(shí)現(xiàn)原理示例解析
這篇文章主要為大家介紹了flutter?Bloc實(shí)現(xiàn)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11詳解Android過(guò)濾emoji表情正則表達(dá)式
這篇文章主要介紹了Android過(guò)濾emoji表情正則表達(dá)式,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06Android對(duì)話(huà)框AlertDialog與DatePickerDialog及TimePickerDialog使用詳解
這篇文章主要介紹了Android對(duì)話(huà)框中的提醒對(duì)話(huà)框AlertDialog、日期對(duì)話(huà)框DatePickerDialog、時(shí)間對(duì)話(huà)框TimePickerDialog使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-09-095分鐘快速實(shí)現(xiàn)Android爆炸破碎酷炫動(dòng)畫(huà)特效的示例
本篇文章主要介紹了5分鐘快速實(shí)現(xiàn)Android爆炸破碎酷炫動(dòng)效的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12Android中ActionBar以及menu的代碼設(shè)置樣式
這篇文章主要介紹了Android中ActionBar以及menu的代碼設(shè)置樣式的相關(guān)資料,需要的朋友可以參考下2015-07-07Android媒體開(kāi)發(fā)之音樂(lè)播放器
這篇文章主要為大家詳細(xì)介紹了Android媒體開(kāi)發(fā)之音樂(lè)播放器,播放SD卡中的音樂(lè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12