apps目錄的contacts應用(有讀取通話記錄功能),是訪問provider目錄的provider.contacts應用(有暴露通話記錄),所以要閱讀Android操作系統源碼-->packages/providers/ContactsProvider通話記錄的(內容提供者)

閱讀 com.android.providers.contacts 數據庫與表
Android操作系統的文件系統目錄/data/data/com.android.contacts,是訪問/data/data/com.android.providers.contacts(內容提供者應用)
所以需要閱讀/data/data/com.android.providers.contacts(內容提供者應用)的數據庫:

首選要有一條通話記錄

打開 contacts2.db
打開后:表非常多,視圖非常多,等等,但只需關心,calls(通話記錄表)

閱讀 com.android.providers.contacts AndroidManifest.xml
android:name="CallLogProvider" 通話記錄的內容提供者
android:authorities="call_log" 授權唯一標識
android:exported="true" 允許對外輸出
android:readPermission="android.permission.READ_CALL_LOG" 訪問者必須要配置的權限
android:writePermission="android.permission.WRITE_CALL_LOG" 訪問者必須要配置的權限
<provider android:name="CallLogProvider" android:authorities="call_log" android:syncable="false" android:multiprocess="false" android:exported="true" android:readPermission="android.permission.READ_CALL_LOG" android:writePermission="android.permission.WRITE_CALL_LOG"> </provider>
閱讀 com.android.providers.contacts CallLogProvider.java
首先要找到的就是Uri,所以搜索UriMatcher的matcher.addURI
有規律,通常情況下,matcher.addURI(授權, path, code),第二個參數 path 和數據庫表名對應的 calls
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS); sURIMatcher.addURI(CallLog.AUTHORITY, "calls/#", CALLS_ID); sURIMatcher.addURI(CallLog.AUTHORITY, "calls/filter/*", CALLS_FILTER); }
C應用 AndroidManifest.xml 權限的配置:
<!-- 訪問操作系統短信通話記錄提供者應用,需要加入的權限 android:readPermission="android.permission.READ_CALL_LOG" android:writePermission="android.permission.WRITE_CALL_LOG" --> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
<!-- C應用配置撥打電話的權限 撥打電話的權限 --> <uses-permission android:name="android.permission.CALL_PHONE" />
C應用 讀取操作系統通話記錄並/撥打電話/發送短信/復制號碼到撥號盤 Java代碼:
package liudeli.cp.client; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.CallLog; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.SimpleCursorAdapter; public class CallLogActivity extends Activity { /** * 找尋到了Android操作系統的通話記錄內容提供者的授權和Uri * android:authorities="call_log" * sURIMatcher.addURI(CallLog.AUTHORITY, "calls", CALLS); */ private final String AUTHORITY = "call_log"; private Uri callLogUri = Uri.parse("content://" + AUTHORITY + "/calls"); /** * 系統也提供了常量的方式來獲取Uir,為了學習,還是直接看源碼復制出來的比較理解些 */ /*private final String AUTHORITY = CallLog.AUTHORITY; private Uri callLogUri = Uri.parse(CallLog.CONTENT_URI + "/calls");*/ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_call_log); ListView listView = findViewById(R.id.listview); String[] porjecting = new String[]{"_id" ,"number", "date", "type"}; final Cursor cursor = getContentResolver().query(callLogUri, porjecting, null, // 不要查詢條件 null, // 不要查詢條件值 null); // 不排序 while (cursor.moveToNext()) { Log.d("cccc", "" + cursor.getString(0) + " "+ cursor.getString(1) + " " + cursor.getString(2) + " "+ cursor.getString(3)); } final SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(this, // 上下文 R.layout.layou_calllog_item, // Item顯示的布局 cursor, // 游標數據 porjecting, // 數據從哪里來(從Cursor中獲取對應的字段) new int[]{R.id.tv_id, R.id.tv_number, R.id.tv_date, R.id.tv_type} // 數據到哪里去,到Item布局里面的控件顯示 ); listView.setAdapter(cursorAdapter); // 千萬不能關閉 cursor.close();,否則數據展示不出來 listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { // 獲取Item的值 (轉換規則,傳入什么類型,就轉換成什么類型) final Cursor itemCursor = (Cursor) cursorAdapter.getItem(position); /** * 把Cursor移動到指定的行數,然后在取值 itemCursor.getString(0~9) */ itemCursor.moveToPosition(position); new AlertDialog.Builder(CallLogActivity.this) .setTitle("請選擇") /*.setMessage("請選擇,下面列表的功能")*/ //列表對話框不能設置這個,否則顯示不出來 .setItems(new String[]{"撥打電話", "復制號碼到撥號盤", "復制號碼到短信編輯界面"}, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 把Cursor移動到指定的行數,然后在取值 itemCursor.getString(0~9) String number = itemCursor.getString(itemCursor.getColumnIndex("number")); /** * 下面這三個功能,都需要隱式意圖的方式去激活系統暴露的組件 * 匹配規則:只要匹配一組,就可以來 */ switch (which) { case 0: // 撥打電話 Intent intentCall = new Intent(); intentCall.setAction(Intent.ACTION_CALL); intentCall.setData(Uri.parse("tel:" + number)); startActivity(intentCall); break; case 1: // 復制號碼到撥號盤 Intent intentCopyCall = new Intent(); intentCopyCall.setAction(Intent.ACTION_DIAL); intentCopyCall.setData(Uri.parse("tel:" + number)); startActivity(intentCopyCall); break; case 2: // 復制號碼到短信編輯界面 Intent intentSmsEdit = new Intent(); intentSmsEdit.setAction(Intent.ACTION_VIEW); intentSmsEdit.setData(Uri.parse("sms:" + number)); startActivity(intentSmsEdit); break; default: break; } } }) .show(); return false; } }); } }
C應用顯示的Layout:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
C應用顯示的Layout --> ListVIew-->Item布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp"> <!-- 默認比重為0 我先填充 --> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="id" android:textColor="@android:color/black" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="number" android:textColor="@android:color/black" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="date" android:textColor="@android:color/black" android:layout_marginTop="5dp" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="type" android:textColor="@android:color/black" android:layout_marginTop="5dp" /> </LinearLayout> <!-- 哥們,你已經填充完來吧,剩下的空間我全部使用 --> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" android:layout_weight="1" android:layout_marginLeft="20dp" > <TextView android:id="@+id/tv_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="id" android:textColor="@android:color/black" /> <TextView android:id="@+id/tv_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="number" android:textColor="@android:color/black" /> <TextView android:id="@+id/tv_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="date" android:textColor="@android:color/black" android:layout_marginTop="5dp" /> <TextView android:id="@+id/tv_type" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="type" android:textColor="@android:color/black" android:layout_marginTop="5dp" /> </LinearLayout> </LinearLayout>
C應用效果圖:

真實開發中,必須要用常量,才靠譜,萬一字段變來怎么辦,是吧
/** * 通話記錄通常是有常量的 */ Calls.Date; CallLog.Calls._ID .... CallLog.AUTHORITY CallLog.Calls.NUMBER; CallLog.CONTENT_URI ....

