mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-18 23:05:26 +02:00
fix: Fix legacy storage support
This commit is contained in:
parent
ef6487903e
commit
4681a1d924
@ -4,9 +4,12 @@ import android.content.Context
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import androidx.core.net.toFile
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles
|
import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles
|
||||||
|
import app.myzel394.alibi.helpers.VideoBatchesFolder.Companion.MEDIA_SUBFOLDER
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
|
import java.io.File
|
||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
@ -23,7 +26,11 @@ class AudioBatchesFolder(
|
|||||||
) {
|
) {
|
||||||
override val concatenationFunction = ::concatenateVideoFiles
|
override val concatenationFunction = ::concatenateVideoFiles
|
||||||
override val ffmpegParameters = FFMPEG_PARAMETERS
|
override val ffmpegParameters = FFMPEG_PARAMETERS
|
||||||
override val mediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
override val scopedMediaContentUri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
|
||||||
|
override val legacyMediaFolder = File(
|
||||||
|
scopedMediaContentUri.toFile(),
|
||||||
|
MEDIA_SUBFOLDER + "/" + subfolderName
|
||||||
|
)
|
||||||
|
|
||||||
private var customFileFileDescriptor: ParcelFileDescriptor? = null
|
private var customFileFileDescriptor: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
|
@ -16,8 +16,11 @@ import java.time.LocalDateTime
|
|||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.net.toFile
|
||||||
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
|
||||||
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
||||||
|
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlin.reflect.KFunction3
|
import kotlin.reflect.KFunction3
|
||||||
|
|
||||||
@ -29,7 +32,8 @@ abstract class BatchesFolder(
|
|||||||
) {
|
) {
|
||||||
abstract val concatenationFunction: KFunction3<Iterable<String>, String, String, CompletableDeferred<Unit>>
|
abstract val concatenationFunction: KFunction3<Iterable<String>, String, String, CompletableDeferred<Unit>>
|
||||||
abstract val ffmpegParameters: Array<String>
|
abstract val ffmpegParameters: Array<String>
|
||||||
abstract val mediaContentUri: Uri
|
abstract val scopedMediaContentUri: Uri
|
||||||
|
abstract val legacyMediaFolder: File
|
||||||
|
|
||||||
val mediaPrefix
|
val mediaPrefix
|
||||||
get() = MEDIA_RECORDINGS_PREFIX + subfolderName.substring(1) + "-"
|
get() = MEDIA_RECORDINGS_PREFIX + subfolderName.substring(1) + "-"
|
||||||
@ -45,7 +49,11 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
// Add support for < Android 10
|
// Scoped storage works fine on new Android versions,
|
||||||
|
// we need to manually manage the folder on older versions
|
||||||
|
if (!SUPPORTS_SCOPED_STORAGE) {
|
||||||
|
legacyMediaFolder.mkdirs()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,11 +66,12 @@ abstract class BatchesFolder(
|
|||||||
return customFolder!!.findFile(subfolderName)!!
|
return customFolder!!.findFile(subfolderName)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
protected fun queryMediaContent(
|
protected fun queryMediaContent(
|
||||||
callback: (rawName: String, counter: Int, uri: Uri, cursor: Cursor) -> Any?,
|
callback: (rawName: String, counter: Int, uri: Uri, cursor: Cursor) -> Any?,
|
||||||
) {
|
) {
|
||||||
context.contentResolver.query(
|
context.contentResolver.query(
|
||||||
mediaContentUri,
|
scopedMediaContentUri,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@ -89,7 +98,7 @@ abstract class BatchesFolder(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val uri = Uri.withAppendedPath(mediaContentUri, id)
|
val uri = Uri.withAppendedPath(scopedMediaContentUri, id)
|
||||||
|
|
||||||
val result = callback(rawName, counter, uri, cursor)
|
val result = callback(rawName, counter, uri, cursor)
|
||||||
|
|
||||||
@ -127,13 +136,19 @@ abstract class BatchesFolder(
|
|||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
val filePaths = mutableListOf<String>()
|
val filePaths = mutableListOf<String>()
|
||||||
|
|
||||||
queryMediaContent { _, _, uri, _ ->
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
filePaths.add(
|
queryMediaContent { _, _, uri, _ ->
|
||||||
FFmpegKitConfig.getSafParameterForRead(
|
filePaths.add(
|
||||||
context,
|
FFmpegKitConfig.getSafParameterForRead(
|
||||||
uri,
|
context,
|
||||||
)!!
|
uri,
|
||||||
)
|
)!!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
legacyMediaFolder.listFiles()?.forEach {
|
||||||
|
filePaths.add(it.absolutePath)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filePaths
|
filePaths
|
||||||
@ -175,15 +190,22 @@ abstract class BatchesFolder(
|
|||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
var exists = false
|
var exists = false
|
||||||
|
|
||||||
queryMediaContent { rawName, _, _, _ ->
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
if (rawName == fileName) {
|
queryMediaContent { rawName, _, _, _ ->
|
||||||
exists = true
|
if (rawName == fileName) {
|
||||||
return@queryMediaContent true
|
exists = true
|
||||||
} else {
|
return@queryMediaContent true
|
||||||
|
} else {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
exists
|
return exists
|
||||||
|
} else {
|
||||||
|
return File(
|
||||||
|
legacyMediaFolder,
|
||||||
|
fileName,
|
||||||
|
).exists()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,12 +273,16 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
queryMediaContent { _, _, uri, _ ->
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
context.contentResolver.delete(
|
queryMediaContent { _, _, uri, _ ->
|
||||||
uri,
|
context.contentResolver.delete(
|
||||||
null,
|
uri,
|
||||||
null,
|
null,
|
||||||
)
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
legacyMediaFolder.deleteRecursively()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,12 +298,16 @@ abstract class BatchesFolder(
|
|||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
var hasRecordings = false
|
var hasRecordings = false
|
||||||
|
|
||||||
queryMediaContent { _, _, _, _ ->
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
hasRecordings = true
|
queryMediaContent { _, _, _, _ ->
|
||||||
return@queryMediaContent true
|
hasRecordings = true
|
||||||
}
|
return@queryMediaContent true
|
||||||
|
}
|
||||||
|
|
||||||
hasRecordings
|
return hasRecordings
|
||||||
|
} else {
|
||||||
|
return legacyMediaFolder.listFiles()?.isNotEmpty() ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -301,13 +331,23 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
queryMediaContent { _, counter, uri, _ ->
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
if (counter < earliestCounter) {
|
queryMediaContent { _, counter, uri, _ ->
|
||||||
context.contentResolver.delete(
|
if (counter < earliestCounter) {
|
||||||
uri,
|
context.contentResolver.delete(
|
||||||
null,
|
uri,
|
||||||
null,
|
null,
|
||||||
)
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
legacyMediaFolder.listFiles()?.forEach {
|
||||||
|
val fileCounter = it.nameWithoutExtension.toIntOrNull() ?: return@forEach
|
||||||
|
|
||||||
|
if (fileCounter < earliestCounter) {
|
||||||
|
it.delete()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,6 +367,7 @@ abstract class BatchesFolder(
|
|||||||
return File(getInternalFolder(), "$counter.$fileExtension")
|
return File(getInternalFolder(), "$counter.$fileExtension")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
fun getOrCreateMediaFile(
|
fun getOrCreateMediaFile(
|
||||||
name: String,
|
name: String,
|
||||||
mimeType: String,
|
mimeType: String,
|
||||||
@ -336,7 +377,7 @@ abstract class BatchesFolder(
|
|||||||
var uri: Uri? = null
|
var uri: Uri? = null
|
||||||
|
|
||||||
context.contentResolver.query(
|
context.contentResolver.query(
|
||||||
mediaContentUri,
|
scopedMediaContentUri,
|
||||||
arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME),
|
arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME),
|
||||||
"${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'",
|
"${MediaStore.MediaColumns.DISPLAY_NAME} = '$name'",
|
||||||
null,
|
null,
|
||||||
@ -351,7 +392,7 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
uri = ContentUris.withAppendedId(
|
uri = ContentUris.withAppendedId(
|
||||||
mediaContentUri,
|
scopedMediaContentUri,
|
||||||
cursor.getLong(id)
|
cursor.getLong(id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -360,7 +401,7 @@ abstract class BatchesFolder(
|
|||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
// Create empty output file to be able to write to it
|
// Create empty output file to be able to write to it
|
||||||
uri = context.contentResolver.insert(
|
uri = context.contentResolver.insert(
|
||||||
mediaContentUri,
|
scopedMediaContentUri,
|
||||||
ContentValues().apply {
|
ContentValues().apply {
|
||||||
put(
|
put(
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||||
|
@ -2,6 +2,7 @@ package app.myzel394.alibi.helpers
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
@ -10,6 +11,7 @@ import app.myzel394.alibi.helpers.MediaConverter.Companion.concatenateVideoFiles
|
|||||||
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_INTERNAL_SELECTED_VALUE
|
||||||
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
||||||
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
import com.arthenica.ffmpegkit.FFmpegKitConfig
|
||||||
|
import java.io.File
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
class VideoBatchesFolder(
|
class VideoBatchesFolder(
|
||||||
@ -25,7 +27,11 @@ class VideoBatchesFolder(
|
|||||||
) {
|
) {
|
||||||
override val concatenationFunction = ::concatenateVideoFiles
|
override val concatenationFunction = ::concatenateVideoFiles
|
||||||
override val ffmpegParameters = FFMPEG_PARAMETERS
|
override val ffmpegParameters = FFMPEG_PARAMETERS
|
||||||
override val mediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
override val scopedMediaContentUri: Uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||||
|
override val legacyMediaFolder = File(
|
||||||
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
|
||||||
|
MEDIA_SUBFOLDER
|
||||||
|
)
|
||||||
|
|
||||||
private var customParcelFileDescriptor: ParcelFileDescriptor? = null
|
private var customParcelFileDescriptor: ParcelFileDescriptor? = null
|
||||||
|
|
||||||
@ -46,16 +52,25 @@ class VideoBatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BatchType.MEDIA -> {
|
BatchType.MEDIA -> {
|
||||||
val mediaUri = getOrCreateMediaFile(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
name = getName(date, extension),
|
val mediaUri = getOrCreateMediaFile(
|
||||||
mimeType = "video/$extension",
|
name = getName(date, extension),
|
||||||
relativePath = MEDIA_RELATIVE_PATH,
|
mimeType = "video/$extension",
|
||||||
)
|
relativePath = SCOPED_STORAGE_RELATIVE_PATH,
|
||||||
|
)
|
||||||
|
|
||||||
FFmpegKitConfig.getSafParameterForWrite(
|
return FFmpegKitConfig.getSafParameterForWrite(
|
||||||
context,
|
context,
|
||||||
mediaUri
|
mediaUri
|
||||||
)!!
|
)!!
|
||||||
|
} else {
|
||||||
|
return File(
|
||||||
|
legacyMediaFolder.parentFile!!,
|
||||||
|
getName(date, extension)
|
||||||
|
).apply {
|
||||||
|
createNewFile()
|
||||||
|
}.absolutePath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -105,7 +120,8 @@ class VideoBatchesFolder(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val MEDIA_RELATIVE_PATH = Environment.DIRECTORY_DCIM + "/alibi/video_recordings"
|
val MEDIA_SUBFOLDER = "/alibi/.video_recordings"
|
||||||
|
val SCOPED_STORAGE_RELATIVE_PATH = Environment.DIRECTORY_DCIM + MEDIA_SUBFOLDER
|
||||||
|
|
||||||
// Parameters to be passed in descending order
|
// Parameters to be passed in descending order
|
||||||
// Those parameters first try to concatenate without re-encoding
|
// Those parameters first try to concatenate without re-encoding
|
||||||
|
@ -5,7 +5,6 @@ import android.content.ContentValues
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.ServiceInfo
|
import android.content.pm.ServiceInfo
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.util.Range
|
import android.util.Range
|
||||||
import androidx.camera.core.Camera
|
import androidx.camera.core.Camera
|
||||||
@ -28,7 +27,7 @@ import app.myzel394.alibi.db.RecordingInformation
|
|||||||
import app.myzel394.alibi.enums.RecorderState
|
import app.myzel394.alibi.enums.RecorderState
|
||||||
import app.myzel394.alibi.helpers.BatchesFolder
|
import app.myzel394.alibi.helpers.BatchesFolder
|
||||||
import app.myzel394.alibi.helpers.VideoBatchesFolder
|
import app.myzel394.alibi.helpers.VideoBatchesFolder
|
||||||
import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER
|
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -36,6 +35,7 @@ import kotlinx.coroutines.SupervisorJob
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
import java.io.File
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class VideoRecorderService :
|
class VideoRecorderService :
|
||||||
@ -227,11 +227,14 @@ class VideoRecorderService :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getNameForMediaFile() =
|
||||||
|
"${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}"
|
||||||
|
|
||||||
@SuppressLint("MissingPermission", "NewApi")
|
@SuppressLint("MissingPermission", "NewApi")
|
||||||
private fun prepareVideoRecording() =
|
private fun prepareVideoRecording() =
|
||||||
videoCapture!!.output
|
videoCapture!!.output
|
||||||
.let {
|
.let {
|
||||||
if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) {
|
if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && SUPPORTS_SCOPED_STORAGE) {
|
||||||
it.prepareRecording(
|
it.prepareRecording(
|
||||||
this,
|
this,
|
||||||
FileDescriptorOutputOptions.Builder(
|
FileDescriptorOutputOptions.Builder(
|
||||||
@ -242,34 +245,48 @@ class VideoRecorderService :
|
|||||||
).build()
|
).build()
|
||||||
)
|
)
|
||||||
} else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) {
|
} else if (batchesFolder.type == BatchesFolder.BatchType.MEDIA) {
|
||||||
it.prepareRecording(
|
if (SUPPORTS_SCOPED_STORAGE) {
|
||||||
this,
|
it.prepareRecording(
|
||||||
MediaStoreOutputOptions.Builder(
|
this,
|
||||||
contentResolver,
|
MediaStoreOutputOptions.Builder(
|
||||||
batchesFolder.mediaContentUri,
|
contentResolver,
|
||||||
).setContentValues(
|
batchesFolder.scopedMediaContentUri,
|
||||||
ContentValues().apply {
|
).setContentValues(
|
||||||
val name =
|
ContentValues().apply {
|
||||||
"${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}"
|
val name = getNameForMediaFile()
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
put(
|
||||||
|
MediaStore.Video.Media.IS_PENDING,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
MediaStore.Video.Media.RELATIVE_PATH,
|
||||||
|
VideoBatchesFolder.SCOPED_STORAGE_RELATIVE_PATH,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
put(
|
put(
|
||||||
MediaStore.Video.Media.IS_PENDING,
|
MediaStore.Video.Media.DISPLAY_NAME,
|
||||||
1
|
name
|
||||||
)
|
|
||||||
put(
|
|
||||||
MediaStore.Video.Media.RELATIVE_PATH,
|
|
||||||
VideoBatchesFolder.MEDIA_RELATIVE_PATH,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
).build()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val name = getNameForMediaFile()
|
||||||
|
val file = File(
|
||||||
|
batchesFolder.legacyMediaFolder,
|
||||||
|
name
|
||||||
|
).apply {
|
||||||
|
createNewFile()
|
||||||
|
}
|
||||||
|
|
||||||
put(
|
it.prepareRecording(
|
||||||
MediaStore.Video.Media.DISPLAY_NAME,
|
this,
|
||||||
name
|
FileOutputOptions.Builder(file).build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
).build()
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
it.prepareRecording(
|
it.prepareRecording(
|
||||||
this,
|
this,
|
||||||
|
@ -2,13 +2,14 @@ package app.myzel394.alibi.ui
|
|||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
val BIG_PRIMARY_BUTTON_SIZE = 64.dp
|
val BIG_PRIMARY_BUTTON_SIZE = 64.dp
|
||||||
val MAX_AMPLITUDE = 20000
|
val MAX_AMPLITUDE = 20000
|
||||||
val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||||
val RECORDER_SUBFOLDER_NAME = ".recordings"
|
val RECORDER_SUBFOLDER_NAME = ".recordings"
|
||||||
val VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
||||||
|
// TODO: Fix!
|
||||||
|
val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||||
val MEDIA_RECORDINGS_PREFIX = "alibi-recording-"
|
val MEDIA_RECORDINGS_PREFIX = "alibi-recording-"
|
||||||
val RECORDER_MEDIA_SELECTED_VALUE = "_'media"
|
val RECORDER_MEDIA_SELECTED_VALUE = "_'media"
|
||||||
val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal"
|
val RECORDER_INTERNAL_SELECTED_VALUE = "_'internal"
|
||||||
|
@ -35,12 +35,11 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import app.myzel394.alibi.R
|
import app.myzel394.alibi.R
|
||||||
import app.myzel394.alibi.dataStore
|
import app.myzel394.alibi.dataStore
|
||||||
import app.myzel394.alibi.db.AppSettings
|
import app.myzel394.alibi.db.AppSettings
|
||||||
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
import app.myzel394.alibi.ui.RECORDER_MEDIA_SELECTED_VALUE
|
||||||
import app.myzel394.alibi.ui.VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER
|
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||||
import app.myzel394.alibi.ui.components.atoms.SettingsTile
|
import app.myzel394.alibi.ui.components.atoms.SettingsTile
|
||||||
import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog
|
import app.myzel394.alibi.ui.utils.rememberFolderSelectorDialog
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -219,7 +218,7 @@ fun SaveFolderTile(
|
|||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!VIDEO_RECORDER_SUPPORTS_CUSTOM_FOLDER) {
|
if (!SUPPORTS_SCOPED_STORAGE) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user