mirror of
https://github.com/Myzel394/Alibi.git
synced 2025-06-19 07:15:25 +02:00
fix: Fix recording for Android API 30
This commit is contained in:
parent
0372b44901
commit
08a6d557f8
@ -27,6 +27,8 @@
|
|||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
|
|
||||||
<!-- Todo: Check android permissions -->
|
<!-- Todo: Check android permissions -->
|
||||||
|
<!-- Starting with Android 29, apps don't need to request the READ_EXTERNAL_STORAGE permission
|
||||||
|
for files in their own MediaStore -->
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="28" />
|
android:maxSdkVersion="28" />
|
||||||
|
@ -64,7 +64,7 @@ class AudioBatchesFolder(
|
|||||||
val mediaUri = getOrCreateMediaFile(
|
val mediaUri = getOrCreateMediaFile(
|
||||||
name = getName(date, extension),
|
name = getName(date, extension),
|
||||||
mimeType = "audio/$extension",
|
mimeType = "audio/$extension",
|
||||||
relativePath = Environment.DIRECTORY_DCIM + "/" + MEDIA_SUBFOLDER_NAME,
|
relativePath = BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_SUBFOLDER_NAME,
|
||||||
)
|
)
|
||||||
|
|
||||||
return FFmpegKitConfig.getSafParameterForWrite(
|
return FFmpegKitConfig.getSafParameterForWrite(
|
||||||
@ -146,8 +146,13 @@ class AudioBatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/audio_recordings"
|
val MEDIA_RECORDINGS_SUBFOLDER = MEDIA_SUBFOLDER_NAME + "/audio_recordings"
|
||||||
|
val BASE_SCOPED_STORAGE_RELATIVE_PATH =
|
||||||
|
(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||||
|
Environment.DIRECTORY_RECORDINGS
|
||||||
|
else
|
||||||
|
Environment.DIRECTORY_PODCASTS)
|
||||||
val SCOPED_STORAGE_RELATIVE_PATH =
|
val SCOPED_STORAGE_RELATIVE_PATH =
|
||||||
Environment.DIRECTORY_DCIM + "/" + MEDIA_RECORDINGS_SUBFOLDER
|
BASE_SCOPED_STORAGE_RELATIVE_PATH + "/" + MEDIA_RECORDINGS_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
|
||||||
|
@ -250,11 +250,13 @@ abstract class BatchesFolder(
|
|||||||
suspend fun concatenate(
|
suspend fun concatenate(
|
||||||
recordingStart: LocalDateTime,
|
recordingStart: LocalDateTime,
|
||||||
extension: String,
|
extension: String,
|
||||||
disableCache: Boolean = false,
|
disableCache: Boolean? = null,
|
||||||
onNextParameterTry: (String) -> Unit = {},
|
onNextParameterTry: (String) -> Unit = {},
|
||||||
durationPerBatchInMilliseconds: Long = 0,
|
durationPerBatchInMilliseconds: Long = 0,
|
||||||
onProgress: (Float?) -> Unit = {},
|
onProgress: (Float?) -> Unit = {},
|
||||||
): String {
|
): String {
|
||||||
|
val disableCache = disableCache ?: (type != BatchType.INTERNAL)
|
||||||
|
|
||||||
if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) {
|
if (!disableCache && checkIfOutputAlreadyExists(recordingStart, extension)) {
|
||||||
return getOutputFileForFFmpeg(
|
return getOutputFileForFFmpeg(
|
||||||
date = recordingStart,
|
date = recordingStart,
|
||||||
@ -462,7 +464,7 @@ abstract class BatchesFolder(
|
|||||||
context.contentResolver.query(
|
context.contentResolver.query(
|
||||||
scopedMediaContentUri,
|
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' AND ${Media.RELATIVE_PATH} = '$relativePath'",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
)!!.use { cursor ->
|
)!!.use { cursor ->
|
||||||
@ -482,6 +484,7 @@ abstract class BatchesFolder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
|
try {
|
||||||
// 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(
|
||||||
scopedMediaContentUri,
|
scopedMediaContentUri,
|
||||||
@ -495,14 +498,15 @@ abstract class BatchesFolder(
|
|||||||
mimeType
|
mimeType
|
||||||
)
|
)
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
put(
|
put(
|
||||||
Media.RELATIVE_PATH,
|
Media.RELATIVE_PATH,
|
||||||
relativePath,
|
relativePath,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)!!
|
)!!
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Media", "Failed to create file", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uri!!
|
return uri!!
|
||||||
|
@ -7,6 +7,7 @@ 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
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
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.ui.MEDIA_SUBFOLDER_NAME
|
import app.myzel394.alibi.ui.MEDIA_SUBFOLDER_NAME
|
||||||
@ -108,8 +109,8 @@ class VideoBatchesFolder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
fun asMediaGetScopedStorageContentValues(name: String) = ContentValues().apply {
|
fun asMediaGetScopedStorageContentValues(name: String) = ContentValues().apply {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
||||||
put(
|
put(
|
||||||
MediaStore.Video.Media.IS_PENDING,
|
MediaStore.Video.Media.IS_PENDING,
|
||||||
1
|
1
|
||||||
@ -118,7 +119,6 @@ class VideoBatchesFolder(
|
|||||||
MediaStore.Video.Media.RELATIVE_PATH,
|
MediaStore.Video.Media.RELATIVE_PATH,
|
||||||
SCOPED_STORAGE_RELATIVE_PATH,
|
SCOPED_STORAGE_RELATIVE_PATH,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
put(
|
put(
|
||||||
MediaStore.Video.Media.DISPLAY_NAME,
|
MediaStore.Video.Media.DISPLAY_NAME,
|
||||||
|
@ -162,7 +162,7 @@ class AudioRecorderService :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getNameForMediaFile() =
|
private fun getNameForMediaFile() =
|
||||||
"${batchesFolder.mediaPrefix}$counter.${settings.videoRecorderSettings.fileExtension}"
|
"${batchesFolder.mediaPrefix}$counter.${settings.audioRecorderSettings.fileExtension}"
|
||||||
|
|
||||||
// ==== Actual recording related ====
|
// ==== Actual recording related ====
|
||||||
private fun createRecorder(): MediaRecorder {
|
private fun createRecorder(): MediaRecorder {
|
||||||
|
@ -25,6 +25,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.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS
|
||||||
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -231,7 +232,7 @@ class VideoRecorderService :
|
|||||||
private fun prepareVideoRecording() =
|
private fun prepareVideoRecording() =
|
||||||
videoCapture!!.output
|
videoCapture!!.output
|
||||||
.let {
|
.let {
|
||||||
if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && SUPPORTS_SCOPED_STORAGE) {
|
if (batchesFolder.type == BatchesFolder.BatchType.CUSTOM && SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS) {
|
||||||
it.prepareRecording(
|
it.prepareRecording(
|
||||||
this,
|
this,
|
||||||
FileDescriptorOutputOptions.Builder(
|
FileDescriptorOutputOptions.Builder(
|
||||||
|
@ -11,11 +11,14 @@ val SUPPORTS_DARK_MODE_NATIVELY = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|||||||
|
|
||||||
val MEDIA_SUBFOLDER_NAME = "alibi"
|
val MEDIA_SUBFOLDER_NAME = "alibi"
|
||||||
|
|
||||||
val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
val SUPPORTS_SCOPED_STORAGE = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||||
|
val SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS = 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"
|
||||||
|
|
||||||
|
// TODO: Check API 24
|
||||||
|
|
||||||
// You are not allowed to change the constants below.
|
// You are not allowed to change the constants below.
|
||||||
// If you do so, you will be blocked on GitHub.
|
// If you do so, you will be blocked on GitHub.
|
||||||
const val REPO_URL = "https://github.com/Myzel394/Alibi"
|
const val REPO_URL = "https://github.com/Myzel394/Alibi"
|
||||||
|
@ -55,6 +55,7 @@ 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.SHEET_BOTTOM_OFFSET
|
import app.myzel394.alibi.ui.SHEET_BOTTOM_OFFSET
|
||||||
|
import app.myzel394.alibi.ui.SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS
|
||||||
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
import app.myzel394.alibi.ui.SUPPORTS_SCOPED_STORAGE
|
||||||
import app.myzel394.alibi.ui.components.atoms.MessageBox
|
import app.myzel394.alibi.ui.components.atoms.MessageBox
|
||||||
import app.myzel394.alibi.ui.components.atoms.MessageType
|
import app.myzel394.alibi.ui.components.atoms.MessageType
|
||||||
@ -256,7 +257,7 @@ fun SelectionSheet(
|
|||||||
SUPPORTS_SCOPED_STORAGE ||
|
SUPPORTS_SCOPED_STORAGE ||
|
||||||
PermissionHelper.hasGranted(
|
PermissionHelper.hasGranted(
|
||||||
context,
|
context,
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
updateValue(RECORDER_MEDIA_SELECTED_VALUE)
|
updateValue(RECORDER_MEDIA_SELECTED_VALUE)
|
||||||
@ -276,7 +277,7 @@ fun SelectionSheet(
|
|||||||
showCustomFolderWarning = true
|
showCustomFolderWarning = true
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if (!SUPPORTS_SCOPED_STORAGE) {
|
if (!SUPPORTS_SAVING_VIDEOS_IN_CUSTOM_FOLDERS) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp),
|
modifier = Modifier.padding(horizontal = 32.dp, vertical = 12.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user