在研究完Android的照相功能之后,刘关张终于对刘璋留下的烂摊子有了一个大概的了解。刘关张和孔明四人坐在会议室里,讨论起关乎到他们未来走向的问题。
刘备:“虽然是搞懂了刘璋留下来的工程,可是感觉这样的项目以目前的状态做出来也没什么太大意思啊,发布出去也不会有什么人用的。”
关羽:“是呀,我觉得应该把我们之前学到的东西融入到这个产品当中。”
刘备:“嗯,我觉得首先得把LBS内容加入其中,增强移动产品的特性。”
张飞:“大哥,啥是LBS来着,老鄙视的简称?”
刘备:“你什么记性,基于地理位置信息的服务啊,就拿照相来说,可以看到自己位置附近用户拍摄的照片风景。”
孔明:“可以呀主公,这个点子很不错啊!”
刘备擦了擦眼泪,说:“太感动了,军师竟然没有吐槽我。”
关羽:“另外我还有个想法,我们何不利用Android具有通话的性质,获取用户联系人信息,做出像米什么聊,微那个信这样的产品!”
刘备一拍掌:“啊!差点忘了还能打电话!”
1.1. 常用通信功能简介
一般而言,手机首先是一部电话,能够直接拨打和接听电话。其次,手机应该能够发送和接收文字消息,也就是短信的功能。随着邮件逐渐的成为商务中最主要的联络方式,手机也必须要支持邮件的功能。Android智能手机的最大特点就是可以充分的利用它的设备功能进行应用开发,如上述所说电话、短信、邮件等功能。本章就重点讲解如何在应用程序中使用电话、短信和邮件的功能。
1.2. 电话
手机设备主要的功能就是电话。应用程序不能打断任何一次通话,因此与电话相关的操作都应该具有最高的优先权。Android系统电话的基本功能包括:拨打电话、接听电话、挂断电话、来电响铃等。除了具有完整的电话基本功能外,Android手机还具有联系人管理、来去电管理、联系人收藏、语音拨号、急救电话等多种功能。本节将详细地介绍Android电话应用开发中常用的类,然后通过一个简单的电话实例讲解如何使用这些类。
1.2.1.TelephonyManager
Android中的TelephonyManager类提供了一系列用于访问与手机通讯相关的方法,和手机SIM卡、电信网络状态及手机用户的信息获取方法。在应用程序中可以使用这些方法获取相关数据。TelephonyManager对象需要通过Context.getSystemService(Context.TELEPHONY_SERVICE)方法来获得。
Intent可以通过描述找到对应的组件,将Intent传递给调用的组件,完成组件的调用。拨打电话Intent的具体代码如下:newIntent("android.intent.action.CALL",Uri.parse("tel:" +number))。TelephonyManager类的常用字段和方法如下所示:
表25-1 TelephonyManager类常用字段和方法
常用字段和方法名称 |
说明 |
static final String EXTRA_STATE_IDLE |
电话无任何状态。 |
static final String EXTRA_STATE_OFFHOOK |
接起电话时。 |
static final String EXTRA_STATE_RINGING |
电话打进来时。 |
int getCallState() |
获取数据连接状态。连接状态包括DATA_CONNECTED:已连接;DATA_CONNECTING:正在连接;DATA_DISCONNECTED:断开;DATA_SUSPENDED:暂停。 |
String getDeviceId() |
返回当前移动终端的唯一标识。如果是GSM网络,返回IMEI;如果是CDMA网络,返回MEID。 |
String getDeviceSoftwareVersion() |
返回移动终端的软件版本。 |
String getLine1Number() |
返回手机号码。 |
List<NeighboringCellInfo> getNeighboringCellInfo() |
返回当前移动终端附近的其他移动终端的信息。 |
String getNetworkCountryIso() |
返回ISO标准的国家码,即国际长途区号。 |
String getNetworkOperatorName() |
返回移动网络运营商的名字。 |
int getNetworkType() |
获取网络类型,包括CDMA、EDGE、EVDO0、EVDOA、GPRS、HSDPA、HSPA、HSUPA、UMTS等 |
int getPhoneType() |
返回移动终端的类型。包括PHONE_TYPE_CDMA电信、PHONE_TYPE_GSM移动和联通、PHONE_TYPE_NONE 手机制式。 |
String getSimSerialNumber() |
返回SIM卡的序列号。 |
int getSimState() |
返回移动终端SIM的状态。 |
String getSubscriberId() |
返回用户唯一标识,例如GSM网络的IMSI编号。 |
String getVoiceMailAlphaTag() |
获取与语音信箱号码关联的字母标识。 |
String getVoiceMailNumber() |
返回语音邮件号码。 |
boolean isNetworkRoaming() |
返回手机是否处于漫游状态。 |
void listen(PhoneStateListener listener, int events) |
注册一个监听器,当电话状态改变时接收消息。 |
刘备:IMEI国际移动设备识别码是区别移动设备的标志,储存在移动设备中。MEID 移动设备识别码是CDMA手机的身份识别码。手机中的IMEI和MEID号码就如同我们生活中的身份证一样,它是识别手机身份的重要依据,可以对该手机进行跟踪和监管。 |
1.2.2.PhoneStateListener
PhoneStateListener类提供了电话、网络等状态监控。通过向TelephonyManager注册PhoneStateListener,重写PhoneStateListener的监听状态方法,就可以实现电话呼叫状态监控和网络连接状态监控。监听电话状态需要向TelephonyManager.listen()方法传递PhoneStateListener.LISTEN_CALL_STATE参数。PhoneStateListener的常量和方法如下表25-2所示:
表25-2 PhoneStateListener的常量和方法
常量和方法名称 |
说明 |
int LISTEN_CALL_FORWARDING_INDICATOR |
监听通话转移指示的变化。 |
int LISTEN_CALL_STATE |
监听设备呼叫状态的变化。 |
int LISTEN_CELL_LOCATION |
监听设备单元位置的变化。 |
int LISTEN_DATA_ACTIVITY |
监听数据流量移动方向的变化。 |
int LISTEN_DATA_CONNECTION_STATE |
监听数据连接状态的变化。 |
int LISTEN_MESSAGE_WAITING_INDICATOR |
监听消息等待指示的变化。 |
int LISTEN_NONE |
停止监听。 |
int LISTEN_SERVICE_STATE |
监听网络服务状态的变化。 |
int LISTEN_SIGNAL_STRENGTH |
监听网络信号强度的变化。 |
void onCallForwardingIndicatorChanged(boolean cfi) |
当通话转移指示发生变化时调用。 |
void onCallStateChanged(int state, String incomingNumber) |
当设备通话状态发生变化时调用。 |
void onCellLocationChanged(CellLocation location) |
当设备单元位置变化时调用。 |
void onDataActivity(int direction) |
当数据流量移动方向变化时调用。 |
void onDataConnectionStateChanged(int state) |
当数据连接状态变化时调用。 |
void onDataConnectionStateChanged(int state, int networkType) |
当数据连接状态变化时调用,需要传入网络类型。 |
void onMessageWaitingIndicatorChanged(boolean mwi) |
当消息等待指示变化时调用。 |
void onServiceStateChanged(ServiceState serviceState) |
当设备服务状态发送改变时调用。 |
void onSignalStrengthsChanged(SignalStrength signalStrength) |
当网络信号强度发生变化时调用。 |
1.2.3.PhoneNumberUtils
PhoneNumberUtils也是电话功能开发中必不可少的类。PhoneNumberUtils类包含了一系列的功能方法,用于简化电话号码字符串的处理。PhoneNumberUtils的常用方法如下所示:
l public static StringformatNumber(Stringsource):可以将输入的电话号码转换为基于本地设置的格式化电话号码。
l public static boolean compare(String a,String b):比较两个号码是否是相同的。
l public static boolean isEmergencyNumber(String number):可以确定给定的电话号码是否为紧急号码。
l public static String convertKeypadLettersToDigits (String input):将任何字母[a-z,A-Z]根据手机键盘字母映射转换为等效的数字。
l public static int getFormatTypeForLocale (Locale locale):返回特定区域的电话号码格式类型。
l public static String getNumberFromIntent (Intent intent, Contextcontext):从Intent中抽取电话号码。
l public static String stripSeparators (String phoneNumber):将“0-9”、“*”、“#”、“+”、“WILD”、“WAIT”、“PAUSE”字符分离出来。
l public static boolean isWellFormedSmsAddress (String address):当且仅当网络部分的地址适合用作短信目的地址时,返回true。
1.2.4.电话功能实例
三弟,现在很神秘,每天都煲电话,貌似是和小姑娘搞对象了。我必须学习下Android怎么打电话,监控电话状态,了解三弟的一举一动。军师告诉我,电话主要使用TelephonyManager和PhoneStateListener,就让我们一起来学习下吧。程序的运行结果如图25-1所示:
图25-1电话应用
该实例定义了一个Button按钮,当点击该按钮时,程序首先判断输入的电话号码是否为空,不为空的话将启动Intent打电话。
新建一个Activity,命名为MainActivity,代码如下所示:
MainActivity.java代码清单25-2-4:
/**
*@author关羽:打骚扰电话,嘿嘿!
*/
public classMainActivity extends Activity{
//显示电话状态文本
private TextView myTextView1;
//电话输入框
private EditText myEditText;
//打电话按钮
private Button myButton;
// 打电话按钮监听
private OnClickListener buttonListener = null;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setListener();
myTextView1= (TextView) findViewById(R.id.myTextView);
myEditText= (EditText) findViewById(R.id.myEditText);
//设置格式化电话号码
myEditText.addTextChangedListener(newPhoneNumberFormattingTextWatcher());
myButton= (Button) findViewById(R.id.myButton);
myButton.setOnClickListener(buttonListener);
//新建监听手机电话状态
exPhoneCallListenermyPhoneCallListener = new exPhoneCallListener();
//电话管理器
TelephonyManagertm = (TelephonyManager) this
.getSystemService(Context.TELEPHONY_SERVICE);
//设置监听电话
tm.listen(myPhoneCallListener,PhoneStateListener.LISTEN_CALL_STATE);
}
/**
* @author关羽设置按钮监听
*/
private void setListener() {
buttonListener = new OnClickListener() {
@Override
public void onClick(View v){
//取得输入的电话号码串
String inputStr = myEditText.getText().toString();
//如果输入不为空且是急救电话,则通过Toast提示
if(inputStr.trim().length()!=0&&
PhoneNumberUtils.isEmergencyNumber(inputStr.trim())){
Toast.makeText(MainActivity.this,
"紧急电话!",Toast.LENGTH_LONG).show();
}
//如果输入不为空创建打电话的Intent
elseif(inputStr.trim().length()!=0) {
IntentphoneIntent =
newIntent("android.intent.action.CALL",Uri.parse("tel:" +inputStr));
//启动
startActivity(phoneIntent);
}
//Toast提示一下输入为空
else{
Toast.makeText(MainActivity.this,
"不能输入为空", Toast.LENGTH_LONG).show();
}
}
};
}
//电话状态监听,通过state判断来电状态
public class exPhoneCallListener extends PhoneStateListener{
public void onCallStateChanged(int state, String incomingNumber){
switch (state){
//电话无任何状态
case TelephonyManager.CALL_STATE_IDLE:
myTextView1.setTextColor(getResources().getColor(R.drawable.red));
myTextView1.setText("我小羽羽要打给谁呢!");
break;
//接起电话,电话正在通话
caseTelephonyManager.CALL_STATE_OFFHOOK:
myTextView1.setTextColor(getResources().getColor(R.drawable.green));
myTextView1.setText("我小羽羽要不打给小飞飞吧!");
break;
//电话进来时
caseTelephonyManager.CALL_STATE_RINGING:
Toast.makeText(MainActivity.this,
"小飞飞打电话给我啦!",Toast.LENGTH_LONG).show();
break;
default:
break;
}
super.onCallStateChanged(state, incomingNumber);
}
}
MainActivity通过设置PhoneStateListener监听手机电话状态,重写了其中的onCallStateChanged()方法,通过传入的“state”判断来电状态。当电话无状态时,TextView显示“我小羽羽要打给谁呢!”;当电话正在通话时,TextView显示“我小羽羽要不打给小飞飞吧!”;当电话进来时,显示Toast“小飞飞打电话给我啦!”。值得注意的是,需要在配置文件中显式的声明两个许可权限,READ_PHONE_STATE:允许读取电话状态访问电话状态;CALL_PHONE:允许程序拨打电话。
1.3. 短信
手机上的短信功能大家都很熟悉。短信简称SMS(short message service),是用户通过智能终端向另一终端直接发送或接收小于160个英文字符的消息。Android系统支持短消息服务,应用程序不仅可以自己管理、收发短信,而且可以追踪和确认短信传递。本节首先详细地介绍Android短信应用开发中常见的类,然后通过一个简单的收发短信实例演示如何使用这些类。
1.3.1.SmsManager
在Android中,SmsManager实现了手机短信的发送功能。SmsManager和其他Manager一样,不能直接通过new实例化,而是通过调用静态方法SmsManager.getDefault()来获得对SmsManager的引用。在获取到SmsManager引用后就可以调用其方法发送短信,如下所示:
l void sendDataMessage(String destinationAddress, String scAddress,short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntentdeliveryIntent):发送一个消息数据到指定的应用程序端口。参数含义依次为:destinationAddress表示消息的目标地址;scAddress表示服务中心的地址,默认为SMSC;destinationPort表示消息的目标端口号;data表示消息的主体,即消息要发送的数据;sentIntent如果不为空,当消息成功发送或失败时发送PendingIntent的广播;deliveryIntent如果不为空,当消息成功传送到接收者时发送PendingIntent广播。
l void sendTextMessage(String destinationAddress, String scAddress,String text, PendingIntent sentIntent, PendingIntent deliveryIntent):发送一个消息的文本。
SmsManager除了可以实现手机发送短信的功能之外,还提供了短信字符串处理的方法,例如将长内容分割为多个短内容。SmsManager的相关常量和其他方法如表25-2所示:
表25-2 SmsManager的相关常量和方法
常量和方法名称 |
说明 |
static int RESULT_ERROR_GENERIC_FAILURE |
表示普通错误。 |
static int RESULT_ERROR_NO_SERVICE |
表示服务当前不可用。 |
static int RESULT_ERROR_NULL_PDU |
表示没有提供协议数据单元。 |
static int RESULT_ERROR_RADIO_OFF |
表示无线广播被关闭。 |
static int STATUS_ON_SIM_FREE |
表示*空间。 |
static int STATUS_ON_SIM_READ |
表示接收且已读。 |
static int STATUS_ON_SIM_SENT |
表示存储且已发送。 |
static int STATUS_ON_SIM_UNREAD |
表示接收但未读。 |
static int STATUS_ON_SIM_UNSENT |
表示存储但未发送。 |
boolean copyMessageToSim(byte[] smsc, byte[] pdu, int status) |
保存短信到SIM卡中。 |
boolean deleteMessageFromSim(int messageIndex) |
删除SIM卡中的消息。 |
ArrayList<SmsMessage> getAllMessagesFromSim() |
获得当前SIM卡中的所有消息。 |
boolean updateMessageOnSim(int messageIndex, int newStatus, byte[] pdu) |
更新SIM卡中的消息。 |
static SmsManager getDefault() |
获得SmsManager的引用。 |
ArrayList<String> divideMessage(String text) |
当短信超过SMS消息的最大长度时,将短信分割为几块。方法返回有序的ArrayList<String>,可以重新组合为初始的消息。 |
void sendMultipartTextMessage(String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) |
发送一个基于短信的多部分文本,调用者应该通过调用divideMessage(String text)将消息分割成正确的大小。 |
1.3.2.SmsMessage
SmsMessage是短信消息帮助类,表示一个短信消息。每个SmsMessage对象包含了短信的细节,包括源地址(手机号)、时间和消息体。SmsMessage类常用的方法如下所示:
l public static SmsMessage createFromPdu(byte[] pdu):传入字节数组,可以将协议数据单元pdu字节数组转化成SmsMessage对象。
l public String getMessageBody():如果SmsMessage存在,将message body作为一个字符串返回。
l public String getOriginatingAddress():获取发送手机号。
l public static int[] calculateLength(CharSequence msgBody, booleanuse7bitOnly):计算消息正文和号码所需要的字符数。
l public static ArrayList<String> fragmentText(String text):将消息分成几个片断。
l public String getServiceCenterAddress():获取短信服务中心的地址。
l public MessageClass getMessageClass():获取消息的类。
l public long getTimestampMillis():获取短信服务中心的时间戳。
1.3.3.短信功能实例
哎呀,最近小飞飞恋爱谈得很好嘛。我关云长还没有对象,怎么能让三弟先搞上对象呢,我的一世英名岂不是尽毁。我要破坏他们俩的感情。幸亏我知道在Android上如何实现发送短信,监控短信发送的状态。主要使用SmsManager和BroadcastReceiver类。程序的运行结果如图25-2所示:
刘备:这三弟、二弟,我都看不下去了,也不知道他们知不知道,只有在真机上才会接收到短信成功到达目标用户的广播,模拟器上无法获取目标用户成功接收到消息发送的Intent。 |
图25-2电话应用
该实例定义了一个Button按钮,当点击按钮时,程序将编辑框中的输入内容发送到对应的号码终端上。
新建一个Activity,命名为MainActivity,代码如下所示:
MainActivity.java代码清单25-3-3:
/**
*@author关羽:好兄弟短信满天飞!
*/
public class MainActivity extends Activity{
//短信到达目标的广播
private DeliverReceiver mDeliverReceiver;
//短信已发送的广播
private SendReceiver mSendReceiver;
private Button send;
private TextView show;
private EditText number, content;
//已发送广播标识
private static String SMS_SEND_ACTIOIN ="SMS_SEND_ACTIOIN";
//短信到达目标用户广播标识
private static String SMS_DELIVERED_ACTION ="SMS_DELIVERED_ACTION";
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
show = (TextView)findViewById(R.id.show);
number = (EditText) findViewById(R.id.number);
content = (EditText) findViewById(R.id.content);
send = (Button) findViewById(R.id.send);
send.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View arg0){
//获取发送目标号码
String strDestAddress =number.getText().toString();
//获取短信内容
String strMessage =content.getText().toString();
//创建SmsManager对象
SmsManager smsManager =SmsManager.getDefault();
try{
//用自定义的Action常数描述Intent
Intent send = newIntent(SMS_SEND_ACTIOIN);
Intent deliver = newIntent(SMS_DELIVERED_ACTION);
//已发送广播信息的PendingIntent参数
PendingIntentmSendPI =
PendingIntent.getBroadcast(getApplicationContext(),0, send, 0);
//成功到达目标用户的PendingIntent参数
PendingIntent mDeliverPI =
PendingIntent.getBroadcast(getApplicationContext(), 0, deliver, 0);
//发送短信方法,设置已发送和成功到达PendingIntent参数
smsManager.sendTextMessage(strDestAddress,null,
strMessage, mSendPI, mDeliverPI);
}catch(Exception e){
show.setText(e.toString());
e.printStackTrace();
}
}
});
}
/**@author关羽:自定义BroadcastReceiver,监控短信发送状态,当短信已发送,进行提示!*/
public class SendReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent){
try{
switch(getResultCode()){
//成功发送
case Activity.RESULT_OK:
show.append("\n"+"已发送短信给你的好友小飞飞了!");
break;
//发送失败
caseSmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(MainActivity.this,
"短信发送失败!", Toast.LENGTH_LONG).show();
break;
//无线连接关闭
caseSmsManager.RESULT_ERROR_RADIO_OFF:
break;
//一个pdu失败
caseSmsManager.RESULT_ERROR_NULL_PDU:
break;
}
}catch(Exception e){
show.setText(e.toString());
e.getStackTrace();
}
}
}
/**@author 关羽:自定义BroadcastReceiver,监控短信达到目标用户!*/
public class DeliverReceiver extendsBroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent){
show.append("\n"+"你的好友小飞飞收到短信啦!");
}
}
@Override
protected void onResume(){
//设置广播过滤器,注册短信已发送的广播
IntentFilter mFilter01;
mFilter01 = new IntentFilter(SMS_SEND_ACTIOIN);
mSendReceiver = new SendReceiver();
registerReceiver(mSendReceiver, mFilter01);
//设置广播过滤器,注册短信成功到达的广播
mFilter01 = new IntentFilter(SMS_DELIVERED_ACTION);
mDeliverReceiver = new DeliverReceiver();
registerReceiver(mDeliverReceiver, mFilter01);
super.onResume();
}
@Override
protected void onPause(){
//注销广播
unregisterReceiver(mSendReceiver);
unregisterReceiver(mDeliverReceiver);
super.onPause();
}
}
MainActivity通过使用SMS Manager中的sendTextManager()方法发送短信。为了追踪发出的SMS消息的传送和到达情况,我们在sendTextManager()方法中传递了mSendPI和mDeliverPI的PendingIntent。当系统已发送短信和目标用户接收到SMS消息时,将会发送广播,注册的监听短信消息传递的状态的接收器SendReceiver和DeliverReceiver就会接收到广播。如图25-2所示,当消息已发送时,TextView显示“已发送短信给你的好友小飞飞了!”;当目标用户收到消息时,TextView显示“你的好友小飞飞收到短信啦!”。值得注意的是,需要在配置文件中显式地声明可发送短信权限SEND_SMS。
1.4. 邮件
目前全球商务最主要的沟通方式就是邮件。智能手机除了支持基本的电话、短信功能之外,还支持邮件的收发。Android系统可以通过GPRS、3G网络、Wifi等方式连接网络,收发邮件。在Android中编写关于邮件的应用程序并不复杂,主要通过Intent调用系统的Gmail程序,如下代码所示:
Intentintent=new Intent(Intent.ACTION_SEND);
String[]tos={"zhangfei@gmail.com"};
String[]ccs={"liubei@gmail.com"};
intent.putExtra(Intent.EXTRA_EMAIL,tos);
intent.putExtra(Intent.EXTRA_CC,ccs);
intent.putExtra(Intent.EXTRA_TEXT,"这是内容");
intent.putExtra(Intent.EXTRA_SUBJECT,"这是标题");
intent.setType("message/rfc822");
startActivity(Intent.createChooser(intent,"发送"));
上述代码首先构造Intent,然后在该Intent中使用putExtra()方法放置Email的字段,使用 setType()方法设置 Email的格式,最后启动Activity。
Android提供了三种类型的Intent用于调用Email:
l Intent.ACTION_SENDTO无附件发送邮件。
l Intent.ACTION_SEND带附件发送邮件。
l Intent.ACTION_SEND_MULTIPLE带多附件发送邮件。
通过Intent可传递字段主要有:
l Intent.EXTRA_EMAIL存放邮件地址的字符串数组。
l Intent.EXTRA_SUBJECT邮件的主题。
l Intent.EXTRA_TEXT邮件的内容。
l Intent.EXTRA_BCC存放邮件密送人地址的字符串数组。
l Intent.EXTRA_CC:存放邮件抄送人地址的字符串数组。
l Intent.EXTRA_STREAM附件的存放路径。
刘备:发送多个附件时,首先需要通过使用SEND_MULTIPLE来创建Intent,然后将多个附件的Uri地址组成一个ArrayList<URI>数组,最后调用Intent.putParcelableArrayListExtra(ArrayList<URI> list)方法实现多个附件的发送。 |
1.5. 联系人
不论是发短信、拨打电话,还是发邮件,都需要填写接收人信息。这些接收人信息都存储在系统的联系人中。每个联系人都拥有自己的一组数据元素,包括电话号码、姓名、通信地址和电子邮箱地址等。下面将讲解如何在应用程序中调用联系人信息。
1.5.1.账户
对于Android系统来说,当拥有了Google的电子邮箱地址,就拥有了Google的账户,可以使用此账户访问Google的各种服务。
新建账户是为了将用户的联系人和账户联系起来。手机设备可以有多个账户,每个账户都、拥有自己的联系人组。、账户可以用于导入导出、管理联系人信息。当手机上没有账户时,也可以添加联系人信息。当应用程序希望添加联系人时,就需要知道设备上存在哪些账户,将对应的联系人添加到对应的账户中。下面代码显示了如何将设备上所有的账户都显示出来:
public void getAllAccounts(Context mContext){
//获取到帐号管理类
AccountManageram=AccountManager.get(mContext);
//得到所有帐号
Account[]accounts=am.getAccounts();
for(Accountac:accounts){
Stringacname =ac.name;
Stringactype=ac.type;
}
}
值得注意的是,当调用帐号管理类时,需要在配置文件中显式地声明可读取帐号权限GET_ACCOUNTS。
1.5.2.联系人介绍
Android中的联系人数据是存储在一个数据库中,通过ContentProvider公开。系统联系人数据库存放的位置为“data\data\com.android.providers.contacts\databases\”,数据库名为contacts2.db,如图25-3所示:
图25-3联系人数据库位置
contacts2.db包含了三个重要的表:data表、raw_contacts表和contacts表。data表中每行存储一位联系人的信息,例如电话、姓名、邮箱等。data表的结构如图25-4所示:
图25-4 data表结构图
raw_contacts表中每一行对应一个联系人的信息,例如联系人所属账户、最近联系时间等。raw_contacts表的结构如图25-5所示:
图25-5 contacts表结构图
contacts表中保存的是当前存在的联系人信息,即联系人中显示的是此表中的条目。contacts表的结构如图25-6所示:
图25-6 contacts表结构图
联系人的主要API入口为ContactsContract。Android中通过ContactsContract类对联系人信息进行操作。ContactsContract定义了一个与联系人信息相关的可扩展数据库。联系人信息存放在一个三层数据模型中:ContactsContract.Data、ContactsContract.RawContacts和ContactsContract.Contacts,分别对应上述数据库的表。
1.5.3.联系人实例
我要和小飞飞,建立紧密的联系,幸亏军师教了我一个简单的实例,演示如何获取手机里面的联系人信息拨打电话、发送短信和邮件的功能,我要把这个功能用来和小飞飞建立紧密的关系。程序的运行结果如图25-7所示:
刘备:幸亏我早就娶妻了,才避免这样一场灾难,也不知道二弟知不知道需要在配置文件中显式地声明可拨打电话和发送短信权限噢!只有在内置Gmail Client的模拟器或真机上才会启动发邮件的组件噢。 |
图25-7联系人应用
该实例定义了三个Button按钮,当点击打电话按钮时,程序将拨打电话;当点击发短信按钮时,程序将发短信;当点击发邮件按钮时,程序将发送邮件。
新建一个Activity,命名为MainActivity,代码如下所示:
MainActivity.java代码清单25-5-3:
/**
*@author关羽:找联系人张飞联系联系!
*/
public class MainActivity extends Activity{
//显示联系人信息
private TextView myTextView =null;
//打电话
private Button callButton=null;
//打电话按钮监听
private OnClickListener callButtonListener = null;
//发邮件
private Button emailButton=null;
//发邮件按钮监听
private OnClickListener emailButtonListener = null;
//发短信
private Button smsButton=null;
//发短信监听
private OnClickListener smsButtonListener = null;
//查找的联系人id
public static final int ContactsID =1;
//联系人辅助类
private ContactInfo mContactInfo;
@Override
public void onCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setListener();
myTextView=(TextView)findViewById(R.id.myTextView);
callButton=(Button)findViewById(R.id.callButton);
callButton.setOnClickListener(callButtonListener);
emailButton=(Button)findViewById(R.id.emailButton);
emailButton.setOnClickListener(emailButtonListener);
smsButton=(Button)findViewById(R.id.smsButton);
smsButton.setOnClickListener(smsButtonListener);
//获取id=1的联系人信息
mContactInfo = getContact();
myTextView.append("Id为"+ContactsID+"的联系人信息如下:"+"\n");
myTextView.append("姓名:"+mContactInfo.getContactName()+"\n");
myTextView.append("电话数组:"+Arrays.toString(mContactInfo.getContactPhone().toArray())+"\n");
myTextView.append("电话Email:"+Arrays.toString(mContactInfo.getEmail().toArray())+"\n");
}
/**
* @author关羽:嘿咻咻,小飞飞会把按钮监听都放在一个方法里,我也会!
*/
private void setListener() {
callButtonListener = new OnClickListener() {
@Override
public void onClick(View v) {
//启动打电话组件
Intent intent = newIntent(Intent.ACTION_CALL,Uri.parse("tel:"+mContactInfo.getContactPhone().get(0)));
startActivity(intent);
}
};
emailButtonListener = new OnClickListener() {
@Override
public voidonClick(View v) {
//启动发邮件组件
Intent intent=new Intent(Intent.ACTION_SEND);
String[] tos={mContactInfo.getEmail().get(0)};
String[] ccs={"guanyu@gmail.com"};
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_TEXT, "这是内容:骚扰你");
intent.putExtra(Intent.EXTRA_SUBJECT, "这是标题:小羽羽爱小飞飞");
intent.setType("message/rfc822");
startActivity(Intent.createChooser(intent,"发邮件中!"));
}
};
smsButtonListener = newOnClickListener() {
@Override
public voidonClick(View v) {
//调用SmsManager发送短信
SmsManagersmsManager = SmsManager.getDefault();
smsManager.sendTextMessage(mContactInfo.getContactPhone().get(0),null, "这是短信:小羽羽爱小飞飞",null, null);
Toast.makeText(MainActivity.this, "正在发送短信!", Toast.LENGTH_LONG).show();
}
};
}
//获取联系人的姓名、电话、邮件信息
public ContactInfogetContact(){
//取得ContentResolver
ContentResolver content = getContentResolver();
//联系人的URI
Uri uri = ContactsContract.Contacts.CONTENT_URI;
//查询联系人
Cursor cursor = content.query(uri, null,
ContactsContract.Contacts._ID +" = "+ContactsID, null, null);
ContactInfo contact = new ContactInfo();
if (cursor.moveToFirst()){
// 循环遍历
for (;!cursor.isAfterLast();cursor.moveToNext()){
List<String> allPhoneNum = null;
List<String> allEmailNum = null;
int idColumn =cursor.getColumnIndex(ContactsContract.Contacts._ID);
int displayNameColumn =
cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
int phoneColumn =
cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
// 获得联系人的ID号
StringcontactId = cursor.getString(idColumn);
contact.setContactId(contactId);
// 获得联系人姓名
String disPlayName =cursor.getString(displayNameColumn);
contact.setContactName(disPlayName);
// 电话号码的个数
int phoneNum = cursor.getInt(phoneColumn);
//电话号码可能有好几个
if (phoneNum > 0){
// 获得联系人的电话号码的cursor;
Cursor phones = content.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID+" = "
+ contactId, null, null);
int phoneCount = phones.getCount();
allPhoneNum = newArrayList<String>(phoneCount);
if (phones.moveToFirst()){
// 遍历所有的电话号码
for(;!phones.isAfterLast();phones.moveToNext()){
String phoneNumber =
phones.getString(phones.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
allPhoneNum.add(phoneNumber);
}
if(!phones.isClosed()){
phones.close();
}
}
}
//设置电话
contact.setContactPhone(allPhoneNum);
allEmailNum = new ArrayList<String>();
Cursor emails = content.query(ContactsContract.CommonDataKinds.
Email.CONTENT_URI,null,ContactsContract.CommonDataKinds.Email.
CONTACT_ID + " = " +contactId,null, null);
//通过ID查询对应联系人email
//email可能有好几个
while (emails.moveToNext()){
String emailAddress =
emails.getString(emails.getColumnIndex(ContactsContract.CommonDataKinds.
Email.DATA));
allEmailNum.add(emailAddress);
}
contact.setEmail(allEmailNum);
}
if (!cursor.isClosed()){
cursor.close();
}
return contact;
}
//联系人辅助类
class ContactInfo{
private String contactId;
private String contactName;
private List<String>contactPhone;
private List<String>emailPhone;
public String getContactId(){
return contactId;
}
public String getContactName(){
return contactName;
}
public void setContactId(String id){
contactId = id;
}
public void setContactName(String name){
contactName = name;
}
public List<String> getContactPhone(){
return contactPhone;
}
public void setContactPhone(List<String>phone){
contactPhone = phone;
}
public List<String> getEmail(){
return emailPhone;
}
public void setEmail(List<String> email){
emailPhone = email;
}
}
}
Android的四大组件包含了Content Provider。该MainActivity使用getContentResolver()方法获取到ContentResolver,用于查询联系人的信息。MainActivity程序首先创建一个ContentResolver实例,然后通过ContentResolver查询联系人数据库,返回一个包含联系人表的Cursor,接着检查联系人表是否包含数据,如果有则循环遍历,获取联系人的相关信息。在获取到联系人信息后,MainActivity通过Intent启动打电话、发邮件组件,最后调用SmsManager.sendTextManager()方法实现了向联系人拨打电话、发送邮件和发送短信的功能。为了方便操作联系人信息,MainActivity中自定义了一个联系人辅助类ContactInfo的内部类,用于查询联系人的id、姓名、电话和邮件信息。
1.6. 玄德有话说
张飞:接电话的时候怎么替换掉系统自带的界面,用自己写的界面?
关羽:在配置文件里注册Broadcast Receivers监听来电和呼出。然后在BroadcastReceiver的onReceive()方法里用PhoneStateListener监听电话的状态,当来电或者呼出时,调用自己编写的Activity,从而可以实现接电话的时跳转到自己写的界面。
张飞:侦听到SMS Receiver后,如何将该条短信删除?
关羽:需要定义一个ContentObserver(内容观察者)来侦听短信箱的provider。当有SMS到来时,通过onChange(boolean arg0)方法可以获得回调方法,再遍历收件箱中是否有需要删除的SMS,找到其id,并将其删除。同样,使用ContentObserver也可以监听电话、
刘备:你们两个搞什么,都不带我!