android 高階之旅 (十三) 真! 如何判斷去電是否接通? 已解決!
最近做一個關於來去電監聽然後結束通話發簡訊功能的專案,碰到不知如何判斷去電是否接通的問題,多方查詢,網上的答案不一而足,最後 ,在借鑑網上的答案和自己的修改後,得出解決方案記錄如下:
判斷來電是否接通
這個好判斷。
1. 當為來電時,電話狀態首先進入TelephonyManager.CALL_STATE_RINGING 也就是 響鈴 狀態
2. 接通時 進入 TelephonyManager.CALL_STATE_OFFHOOK 狀態,也就是接通狀態
3. 當結束通話時,進入TelephonyManager.CALL_STATE_IDLE 狀態 ,也就是結束通話狀態。
所以,我們只需判斷電話狀態由 RINGING—>OFFHOOK時,就可以知道電話接通了。
但是當我們是撥出電話時,一撥的時候電話就會處於OFFHOOK狀態,接通之後也是OFFHOOK狀態,那咋辦呢??
判斷去電是否接通
多方查詢,最後通過查詢 calllog 也就是通話記錄的方法,來判斷電話是否撥通!
- 本來我也對 calllog瞭解不多,這裡給個傳送門,不太瞭解的同學可以看看關於這方面的API 。
- 因為android平臺上的通話記錄是以Content Provider的形式儲存在手機上的,因此你需要使用ContentResolver來查詢通話記錄,返回Cursor介面。
- 然後通過這個contentresolver 來查詢通話記錄的資料庫 ,得到一個遊標 cursor,然後通過這個遊標得到我們想要的通話記錄
然後就可以查詢到通話記錄中的duration ,這就是最關鍵的,當duration>0的時候就說明電話接通了哈哈哈是不是也不難?!
光說不練假把式 且看程式碼!
下面給出我整個類的程式碼,裡面關於資料庫啊什麼的,各位就不用深究,關鍵看如何得到去電是否接通。
註釋也較詳細,各位有不懂,歡迎留言或私信
public class EndCallReceiver extends BroadcastReceiver {
private RefuseComeMsgDao refuseComeMsgDao;
private String content;
@Override
public void onReceive(final Context context, Intent intent) {
//獲取來電號碼
final String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
TelephonyManager tm = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
int phoneState = tm.getCallState();
switch (phoneState) {
//如果電話的狀態是來電響鈴
case TelephonyManager.CALL_STATE_RINGING:
LogUtil.log("響鈴!!!!");
// 這裡儲存的是如果是來電響鈴 則說明當前是來電 儲存為TRUE
context.getSharedPreferences("blacklist", MODE_PRIVATE).edit().putBoolean("iscome", true).commit();
context.getSharedPreferences("blacklist", MODE_PRIVATE).edit().putBoolean("offhook", false).commit();
//這裡設定匹配黑名單號碼 或者此時是否處於拒接時間段
if (isInBlackList(number, context) || DateUtil.isRefuse(context)) {
Class<TelephonyManager> telephonyManagerClass = TelephonyManager.class;
try {
//通過反射獲取getITelephony方法
Method method = telephonyManagerClass.getDeclaredMethod("getITelephony", new Class[0]);
//設定該方法可訪問
method.setAccessible(true);
//呼叫getITelephony方法獲取ITelephony的例項
ITelephony itelephony = (ITelephony) method.invoke(tm, new Object[]{});
//結束通話電話
itelephony.endCall();
//由黑名單或者拒接時間段結束通話的電話,儲存狀態以防止重複傳送簡訊
//這裡儲存的狀態true 表示 是主動拒接的!
context.getSharedPreferences("refuseTime", MODE_PRIVATE).edit()
.putBoolean("dorefuse", true)
.commit();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
case TelephonyManager.CALL_STATE_IDLE: //電話狀態 結束通話, 在這裡傳送 來去電簡訊
LogUtil.log("結束通話!!");
//如果是來電 傳送當前來電模版簡訊
SharedPreferences preferences = context.getSharedPreferences("blacklist", MODE_PRIVATE);
SharedPreferences preferences2 = context.getSharedPreferences("refuseTime", MODE_PRIVATE);
boolean iscome = preferences.getBoolean("iscome", false);//是否為來電
if (iscome) { //------------------------如果是來電----------------------------
//--------如果是通過黑名單或者時間段拒絕的 傳送拒接簡訊-------
if (preferences2.getBoolean("dorefuse", false)) { //這個if裡判斷是否為主動拒接!!
//先得到當前設定的拒接簡訊
DaoSession daoSession = DBUtil.initDb(new GreenDaoContext());
refuseComeMsgDao = daoSession.getRefuseComeMsgDao();
RefuseComeMsg refuse1 = refuseComeMsgDao.queryBuilder().where(RefuseComeMsgDao.Properties.Type.eq("refuse")).build().unique();
if (refuse1 == null) {
content = "";
} else {
content = refuse1.getContent();
}
//傳送拒接簡訊
if (!content.equals("") && !content.equals("不傳送")) {
sendSMS(number, content, context);
}
// 拒接狀態恢復
preferences2.edit().putBoolean("dorefuse", false).commit();
} else { // ---------否則傳送來電簡訊---------
String content = doGetComeMsg();//得到當前的來電回覆簡訊
if (!content.equals("不傳送")) {
sendSMS(number, content, context);
}
preferences.edit().putBoolean("iscome", false).commit();
}
} else if (!iscome) {
//--------------------如果是去電 傳送去電簡訊--------------------//
/*休眠一秒 等待通話記錄寫入資料庫
* 如果不休眠這一秒 直接查詢資料庫 會查詢不到當前打的這個電話的記錄
* 因為在系統將通話記錄寫入資料庫之前就開始查詢操作 所以查到的最近的記錄實際上是上一次撥打的記錄
* 因此休眠一秒 可能在效能垃圾的手機上1s不夠 ? 不至於把 !
* */
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
boolean callLogState = getCallLogState(context,number);
if (callLogState) {
//----------------去電接通 執行傳送去電簡訊操作!--------------------
String content = doGetOutMsg(); //得到當前的去電回覆簡訊
if (!content.equals("") && !content.equals("不傳送")) {
sendSMS(number, content, context);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
LogUtil.log("OFFHOOK");
break;
}
}
/**
* 獲得當前的去電回覆簡訊
* @return 當前設定的去電回覆模板
*/
private String doGetOutMsg() {
String string;
DaoSession daoSession = DBUtil.initDb(GreenDaoContext.getInstance());
Database database = daoSession.getDatabase();
CurrentTemplateDaoDao currentTemplateDaoDao = daoSession.getCurrentTemplateDaoDao();
currentTemplateDaoDao.createTable(database, true); //無表就建表
//查詢資料庫中type為out的資料
CurrentTemplateDao out = currentTemplateDaoDao.queryBuilder().where(CurrentTemplateDaoDao.Properties.Type.eq("out")).build().unique();
if (out != null) {
string = out.getContent();
} else {
string = "不傳送";
}
return string;
}
/**
* 獲得當前的來電回覆簡訊
* @return 當前設定的來電模板內容
*/
private String doGetComeMsg() {
String string;
DaoSession daoSession = DBUtil.initDb(GreenDaoContext.getInstance());
Database database = daoSession.getDatabase();
CurrentTemplateDaoDao currentTemplateDaoDao = daoSession.getCurrentTemplateDaoDao();
currentTemplateDaoDao.createTable(database, true); //無表就建表
//查詢資料庫中type為come的資料
CurrentTemplateDao come = currentTemplateDaoDao.queryBuilder().where(CurrentTemplateDaoDao.Properties.Type.eq("come")).build().unique();
if (come != null) {
string = come.getContent();
} else {
string = "不傳送";
}
return string;
}
//判斷號碼是否在黑名單中
private boolean isInBlackList(String number, Context context) {
List<BlackList> blackLists = DBUtil.getBlackListDao().loadAll();
for (BlackList blackList : blackLists) {
if (blackList.getNumber().equals(number)) {
//匹配成功
return true;
}
}
return false;
}
private void sendSMS(String phoneNum, String message, Context context) {
//在這裡判斷此號碼在設定重複時段內是否重複
//獲取簡訊管理器
android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault();
//拆分簡訊內容(手機簡訊長度限制)
List<String> divideContents = smsManager.divideMessage(message);
for (String text : divideContents) {
/**
* 引數4和5:
* sentIntent——如果不為空,當訊息成功傳送或失敗這個PendingIntent就廣播。結果程式碼是Activity.RESULT_OK表示成功,或RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_RADIO_OFF、RESULT_ERROR_NULL_PDU之一表示錯誤。對應RESULT_ERROR_GENERIC_FAILURE,sentIntent可能包括額外的“錯誤程式碼”包含一個無線電廣播技術特定的值,通常只在修復故障時有用。
* 每一個基於SMS的應用程式控制檢測sentIntent。如果sentIntent是空,呼叫者將檢測所有未知的應用程式,這將導致在檢測的時候傳送較小數量的SMS。
* deliveryIntent——如果不為空,當訊息成功傳送到接收者這個PendingIntent就廣播。
*/
smsManager.sendTextMessage(phoneNum, null, text, null, null);
//將傳送的資訊新增到 傳送記錄 資料庫中
DBUtil.initDb(new GreenDaoContext()).getSendRecordDao().insert(new SendRecord(null, phoneNum, DateUtil.getDate(), message));
}
}
private boolean getCallLogState(Context context,String number) {
boolean isLink = false;
ContentResolver cr = context.getContentResolver();
PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG);
final Cursor cursor = cr.query(CallLog.Calls.CONTENT_URI,
new String[]{CallLog.Calls.NUMBER,CallLog.Calls.TYPE,CallLog.Calls.DURATION},
CallLog.Calls.NUMBER +"=?",
new String[]{number},
CallLog.Calls.DATE + " desc");
int i = 0;
while(cursor.moveToNext()){
if (i == 0) {//第一個記錄 也就是當前這個電話的記錄
int durationIndex = cursor.getColumnIndex(CallLog.Calls.DURATION);
long durationTime = cursor.getLong(durationIndex);
// Log.d("test", "getCallLogState: -----------------duration= " + durationTime);
if(durationTime > 0){
LogUtil.log("到這裡了 這是if裡 durationTime = "+durationTime);
isLink = true;
} else {
LogUtil.log("到這裡了 這是else裡");
isLink = false;
}
}
i++;
// int durationIndex = cursor.getColumnIndex(CallLog.Calls.DURATION);
// long durationTime = cursor.getLong(durationIndex);
}
return isLink;
}
}
**程式碼中的獲取電話狀態是通過AIDL實現的。
程式碼不是啥完美的程式碼,有瑕疵也歡迎吐槽。
以上。**
相關推薦
android 高階之旅 (十三) 真! 如何判斷去電是否接通? 已解決!
最近做一個關於來去電監聽然後結束通話發簡訊功能的專案,碰到不知如何判斷去電是否接通的問題,多方查詢,網上的答案不一而足,最後 ,在借鑑網上的答案和自己的修改後,得出解決方案記錄如下: 判斷來電是否接通 這個好判斷。 1. 當為來電時,電話狀態首先進入T
Python學習之旅(十三)
Python基礎知識(12):函式(Ⅲ) 高階函式 1、map map()函式接收兩個引數,一個是函式,一個是Iterable,map將傳入的函式依次作用到序列的每個元素,並把結果作為新的Iterator返回。 def test(x): return x+2 n=map(test,[1,
我的Android NDK之旅(四),android串列埠通訊-mac+串列埠除錯工具
一些關於串列埠的知識 什麼是串列埠 串列埠是計算機上一種非常通用裝置通訊的協議,不要與通用序列匯流排Universal Serial Bus(USB)混淆。大多數計算機包含兩個基於RS232的串列埠。串列埠同時也是儀器儀表裝置通用的通訊協議;很多GP
我的Android NDK之旅(一),不使用ndk-build命令來建立jni
最近閒來無事,想摸索下一下ndk,可是ndk不是塊好啃的骨頭,但作為一名程式設計師,什麼都要了解下,對吧╮( ̄▽ ̄)╭。首先我想吐槽一下,網上有些部落格寫的很亂,一上來就貼一段程式碼,也不告訴是要幹什麼,程式碼一寫完就完事,這讓初學者很難理解jni到底是個什
Android探索之旅(第三十三篇)恩?你想成為Android架構師,我這裡有料呦~~(持續更新中)
筆者認為你若想要成為熟悉及精通Android知識,勢必對於Gradle要求很是熟悉,推薦大家看徐宜生的《Android群英傳·神兵利器》,這本書最突出的就是它對於Gradle講解的非常詳細,讀完這本書之
Android的DatePicker和TimePicker-android學習之旅(三十八)
cursor ini lis drawable textview @+ type pin view DatePicker和TimePicker簡單介紹 DatePicker和TimePicker是從FrameLayout繼承而來。他們都是比較簡單的組件
Android破解學習之路(十三)—— 另類的破解VIP思路
前言 一般按照以往,我們想要獲得某個軟體的VIP,一般是通過修改支付寶的支付流程,原本購買失敗的,我們修改程式碼,從而使得失敗變成了成功,不花費金錢 另類思路 有些軟體將判斷使用者是否為VIP的程式碼寫在了本地,這樣我們就可以修改這個程式碼獲得VIP,注意是有些軟體,並不是所有 判斷VIP也是一個簡單
大疆無人機Android版SDK開發踩坑之旅(一)----前言
最近一段時間一直在做大疆無人機安卓版開發,這水也是挺深的,不仔細看官網SDK的介紹就會遇到各種各樣的坑,簡單記錄一下,希望可以讓其他人少走一些彎路。 安卓端用到的SDK大概有兩種:Android SDK和Android UX SDK Android SDK(官網介紹): 開發人員可以通過SDK
Android探索之旅(第十篇) 推薦幾款非常好用的Bug除錯工具
首推 騰訊Bugly - 一種愉悅的開發方式是一款非常方便幫組開發者實時的檢測App的異常及應用統計,還有更加強大的應用更新及熱修復,讓你的App 6飛起 官網地址:https://bugly.qq
Android探索之旅(第三十六篇)Android中使用者反饋需要開發?不存在的
作為一個合格的Android開發者,我們時不時會碰到產品給使用者反饋功能需求,你大概需要一天?兩天?三天?請求介面?NO NO NO~~~不存在的 下面介紹一個平臺叫吐個槽官網 1. 進入平臺後需要建立產品,建立好之後會為你分配APPID 2.
Android學習之旅(第一篇) SurfaceView的原理以及使用場景
為什麼要使用SurfaceView來實現動畫? 因為View的繪圖存在以下缺陷: View缺乏雙緩衝機制 當程式需要更新View上的影象時,程式必須重繪View上顯示的整張圖片 新執行緒無法直接更新View元件 SurfaceView的繪圖機制
Android探索之旅(第十四篇)Android中實現炫酷效果的Demo(持續收錄中......)
浪起來!使用 drawBitmapMesh 實現模擬水波紋效果 簡書傳送門 三十秒實現QQ首頁動畫特效 BMoveView為RadioGroup新增移動的特
Android的SeekBar和RateBar的使用-android學習之旅(三十二)
SeekBar簡介 SeekBar允許使用者拖動,進行調節經常用於音量調節等方面。 android:thumb設定drawable物件來表示拖動的物體。 setOnSeekBarChangeLis
SimpleAdapter和Baseadapter填充listActivity-android學習之旅()
簡介 SimpleAdapter的功能是能夠為AbsListView提供複雜的資料,需要構造ListView 程式碼示例 package peng.liu.testview; import android.app.Activity; import a
Android日曆檢視(CalendarView)講解-android學習之旅(三十六)
CalendarView簡介 CalendarView用於顯示和選擇日期,如果希望監聽事件的改變可以用setOnDateChangeListener()方法。 CalendarView屬性介紹
Android探索之旅(第三十七篇)網路動態獲取並載入Selector(Glide篇)
最近公司一個專案選單切換欄需要動態從後臺獲取,於是翻閱了很多資料來去學習這一塊的知識,很多方案很不錯,但並不適合我現在要做的專案,我專案中切換tab的icon是從後臺獲取到的,並且載入選中未選中兩套圖,只允許第一次載入慢,往後就採用快取載入,無延遲,效果圖
Android的stateListDrawable,layerDawable,clipdrawable,AnimationDarwable介紹-android學習之旅(五十五)
StatelistDrawable資源 程式碼示例 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:a
Android探索之旅(第二十五篇)騰訊熱修復框架Tinker與阿里第三代熱修復框架Sophix對比
本人在開發專案中仍然使用的還是Tinker熱修復框架,在專案中遇到Bug,Tinker都能夠解決,可以說是萬無一失,但是在網際網路時代也必須懷揣著新生的事物必將取代先前的事物的心態,因此就索性集成了Sophix,下面由我來分析一下它們之間的區別 從上
F# 之旅(上)
簡單 ssi arp compile posit slist change 縮進 類型 寫在前面的話 解答一下在上一篇文章《在Visual Studio中入門F#》中有人的提問, 1. 問:是準備寫 F# 系列嗎? 答:當然不是,本人也是剛剛學習 F#,只是
css重構之旅(一)
rdquo lan set 變化 部分 網站 一個 寬度 lang css重構之旅 >前言: 今年我大一,馬上就要大二了。從高三畢業暑假到大學的這一年馬上過去,馬上迎來大二生活.學習前端也有將近一年了。一昧去追求那些視覺的效果和相對高端和新穎的技術,反而忽略了最基礎