21、联系人快捷拨号:使用Intent拨打电话、检查拨号权限、通话记录查询(CallLog)、快速拨号Widget
快捷拨号这个功能,说白了就是让用户少点几下屏幕。我做过好几个联系人相关的项目,每次做到拨号这块,总会有产品经理跑过来说:“能不能让用户一键就拨出去?”嗯,今天我们就来聊聊怎么实现这个“一键拨号”。
21.1 使用Intent拨打电话
Android里拨打电话有两种方式,区别挺大的。我刚开始做的时候也踩过坑,这里给你讲清楚。
方式一:直接拨号(需要权限)
这种方式会直接进入拨号界面,并且号码已经填好。用户只需要点一下拨号键就行。
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + phoneNumber));
startActivity(intent);
方式二:自动拨出(需要权限)
这种方式更狠——直接就把电话拨出去了,用户连确认的机会都没有。
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + phoneNumber));
startActivity(intent);
android.permission.CALL_PHONE 权限。如果你用 ACTION_DIAL,则不需要额外权限。
我个人习惯用 ACTION_DIAL,为什么呢?因为用户体验更好。你想想看,万一用户不小心碰到了拨号按钮,直接打出去多尴尬。我曾经在一个紧急联系人App里用了 ACTION_CALL,结果测试的时候不小心点到了,直接给产品经理打了个电话……场面一度非常尴尬。
21.2 检查拨号权限
Android 6.0 以后,权限管理变得严格了。你不能直接调用 ACTION_CALL,必须先检查权限。
这里我贴一段我常用的权限检查代码:
private void checkCallPermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
// 权限没有被授予,需要申请
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE},
REQUEST_CODE_CALL_PHONE);
} else {
// 权限已经有了,直接拨号
makeCall(phoneNumber);
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode,
permissions, grantResults);
if (requestCode == REQUEST_CODE_CALL_PHONE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
makeCall(phoneNumber);
} else {
// 用户拒绝了权限
Toast.makeText(this, "需要拨号权限才能拨打电话",
Toast.LENGTH_SHORT).show();
}
}
}
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 可以跳转到应用详情页。
21.3 通话记录查询(CallLog)
CallLog 是 Android 系统提供的一个内容提供者,里面存了所有的通话记录。你可以用它来查询最近的通话、未接来电等等。
查询 CallLog 需要 android.permission.READ_CALL_LOG 权限。这个权限在 Android 6.0 以后也是运行时权限,需要动态申请。
来看一个查询最近10条通话记录的示例:
private void queryCallLog() {
Cursor cursor = getContentResolver().query(
CallLog.Calls.CONTENT_URI,
null,
null,
null,
CallLog.Calls.DATE + " DESC"
);
if (cursor != null) {
List<CallLogEntry> callLogs = new ArrayList<>();
int count = 0;
while (cursor.moveToNext() && count < 10) {
CallLogEntry entry = new CallLogEntry();
entry.number = cursor.getString(
cursor.getColumnIndex(CallLog.Calls.NUMBER));
entry.name = cursor.getString(
cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
entry.type = cursor.getInt(
cursor.getColumnIndex(CallLog.Calls.TYPE));
entry.date = cursor.getLong(
cursor.getColumnIndex(CallLog.Calls.DATE));
entry.duration = cursor.getLong(
cursor.getColumnIndex(CallLog.Calls.DURATION));
callLogs.add(entry);
count++;
}
cursor.close();
}
}
通话类型有几种:
| 类型常量 | 值 | 说明 |
|---|---|---|
| CallLog.Calls.INCOMING_TYPE | 1 | 来电 |
| CallLog.Calls.OUTGOING_TYPE | 2 | 去电 |
| CallLog.Calls.MISSED_TYPE | 3 | 未接来电 |
| CallLog.Calls.VOICEMAIL_TYPE | 4 | 语音信箱 |
| CallLog.Calls.REJECTED_TYPE | 5 | 已拒绝 |
21.4 快速拨号Widget
Widget 是 Android 的一大特色。做一个快速拨号的 Widget,用户可以直接在桌面上点一下就打给某个联系人。
创建 Widget 需要三步:
- 定义 Widget 布局 - 在 res/layout 下创建 widget_layout.xml
- 创建 Widget Provider - 继承 AppWidgetProvider
- 注册到 Manifest - 在 AndroidManifest.xml 中声明
先看布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="8dp"
android:background="@drawable/widget_bg">
<TextView
android:id="@+id/contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="张三"
android:textSize="14sp"
android:textColor="#333333"/>
<TextView
android:id="@+id/contact_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="13800138000"
android:textSize="12sp"
android:textColor="#666666"/>
<ImageButton
android:id="@+id/call_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_call"
android:background="@drawable/call_button_bg"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
然后是 Widget Provider:
public class QuickDialWidget extends AppWidgetProvider {
@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
static void updateAppWidget(Context context,
AppWidgetManager appWidgetManager,
int appWidgetId) {
RemoteViews views = new RemoteViews(
context.getPackageName(),
R.layout.widget_layout);
// 设置点击拨号
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:13800138000"));
PendingIntent pendingIntent = PendingIntent.getActivity(
context, 0, callIntent,
PendingIntent.FLAG_UPDATE_CURRENT |
PendingIntent.FLAG_IMMUTABLE);
views.setOnClickPendingIntent(R.id.call_button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
别忘了在 Manifest 里注册:
<receiver android:name=".QuickDialWidget">
<intent-filter>
<action android:name=
"android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
下面我用一张图来总结一下本章的知识结构:
好了,以上就是联系人快捷拨号的完整实现方案。从 Intent 拨号到权限检查,从通话记录查询到桌面 Widget,每一步都有它的坑和技巧。我在实际项目中把这些功能组合起来,做了一个“智能拨号助手”,用户反馈还不错。希望今天的分享对你有帮助。