217 lines
7.5 KiB
Kotlin
217 lines
7.5 KiB
Kotlin
package jp.juggler.subwaytooter.util
|
|
|
|
import android.Manifest
|
|
import android.content.Intent
|
|
import android.os.Build
|
|
import androidx.activity.result.ActivityResultLauncher
|
|
import androidx.activity.result.PickVisualMediaRequest
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import jp.juggler.subwaytooter.R
|
|
import jp.juggler.subwaytooter.pref.PrefDevice
|
|
import jp.juggler.subwaytooter.pref.prefDevice
|
|
import jp.juggler.util.coroutine.launchAndShowError
|
|
import jp.juggler.util.data.UriAndType
|
|
import jp.juggler.util.data.checkMimeTypeAndGrant
|
|
import jp.juggler.util.data.intentGetContent
|
|
import jp.juggler.util.data.notEmpty
|
|
import jp.juggler.util.log.LogCategory
|
|
import jp.juggler.util.log.showError
|
|
import jp.juggler.util.ui.isOk
|
|
|
|
class VisualMediaPickerCompat(
|
|
private val onPicked: suspend (entries: List<UriAndType>?) -> Unit,
|
|
) {
|
|
companion object {
|
|
private val log = LogCategory("VisualMediaPickerCompat")
|
|
}
|
|
|
|
private var activity: AppCompatActivity? = null
|
|
|
|
private var prefDevice: PrefDevice? = null
|
|
|
|
private var pickMedia1: ActivityResultLauncher<PickVisualMediaRequest>? = null
|
|
|
|
private var pickMediaMultiple: ActivityResultLauncher<PickVisualMediaRequest>? = null
|
|
|
|
private var arSafPicker: ActivityResultLauncher<Intent>? = null
|
|
|
|
/**
|
|
* SAFのピッカーを使うか判定するのに使う
|
|
*/
|
|
private val prSafPickerImage = when {
|
|
// 34以降でも権限をチェック
|
|
Build.VERSION.SDK_INT >= 33 -> PermissionSpec(
|
|
permissions = listOf(
|
|
Manifest.permission.READ_MEDIA_IMAGES,
|
|
),
|
|
deniedId = R.string.permission_denied_media_access,
|
|
rationalId = R.string.permission_rational_media_access,
|
|
)
|
|
|
|
else -> PermissionSpec(
|
|
permissions = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
|
deniedId = R.string.permission_denied_media_access,
|
|
rationalId = R.string.permission_rational_media_access,
|
|
)
|
|
}.requester {
|
|
openSafPicker(
|
|
multiple = prefDevice?.mediaPickerMultiple ?: false,
|
|
allowVideo = false,
|
|
)
|
|
}
|
|
|
|
/**
|
|
* SAFのピッカーを使うか判定するのに使う
|
|
*/
|
|
private val prSafPickerImageAndVideo = when {
|
|
// 34以降でも権限をチェック
|
|
Build.VERSION.SDK_INT >= 33 -> PermissionSpec(
|
|
permissions = listOf(
|
|
Manifest.permission.READ_MEDIA_IMAGES,
|
|
Manifest.permission.READ_MEDIA_VIDEO,
|
|
),
|
|
deniedId = R.string.permission_denied_media_access,
|
|
rationalId = R.string.permission_rational_media_access,
|
|
)
|
|
|
|
else -> PermissionSpec(
|
|
permissions = listOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
|
deniedId = R.string.permission_denied_media_access,
|
|
rationalId = R.string.permission_rational_media_access,
|
|
)
|
|
}.requester {
|
|
openSafPicker(
|
|
multiple = prefDevice?.mediaPickerMultiple ?: false,
|
|
allowVideo = true,
|
|
)
|
|
}
|
|
|
|
fun register(activity: AppCompatActivity, multipleLimit: Int = 4) {
|
|
|
|
prefDevice = activity.prefDevice
|
|
|
|
this.activity = activity
|
|
|
|
pickMedia1 = activity.registerForActivityResult(
|
|
ActivityResultContracts.PickVisualMedia(),
|
|
) { uri ->
|
|
activity.launchAndShowError {
|
|
onPicked((uri?.let { listOf(it) })?.checkMimeTypeAndGrant(activity.contentResolver))
|
|
}
|
|
}
|
|
|
|
pickMediaMultiple = activity.registerForActivityResult(
|
|
ActivityResultContracts.PickMultipleVisualMedia(multipleLimit),
|
|
) { uris ->
|
|
activity.launchAndShowError {
|
|
onPicked(uris?.notEmpty()?.checkMimeTypeAndGrant(activity.contentResolver))
|
|
}
|
|
}
|
|
|
|
prSafPickerImage.register(activity)
|
|
prSafPickerImageAndVideo.register(activity)
|
|
|
|
arSafPicker = activity.registerForActivityResult(
|
|
ActivityResultContracts.StartActivityForResult(),
|
|
) { r ->
|
|
activity.launchAndShowError {
|
|
if (r.isOk) {
|
|
onPicked(
|
|
r.data?.checkMimeTypeAndGrant(activity.contentResolver)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun open(
|
|
multiple: Boolean = false,
|
|
allowVideo: Boolean = false,
|
|
) {
|
|
// SAFのピッカーに必要な権限を持っているか?
|
|
val hasSafPermissions = try {
|
|
when {
|
|
allowVideo -> prSafPickerImageAndVideo
|
|
else -> prSafPickerImage
|
|
}.hasPermissions()
|
|
} catch (ex: Throwable) {
|
|
log.e(ex, "can't check media permissions.")
|
|
activity?.showError(ex, "can't check media permissions.")
|
|
return
|
|
}
|
|
|
|
when {
|
|
// API 33まで、またはAPI34以降でも権限があればSAFのピッカーを使う
|
|
Build.VERSION.SDK_INT < 34 || hasSafPermissions -> openSafPicker(
|
|
multiple = multiple,
|
|
allowVideo = allowVideo,
|
|
)
|
|
|
|
// API34以降で権限が不十分ならJetPack Activity の写真選択ツールを使う
|
|
else -> openVisualMediaPicker(
|
|
multiple = multiple,
|
|
allowVideo = allowVideo,
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun openVisualMediaPicker(
|
|
multiple: Boolean,
|
|
allowVideo: Boolean,
|
|
) {
|
|
log.i("openVisualMediaPicker multiple=$multiple, allowVideo=$allowVideo")
|
|
val mediaType = when (allowVideo) {
|
|
true -> ActivityResultContracts.PickVisualMedia.ImageAndVideo
|
|
else -> ActivityResultContracts.PickVisualMedia.ImageOnly
|
|
}
|
|
val pickerLauncher = when {
|
|
multiple -> pickMediaMultiple
|
|
else -> pickMedia1
|
|
}
|
|
pickerLauncher ?: error("openVisualMediaPicker: pickerLauncher is not registered.")
|
|
pickerLauncher.launch(PickVisualMediaRequest(mediaType))
|
|
}
|
|
|
|
private fun openSafPicker(
|
|
multiple: Boolean,
|
|
allowVideo: Boolean,
|
|
) {
|
|
log.i("openSafPicker multiple=$multiple, allowVideo=$allowVideo")
|
|
val activity = this.activity
|
|
?: error("missing activity")
|
|
|
|
prefDevice?.mediaPickerMultiple = multiple
|
|
|
|
val permissionRequester = when {
|
|
allowVideo -> prSafPickerImageAndVideo
|
|
else -> prSafPickerImage
|
|
}
|
|
|
|
if (!permissionRequester.checkOrLaunch()) return
|
|
|
|
// SAFのIntentで開く
|
|
try {
|
|
val captionId = when {
|
|
multiple -> when {
|
|
allowVideo -> R.string.pick_images_or_video
|
|
else -> R.string.pick_images
|
|
}
|
|
|
|
else -> R.string.pick_image
|
|
}
|
|
val intent = intentGetContent(
|
|
allowMultiple = true,
|
|
caption = activity.getString(captionId),
|
|
mimeTypes = when {
|
|
allowVideo -> arrayOf("image/*", "video/*")
|
|
else -> arrayOf("image/*")
|
|
}
|
|
)
|
|
arSafPicker!!.launch(intent)
|
|
} catch (ex: Throwable) {
|
|
activity.showError(ex, "openVisualMediaPicker33 failed.")
|
|
}
|
|
}
|
|
}
|