2012年4月1日 星期日

[TQC+ Android] 3-5 接聽來電 Use PhoneStateListener , ContentResolver





設計接聽來電程式 , 以程式接收來自DDMS的模擬來電 , 並顯示來電號碼於畫面 , 開啟程式之後就等待來電 , 不過倒不是使用BroadcastReceiver , 而是使用 PhoneStateListener 來監聽來電狀態 , 接到來電之後再以電話號碼搜尋通訊錄 , 找出來電的人是誰 , 以下是程式碼。


package COM.TQC.GDD03;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.TextView;

public class GDD03 extends Activity
{
 
  public TextView myTextView1;

  /** Called when the activity is first created. */
  
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    myTextView1 = (TextView) this.findViewById(R.id.textView1);
    
    exPhoneCallListener myPhoneStateListener = new exPhoneCallListener();
        
    TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    //取得TelephonyManager    
    
    telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    //將電話狀態的Listener加到取得的TelephonyManager中
    //在這裡我們選擇要聆聽的狀態為PhoneStateListener.LISTEN_CALL_STATE
  }  
   
  //運用PhoneStateListener監聽電話狀態  
  class exPhoneCallListener extends PhoneStateListener
  {    
    //PhoneStateListener 中不同的聆聽事件所要override的method各有不同, 請針對之前註冊的聆聽事件相對應的method進行override
 //我們之前選擇的聆聽事件為 LISTEN_CALL_STATE , 所以override onCallStateChanged() method 
 public void onCallStateChanged(int state, String incomingNumber)
    {
     switch (state)
     {
        //電話狀態是閒置的
        case TelephonyManager.CALL_STATE_IDLE:                    
           break;
        
           //電話狀態是接起的       
        case TelephonyManager.CALL_STATE_OFFHOOK:                
           break;
           
           //電話狀態是響起的        
        case TelephonyManager.CALL_STATE_RINGING:           
        getContactPeople(incomingNumber);
        //由電話號碼查詢聯絡人
        break;
        
        default:
        break;
     }
     
      super.onCallStateChanged(state, incomingNumber);
    }
  }

  private void getContactPeople(String incomingNumber)
  {
    myTextView1.setTextColor(Color.BLUE);
    
    ContentResolver contentResolver = getContentResolver();
    //必須靠ContentResolver的協助取得電話簿的資料
    
    Cursor cursor = null;
    
    String[] projection = new String[]
    {PhoneLookup._ID, PhoneLookup.DISPLAY_NAME, PhoneLookup.LOOKUP_KEY};
    //在query後我們要求ContentResolver回傳以上欄位的資料內容
    
    Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(incomingNumber));
    //將來電號碼append上去當作篩選條件 , 效果等於WHERE = incomingNumber
    
    cursor = contentResolver.query(uri, projection, PhoneLookup.NUMBER + " = ?", new String[] {incomingNumber}, null);
    /*
     * 因為我們所要查詢手機中通訊錄的資料, 所以使用ContentResolver class 來存取Content model
     * 我們可以將 PhoneLookup.CONTENT_FILTER_URI 這個參數視為想要存取的Table, 你也可以使用 null 他就會回傳所有的column 不過這樣比較沒有效率
     * PhoneLookup.NUMBER + "?" 這個字串相當於SQL語法中的 WHERE , 搭配後面的參數 new String[] {incomingNumber}
     * 其實可以寫成這樣 PhoneLookup.NUMBER = incomingNumber
     * 
     * public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
     * 
     * uri : The URI, using the content:// scheme, for the content to retrieve.
     * projection : A list of which columns to return. Passing null will return all columns, which is inefficient.
     * selection A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null will return all rows for the given URI.
     * selectionArgs You may include ?s in selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.
     * sortOrder How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered.
     * 
     */  
    
    if (cursor.getCount() == 0) //判斷此電話號碼存不存在電話簿中, 不存在的話使用 unknown Number
    {
      myTextView1.setText("unknown Number:" + incomingNumber);
    } 
    else if (cursor.getCount() > 0) //這來電號碼存在電話簿中 , 顯示聯絡人姓名
    {
      cursor.moveToFirst();      
      
      String name = cursor.getString(1);
      //取得index 1 欄位的字串型態資料, 關於欄位的順序就是當初我們 projection 字串陣列中的字串順序
      //如果當初的參數為 null,那它會回傳所有的column, 這樣最好搭配 getColumnIndex(String ColumnName)來找尋指定column的資料 , 而回傳的資料型態同樣必須查詢文件才能得知
      
      myTextView1.setText(name + ":" + incomingNumber);
    }
  }
}


這個程式的運作流程開頭有大概說明了 , 程式中註解也進一步解釋了 , 在這邊在稍微補充 , PhoneStateListener 可以監控手機裝置的狀態 , 訊號強度 來電狀態 基地台地點....等等不少事件, , PhoneStateListener 中不同的監控事件所要override的method各有不同, 請針對之前註冊的監控事件相對應的method進行override , 如果想查詢監控事件所對應到的method可以造訪這個文件 : PhoneStateListener 




接下來是AndroidManifest.xml , 別忘了因為我們要監控來電的狀態所以要註冊 <uses-permission android:name="android.permission.READ_PHONE_STATE"/> 另外讀取通訊錄也需要註冊權限 : <uses-permission android:name="android.permission.READ_CONTACTS"/>
    
    
    
        
            
                
                
            
        
    
    
 















P.S. 題目中所要求的Variable和Method皆會保留 , 也會根據題目所要求的流程去實作 , 縱使題目要求繞遠路....







沒有留言:

張貼留言

Google Analytics