Android 基础知识6:常用的隐式 Intent

2,423 阅读11分钟

前言

本篇文章的内容其实是属于上一篇文章(Android 基础知识5:Intent 和 Intent 过滤器)的延伸,考虑到篇幅长度的原因,所以没有把本篇文章的内容写到上一篇文章中,另外单独写成一篇文章还有个好处就是方便读者查阅。这篇文章主要为大家列举了常用的隐式 Intent,大家如果在平时工作中有相关的需求可以直接复制代码使用。

本文目录

目录

一、闹钟相关的操作

(一)创建闹钟

下面介绍一下如何创建一个闹钟:

1.属性设置 创建闹钟Intent 2.Intent 示例

  • kotlin版本:
fun createAlarm(message: String, hour: Int, minutes: Int) {
    val intent = Intent(AlarmClock.ACTION_SET_ALARM).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_HOUR, hour)
        putExtra(AlarmClock.EXTRA_MINUTES, minutes)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void createAlarm(String message, int hour, int minutes) {
    Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_HOUR, hour)
            .putExtra(AlarmClock.EXTRA_MINUTES, minutes);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

所需权限

<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />

3.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SET_ALARM" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

运行效果如下: 创建闹钟

(二)创建定时器

下面介绍一下如何创建一个定时器:

1.属性设置 创建定时器Intent 2.Intent 示例

  • kotlin版本:
fun startTimer(message: String, seconds: Int) {
    val intent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void startTimer(String message, int seconds) {
    Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

所需权限

<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />

3.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SET_TIMER" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

运行效果如下

为了方便演示,我把 AlarmClock.EXTRA_SKIP_UI 设置成了 false

创建定时器

(三)显示所有闹钟

调用此 Intent 的应用并不多(使用它的主要是系统应用),但是如果应用的类型是闹钟的话都应实现此 Intent 过滤器。

1.属性设置 显示所有闹钟Intent 2.Intent 示例

  • kotlin版本:
fun allAlarm() {
        val intent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
        if (intent.resolveActivity(packageManager) != null) {
            startActivity(intent)
        }
    }
  • Java版本
public void allAlarm() {
	Intent intent = new Intent(AlarmClock.ACTION_SHOW_ALARMS);
		if (intent.resolveActivity(getPackageManager()) != null) {
			startActivity(intent);
		}
}

3.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SHOW_ALARMS" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

运行效果如下

显示所有闹钟

二、日历

如需向手机的日历添加新事件,请参考如下属性设置:

1.属性设置 日历Intent 2.Intent 示例

  • kotlin版本
fun addEvent(title: String, location: String, begin: Long, end: Long) {
    val intent = Intent(Intent.ACTION_INSERT).apply {
        data = Events.CONTENT_URI
        putExtra(Events.TITLE, title)
        putExtra(Events.EVENT_LOCATION, location)
        putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin)
        putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void addEvent(String title, String location, long begin, long end) {
    Intent intent = new Intent(Intent.ACTION_INSERT)
            .setData(Events.CONTENT_URI)
            .putExtra(Events.TITLE, title)
            .putExtra(Events.EVENT_LOCATION, location)
            .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin)
            .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

3.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.INSERT" />
        <data android:mimeType="vnd.android.cursor.dir/event" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

运行效果: 添加日历事件 调用 addEvent() 方法的测试代码如下:

addEvent("去机场接人", "国际机场", System.currentTimeMillis(), System.currentTimeMillis());

三、相机相关的操作

(一)拍摄照片或视频并将其返回

如需打开相机并接收拍摄的照片或视频,请使用 ACTION_IMAGE_CAPTUREACTION_VIDEO_CAPTURE 操作。此外,还可在 EXTRA_OUTPUT extra 中指定相机将照片或视频保存到的 URI 位置。

1.属性设置 在这里插入图片描述 当相机成功将焦点归还给 Activity(onActivityResult() 回调)时,可以根据 EXTRA_OUTPUT 指定的 URI 访问照片或视频。

2.代码示例

调用相机的方法如果按照Android 官方文档上的做法是没办法实现的,原因是调用相机其实会涉及到权限申请(6.0及其以上的系统),还会涉及到拍照完成后,获取拍照的结果的问题,在 7.0 之前和 7.0 之后的获取方式有很大差异,因此如果按照官方文档的方式写代码,会发现根本调不起来相机。关于权限申请和相机相关的知识,我们后面会单独写文章介绍。本文的目的只是帮助大家介绍一下Intent 的使用,所以如果大家遇到不太明白的代码也不用太深究,重点看一下 Intent 即可。完整代码示例如下,大家重点看下 dispatchTakePictureIntent() 方法里的 Intent 用法即可:

public class MainActivity extends AppCompatActivity {
    private TextView mBtnCreateAlarm;
    private ImageView mIvCapturePhoto;

    private static final int MY_PERMISSIONS_REQUEST = 0;
    private static final int REQUEST_TAKE_PHOTO = 1;

    /**
     * 需要使用但是没被授权的权限保存到此List集合中
     */
    private List<String> permissionNotGrantedList = new ArrayList<>();
    private String currentPhotoPath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtnCreateAlarm = findViewById(R.id.btn_create_alarm);
        mIvCapturePhoto = findViewById(R.id.iv_capture_photo);

        mBtnCreateAlarm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    //Android 6.0 及其以上系统内会动态去申请权限
                    checkPermission();
                } else {
                    dispatchTakePictureIntent();
                }
            }
        });

    }

    /**
     * Android 6.0 动态权限申请
     */
    private void checkPermission() {
        // 判断权限是否已经授予,没有就把该权限添加到列表中
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            permissionNotGrantedList.add(Manifest.permission.CAMERA);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            permissionNotGrantedList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }

        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            permissionNotGrantedList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
        }

        // 如果列表为空,就是全部权限都获取了,不用再次获取了。不为空就去申请权限
        if (!permissionNotGrantedList.isEmpty()) {
            ActivityCompat.requestPermissions(MainActivity.this,
                    permissionNotGrantedList.toArray(new String[permissionNotGrantedList.size()]), MY_PERMISSIONS_REQUEST);
        } else {
            dispatchTakePictureIntent();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST:
                List<String> list = new ArrayList();
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                            list.add(permissions[i]);
                        }
                    }

                    if (list.isEmpty()) {
                        dispatchTakePictureIntent();
                    }
                } else {
                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                }
                break;
            default:
                break;
        }
    }

    /**
     * 构建 Intent
     */
    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException ex) {

            }

            if (photoFile != null) {
                Uri photoURI;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    photoURI = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile);
                } else {
                    photoURI = Uri.fromFile(photoFile);
                }

                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
            }
        }
    }


    private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(
                imageFileName,
                ".jpg",
                storageDir
        );

        // Save a file: path for use with ACTION_VIEW intents
        currentPhotoPath = image.getAbsolutePath();
        return image;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
            Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath);
            mIvCapturePhoto.setImageBitmap(bitmap);
        }
    }
}

运行效果 拍摄照片或视频并将其返回

(二)以静态图像模式启动相机应用

以静态图像模式启动相机应用的意思是直接跳转到相机应用里,拍照的照片直接保存到手机的相册里,不会将结果返回给调用的 Activity。以静态图像模式启动相机应用需要用到的Action是:INTENT_ACTION_STILL_IMAGE_CAMERA

1.Intent 示例:

  • kotlin版本
fun capturePhoto() {
    val intent = Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
    }
}
  • Java版本
public void capturePhoto() {
    Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
    }
}

2.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.media.action.STILL_IMAGE_CAMERA" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

运行效果 以静态图像模式启动相机应用 可以看到拍照完成后,直接就把结果保存到了相册里,不会出现让用户选择重拍或者选取拍照结果的按钮。

(三)以视频模式启动相机应用

以视频模式启动相机应用需要用到的Action是:INTENT_ACTION_VIDEO_CAMERA

1.Intent 示例

  • Kotlin版本
fun capturePhoto() {
    val intent = Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
    }
}
  • Java版本
public void capturePhoto() {
    Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
    }
}

2.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.media.action.VIDEO_CAMERA" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

四、联系人相关操作

(一)选择联系人

如果需要获取某个联系人,请使用 ACTION_PICK 操作,并将 MIME 类型指定为 Contacts.CONTENT_TYPE。选取联系人后返回的结果是一个 content:URI。此操作是利用 Contacts Provider API 授予该该联系人的临时读取权限,因此不需要我们去添加 READ_CONTACTS 权限。

1.属性设置 选择联系人 2.Intent 实例

  • Kotlin版本
const val REQUEST_SELECT_CONTACT = 1

fun selectContact() {
    val intent = Intent(Intent.ACTION_PICK).apply {
        type = ContactsContract.Contacts.CONTENT_TYPE
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_SELECT_CONTACT)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
        val contactUri: Uri = data.data
        // Do something with the selected contact at contactUri
        //...
    }
}
  • Java版本
static final int REQUEST_SELECT_CONTACT = 1;

public void selectContact() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_SELECT_CONTACT);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_CONTACT && resultCode == RESULT_OK) {
        Uri contactUri = data.getData();
        // Do something with the selected contact at contactUri
        ...
    }
}

运行效果 选择联系人 在获得联系人 URI 后如何检索联系人详情的信息在这里不展开,我们会在后面的文章中介绍。

(二)选择联系人的某个特定数据

在上一节中,我们介绍了如何选择和获取某个联系人的 Uri ,如果需要该联系人的其他更具体的信息,我们需要对该 Uri 进行特殊解析,这样做的好处是我们可以根据该 Uri 获取该联系人的所有信息,缺点是需要编写更多的解析代码。但是如果我们的需求只是获取某个联系人的电话号码或者只需要某个联系人的邮箱(比如获取张三这个联系人的电话号码,或者获取张三这个联系人的邮箱)这样的需求就可以使用本节介绍的内容。同样的,本操作也不需要为应用授予 READ_CONTACTS 权限。

1.属性设置 选择联系人的某个特定数据 2.Intent 实例

  • Kotlin版本
const val REQUEST_SELECT_PHONE_NUMBER = 1

fun selectContact() {
    // Start an activity for the user to pick a phone number from contacts
    val intent = Intent(Intent.ACTION_PICK).apply {
        type = CommonDataKinds.Phone.CONTENT_TYPE
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == Activity.RESULT_OK) {
        // Get the URI and query the content provider for the phone number
        val contactUri: Uri = data.data
        val projection: Array<String> = arrayOf(CommonDataKinds.Phone.NUMBER)
        contentResolver.query(contactUri, projection, null, null, null).use { cursor ->
            // If the cursor returned is valid, get the phone number
            if (cursor.moveToFirst()) {
                val numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER)
                val number = cursor.getString(numberIndex)
                // Do something with the phone number
                ...
            }
        }
    }
}
  • Java版本
static final int REQUEST_SELECT_PHONE_NUMBER = 1;

public void selectContact() {
    // Start an activity for the user to pick a phone number from contacts
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(CommonDataKinds.Phone.CONTENT_TYPE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == RESULT_OK) {
        // Get the URI and query the content provider for the phone number
        Uri contactUri = data.getData();
        String[] projection = new String[]{CommonDataKinds.Phone.NUMBER};
        Cursor cursor = getContentResolver().query(contactUri, projection,
                null, null, null);
        // If the cursor returned is valid, get the phone number
        if (cursor != null && cursor.moveToFirst()) {
            int numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER);
            String number = cursor.getString(numberIndex);
            // Do something with the phone number
            //...
        }
    }
}

运行效果: 选择联系人的某个特定数据

(三)查看联系人

我们可以根据联系人的 Uri 查询该联系人的详细信息,同时不需要任何应用权限。

1.属性设置 查看联系人 1.Intent 示例

  • Kotlin版本
fun viewContact(contactUri: Uri) {
    val intent = Intent(Intent.ACTION_VIEW, contactUri)
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void viewContact(Uri contactUri) {
    Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

运行效果: 查看联系人

(四)编辑现有联系人

如需编辑已知联系人,请使用 ACTION_EDIT 操作,使用 content: URI 作为 Intent 数据指定联系人,并将 extra 中由常量指定的任何已知联系人信息包括在 ContactsContract.Intents.Insert 中。同时,使用 ACTION_PICK 返回的联系人 URI 的话不需要任何权限。

1.属性设置 编辑现有联系人 2.Intent 示例

  • Kotlin版本
fun editContact(contactUri: Uri, email: String) {
    val intent = Intent(Intent.ACTION_EDIT).apply {
        data = contactUri
        putExtra(ContactsContract.Intents.Insert.EMAIL, email)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void editContact(Uri contactUri, String email) {
    Intent intent = new Intent(Intent.ACTION_EDIT);
    intent.setData(contactUri);
    intent.putExtra(Intents.Insert.EMAIL, email);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

运行效果: 编辑现有联系人

(五)插入联系人

如需插入新联系人,使用 ACTION_INSERT 操作,将 Contacts.CONTENT_TYPE 指定为 MIME 类型,Extra 里保存联系人信息,联系人的信息的 Key 关键字定义在 ContactsContract.Intents.Insert 中,如ContactsContract.Intents.Insert.NAME

1.属性设置 插入联系人 2.Intent 示例

  • Kotlin版本
fun insertContact(name: String, email: String) {
    val intent = Intent(Intent.ACTION_INSERT).apply {
        type = ContactsContract.Contacts.CONTENT_TYPE
        putExtra(ContactsContract.Intents.Insert.NAME, name)
        putExtra(ContactsContract.Intents.Insert.EMAIL, email)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void insertContact(String name, String email) {
    Intent intent = new Intent(Intent.ACTION_INSERT);
    intent.setType(Contacts.CONTENT_TYPE);
    intent.putExtra(Intents.Insert.NAME, name);
    intent.putExtra(Intents.Insert.EMAIL, email);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

运行效果:

插入联系人

五、电子邮件

本节介绍发送电子邮件:

1.属性设置 发送电子邮件 2.Intent 示例

  • Kotlin版本
fun composeEmail(addresses: Array<String>, subject: String, attachment: Uri) {
    val intent = Intent(Intent.ACTION_SEND).apply {
        type = "*/*"
        putExtra(Intent.EXTRA_EMAIL, addresses)
        putExtra(Intent.EXTRA_SUBJECT, subject)
        putExtra(Intent.EXTRA_STREAM, attachment)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void composeEmail(String[] addresses, String subject, Uri attachment) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("*/*");
    intent.putExtra(Intent.EXTRA_EMAIL, addresses);
    intent.putExtra(Intent.EXTRA_SUBJECT, subject);
    intent.putExtra(Intent.EXTRA_STREAM, attachment);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

如果您想确保 Intent 只由电子邮件应用(而非其他短信或社交应用)进行处理,则需使用 ACTION_SENDTO 操作并加入 "mailto:" 类型的 Data。例如:

  • Kotlin版本
fun composeEmail(addresses: Array<String>, subject: String) {
    val intent = Intent(Intent.ACTION_SENDTO).apply {
        data = Uri.parse("mailto:") // only email apps should handle this
        putExtra(Intent.EXTRA_EMAIL, addresses)
        putExtra(Intent.EXTRA_SUBJECT, subject)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void composeEmail(String[] addresses, String subject) {
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    intent.setData(Uri.parse("mailto:")); // only email apps should handle this
    intent.putExtra(Intent.EXTRA_EMAIL, addresses);
    intent.putExtra(Intent.EXTRA_SUBJECT, subject);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

3.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <data android:type="*/*" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SENDTO" />
        <data android:scheme="mailto" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

运行效果: 发送邮件

六、文件存储

(一)获取指定类型的文件

如果需要从文件管理器中获取文档或者照片可以使用 ACTION_GET_CONTENT 操作并指定所需 MIME 类型。获取的结果是文件的 URI。该 URI 可以是任何类型,http: URI、file: URI 或 content: URI

在 Android 4.3(API 级别 18)及更高版本上,还可以通过为 Intent 添加 EXTRA_ALLOW_MULTIPLE 并将其设置为 true,允许用户选择多个文件。然后就可以在由 getClipData() 返回的 ClipData 对象中访问每一个选定的文件。

1.属性设置 获取指定类型的文件 2.Intent 示例

  • Kotlin版本
const val REQUEST_IMAGE_GET = 1

fun selectImage() {
    val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
        type = "image/*"
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_GET)
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_IMAGE_GET && resultCode == Activity.RESULT_OK) {
        val thumbnail: Bitmap = data.getParcelableExtra("data")
        val fullPhotoUri: Uri = data.data
        // Do work with photo saved at fullPhotoUri
        ...
    }
}
  • Java版本
static final int REQUEST_IMAGE_GET = 1;

public void selectImage() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.setType("image/*");
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_IMAGE_GET);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_GET && resultCode == RESULT_OK) {
        Bitmap thumbnail = data.getParcelable("data");
        Uri fullPhotoUri = data.getData();
        // Do work with photo saved at fullPhotoUri
        ...
    }
}

运行效果: 获取指定类型的文件 当然,也可以支持多选:

static final int REQUEST_IMAGE_GET = 1;

    public void selectImage() {
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        //在 Android 4.3(API 级别 18)及更高版本上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        }

        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, REQUEST_IMAGE_GET);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_IMAGE_GET && resultCode == RESULT_OK) {
            ClipData clipData = data.getClipData();
            if (clipData != null) {
                StringBuilder stringBuilder = new StringBuilder();
                for (int i = 0; i < clipData.getItemCount(); i++) {
                    ClipData.Item itemAt = clipData.getItemAt(i);
                    Uri uri = itemAt.getUri();
                    stringBuilder.append(uri + "\n");
                }
                textView.setText(stringBuilder);
            }
        }
    }

获取指定类型的文件-多选

(二)打开特定类型的文件

在 Android 4.4 或更高版本上运行时,可以使用 ACTION_OPEN_DOCUMENT 来替代 ACTION_GET_CONTENT

1.属性设置 打开指定类型的文件 2.Intent 示例

  • Kotlin版本
const val REQUEST_IMAGE_OPEN = 1

fun selectImage2() {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        type = "image/*"
        addCategory(Intent.CATEGORY_OPENABLE)
    }
    // Only the system receives the ACTION_OPEN_DOCUMENT, so no need to test.
    startActivityForResult(intent, REQUEST_IMAGE_OPEN)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if (requestCode == REQUEST_IMAGE_OPEN && resultCode == Activity.RESULT_OK) {
        val fullPhotoUri: Uri = data.data
        // Do work with full size photo saved at fullPhotoUri
        ...
    }
}
  • Java版本
static final int REQUEST_IMAGE_OPEN = 1;

public void selectImage() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.setType("image/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    // Only the system receives the ACTION_OPEN_DOCUMENT, so no need to test.
    startActivityForResult(intent, REQUEST_IMAGE_OPEN);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_OPEN && resultCode == RESULT_OK) {
        Uri fullPhotoUri = data.getData();
        // Do work with full size photo saved at fullPhotoUri
        ...
    }
}

七、电话

如需打开电话应用并拨打电话号码,请使用 ACTION_DIAL 操作,并使用下文定义的 URI 架构指定电话号码。电话应用打开时会显示电话号码,但用户必需按拨打电话按钮才能开始通话。

如需直接拨打电话,请使用 ACTION_CALL 操作,并使用下文定义的 URI 架构指定电话号码。电话应用打开时便会拨打电话,用户无需按拨打电话按钮。

ACTION_CALL 操作需要您在清单文件中添加 CALL_PHONE 权限:

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

1.属性设置 拨打电话 2.Intent 示例

  • Kotlin版本
fun dialPhoneNumber(phoneNumber: String) {
    val intent = Intent(Intent.ACTION_DIAL).apply {
        data = Uri.parse("tel:$phoneNumber")
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void dialPhoneNumber(String phoneNumber) {
    Intent intent = new Intent(Intent.ACTION_DIAL);
    intent.setData(Uri.parse("tel:" + phoneNumber));
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

运行效果: 拨打电话

八、搜索

(一)使用特定应用搜索

如需支持在自己的应用环境内进行搜索,请使用 SEARCH_ACTION 操作在应用中声明一个 Intent 过滤器,如下文示例 Intent 过滤器中所示:

<activity android:name=".SearchActivity">
    <intent-filter>
        <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

(二)使用网页搜索

如需发起网页搜索,请使用 ACTION_WEB_SEARCH 操作,并在 SearchManager.QUERY extra 中指定搜索字符串。

1.属性设置 执行网页搜索 2.Intent 示例

  • Kotlin版本
fun searchWeb(query: String) {
    val intent = Intent(Intent.ACTION_WEB_SEARCH).apply {
        putExtra(SearchManager.QUERY, query)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void searchWeb(String query) {
    Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
    intent.putExtra(SearchManager.QUERY, query);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

运行效果: 使用网页搜索

九、设置

如需打开某个系统设置页面,请使用下列其中一个 Intent 操作:

  • ACTION_SETTINGS
  • ACTION_WIRELESS_SETTINGS
  • ACTION_AIRPLANE_MODE_SETTINGS
  • ACTION_WIFI_SETTINGS
  • ACTION_APN_SETTINGS
  • ACTION_BLUETOOTH_SETTINGS
  • ACTION_DATE_SETTINGS
  • ACTION_LOCALE_SETTINGS
  • ACTION_INPUT_METHOD_SETTINGS
  • ACTION_DISPLAY_SETTINGS
  • ACTION_SECURITY_SETTINGS
  • ACTION_LOCATION_SOURCE_SETTINGS
  • ACTION_INTERNAL_STORAGE_SETTINGS
  • ACTION_MEMORY_CARD_SETTINGS

Intent 示例

  • Kotlin版本
fun openWifiSettings() {
    val intent = Intent(Settings.ACTION_WIFI_SETTINGS)
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void openWifiSettings() {
    Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

运行效果: 打开设置

十、发送短信

如需发起短信或彩信,请使用以下其中一个 Intent 操作,并使用下列 extra 键指定电话号码、主题和消息正文等消息详情。

1.属性设置 发送短信 2.Intent 示例

  • Kotlin版本
fun composeMmsMessage(message: String, attachment: Uri) {
    val intent = Intent(Intent.ACTION_SENDTO).apply {
        type = HTTP.PLAIN_TEXT_TYPE
        putExtra("sms_body", message)
        putExtra(Intent.EXTRA_STREAM, attachment)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void composeMmsMessage(String message, Uri attachment) {
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    intent.setType(HTTP.PLAIN_TEXT_TYPE);
    intent.putExtra("sms_body", message);
    intent.putExtra(Intent.EXTRA_STREAM, attachment);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

如果您想确保 Intent 只由短信应用(而非其他电子邮件或社交应用)进行处理,则需使用 ACTION_SENDTO 操作并加入 "smsto:" 数据架构。例如:

  • Kotlin版本
fun composeMmsMessage(message: String, attachment: Uri) {
    val intent = Intent(Intent.ACTION_SEND).apply {
        data = Uri.parse("smsto:")  // This ensures only SMS apps respond
        putExtra("sms_body", message)
        putExtra(Intent.EXTRA_STREAM, attachment)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}
  • Java版本
public void composeMmsMessage(String message, Uri attachment) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setData(Uri.parse("smsto:"));  // This ensures only SMS apps respond
    intent.putExtra("sms_body", message);
    intent.putExtra(Intent.EXTRA_STREAM, attachment);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

2.intent-filter 示例

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <data android:type="text/plain" />
        <data android:type="image/*" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

参考文档: 通用 Intent