Android 如何不通过存储权限实现相机拍照

365 阅读1分钟

假如没有存储权限,如何实现相机拍照并存储图片呢?

1. 去除读、写存储权限,保留相机权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

2. AndroidManifest.xml 定义provider

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.oo">
 
    <application
        android:requestLegacyExternalStorage="true">
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
        <!-- Android 11 -->
        <meta-data
            android:name="ScopedStorage"
            android:value="true" />
    </application>

</manifest>

3. 定义file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <root-path
        name="root_path"
        path="." />

    <!--Environment.getExternalStorageDirectory()-->
    <external-path
        name="external_path"
        path="/" />

    <!--context.getExternalCacheDir()-->
    <external-cache-path
        name="external_cache_path"
        path="/" />

    <!--context.getCacheDir()-->
    <cache-path
        name="cache_path"
        path="cache_path"/>

    <!--context.getFilesDir()-->
    <files-path
        name="root"
        path="." />

    <!--context.getExternalFilesDir()-->
    <external-files-path
        name="external_files"
        path="." />

</paths>

4. 具体代码逻辑

4.1 检查相机权限 省略 ...

这个代码没啥特别的,请自己实现。

4.2 定义ActivityResultContract接收对象

open class TakeCameraBackPhoto : ActivityResultContract<Uri, Boolean>() {
    @CallSuper
    override fun createIntent(context: Context, input: Uri): Intent {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        intent.putExtra(MediaStore.EXTRA_OUTPUT, input)
        intent.putExtra("android.intent.extras.LENS_FACING_BACK", 1)
        intent.putExtra("android.intent.extras.CAMERA_FACING", 0)
        intent.putExtra("android.intent.extra.USE_FRONT_CAMERA", false)
        return intent
    }

    final override fun getSynchronousResult(
        context: Context,
        input: Uri
    ): SynchronousResult<Boolean>? = null

    @Suppress("AutoBoxing")
    final override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
        return resultCode == Activity.RESULT_OK
    }
}

4.3 具体实现

class MainActivity : AppCompatActivity() {
    var file: File? = null

    private val takeCameraPhoto = registerForActivityResult(TakeCameraBackPhoto()) {
        if (it) {
            file?.let { file -> 
                // file.absolutePath 即文件的完整路径
            }
            return@registerForActivityResult
        }
    }

    // 调用此方法即可
    private fun openCamera(context:Context) {
        file = File(
            context.externalCacheDir, "${System.currentTimeMillis()}.jpg"
        )

        val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file.apply {
            parentFile?.mkdirs()
            createNewFile()
        })

        takeCameraPhoto.launch(uri)
    }
}