如何寫一個電話簿(Phone book) (ContentResolver)

如何寫一個電話簿(Phone book) (ContentResolver)

情境

要怎麼寫一個電話簿?
首先要先了解幾個課題,
如何動態新增內建ListView + 資料庫SQLiteOpenHelper存取[記事本]
如何使用打電話功能(Call)

程式碼說明

首先我們必須取得手機裡面的電話簿(廢話= =)
接著把這些電話號碼放進一個ListView裡面。

一開始先定義XML, 放入一個ListView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="example.givemepass.providercontactdemo.MainActivity">

    <ListView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/listview" />
</RelativeLayout>

主程式碼

public class MainActivity extends AppCompatActivity {
    private static final String NAME = "name";
    private static final String NUMBER = "number";
    private List<Map<String, String>> contactsArrayList;
    private ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
    }
    private void initView(){
        listView = (ListView) findViewById(R.id.list);
        SimpleAdapter adapter = new SimpleAdapter(this,
                contactsArrayList,
                android.R.layout.simple_list_item_1,
                new String[] { NAME,NUMBER },
                new int[] { android.R.id.text1,android.R.id.text2 });
        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final String number = contactsArrayList.get(position).get(NUMBER);
                new AlertDialog.Builder(MainActivity.this)
                    .setTitle(number)
                    .setItems(new String[]{"Call"}, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            switch (which) {
                                case 0:
                                    Intent call = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + number));
                                    startActivity(call);
                                    break;
                            }
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .show();
            }
        });
    }

    private void initData(){
        getPhoneBookData();
    }
    public void getPhoneBookData(){
        contactsArrayList = new ArrayList<>();
        Cursor contacts_name = getContentResolver().query(
                ContactsContract.Contacts.CONTENT_URI,
                null,
                null,
                null,
                null);

        while (contacts_name.moveToNext()) {
            Map<String, String> map = new HashMap<>();
            String phoneNumber = "";
            long id = contacts_name.getLong(
                    contacts_name.getColumnIndex(ContactsContract.Contacts._ID));
            Cursor contacts_number = getContentResolver().query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID
                            + "=" + Long.toString(id),
                    null,
                    null);

            while (contacts_number.moveToNext()) {
                phoneNumber = contacts_number
                        .getString(contacts_number.getColumnIndex(
                                ContactsContract.CommonDataKinds.Phone.NUMBER));
            }
            contacts_number.close();
            String name = contacts_name.getString(contacts_name
                    .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            map.put(NAME, name);
            map.put(NUMBER, phoneNumber);
            contactsArrayList.add(map);
        }
    }
}

我們ListView想要放入人名以及電話號碼,
本來我們可以利用這樣的方法取得人名以及電話,

Cursor cursor = getContentResolver().query(
    Contacts.People.CONTENT_URI, 
    new String[]{Contacts.People.NAME,Contacts.People.NUMBER},
    null,
    null,
    null);

但是Android2.0以後, 就把這個方法deprecated了。
所以我們採取新的方法,
查詢手機裡面電話簿人名,

Cursor contacts_name = getContentResolver()
        .query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

它會回傳一個Cursor, 就可以利用下面的方法取出人名

String name = contacts_name
   .getString(
   contacts_name.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

可是他的電話號碼卻是放在另外一個地方,
因此我們必須重新查詢一次,

Cursor contacts_number = getContentResolver().query(
     ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
     null,
     null,
     null,
     null);

可是這兩個Table怎麼連起來呢? 很簡單, 它裡面有一個欄位叫作_id,
通常習慣上我們都會在每個Table裡面放入一個primary key,
這樣才會符合正規化,然後利用這個key將兩個Table關聯起來。

因此我們寫了一個方法, 把這個兩個Table關聯起來,

public void getPhoneBookData(){
    contacts_name = getContentResolver().query(
        ContactsContract.Contacts.CONTENT_URI,
        null,
        null,
        null,
        null);

    while (contacts_name.moveToNext()) {
      contactsMap = new HashMap<String, String>();
        String phoneNumber = "";
        long id = contacts_name.getLong(
            contacts_name.getColumnIndex(ContactsContract.Contacts._ID));
        contacts_number = getContentResolver().query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            null,
            ContactsContract.CommonDataKinds.Phone.CONTACT_ID 
            + "=" + Long.toString(id),
            null,
            null);

        while (contacts_number.moveToNext()) {
            phoneNumber = contacts_number
                .getString(contacts_number.getColumnIndex(                                                
                ContactsContract.CommonDataKinds.Phone.NUMBER));
        }
        contacts_number.close();
        String name = contacts_name.getString(contacts_name
            .getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
        contactsMap.put(NAME, name);
        contactsMap.put(NUMBER, phoneNumber);
        contactsArrayList.add(contactsMap);
    }
}

這樣一來, 我們就可以直接把存好的ArrayList丟進SimpleAdapter裡面,

SimpleAdapter adapter = new SimpleAdapter(this,
    contactsArrayList,
    android.R.layout.simple_list_item_2,
    new String[] { NAME,NUMBER }, 
    new int[] { android.R.id.text1,android.R.id.text2 });
listView.setAdapter(adapter);

而當我們按下某一個Item的時候, 會跳出AlertDialog讓我們選擇要不要打電話。

listView.setOnItemClickListener(new OnItemClickListener(){
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        final String name = contactsArrayList.get(position).get(NAME);
        final String number = contactsArrayList.get(position).get(NUMBER);
        new AlertDialog.Builder(ProviderContactDemoActivity.this)
            .setTitle(number)
            .setItems(new String[]{"Call"}, new DialogInterface.OnClickListener() {
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                     switch(which){
                     case 0:
                       Intent call = new Intent(
                             Intent.ACTION_CALL, Uri.parse("tel:" + number));
                         startActivity(call);
                         break;
                     }
                 }
            })
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                     // TODO Auto-generated method stub
                 }    
            })
            .show();    
    } 
});

最後記得加上權限,

<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />

結果如下圖





程式碼