From 8e806faecda20d9e7136c8d9f71df257523fbc9e Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 13 Jan 2021 11:42:21 +0100 Subject: [PATCH] Fixed crash with media store on Android < 29 --- .../call/fragments/ControlsFragment.kt | 15 ++++++-- .../call/viewmodels/CallsViewModel.kt | 11 +++++- .../compatibility/Api21Compatibility.kt | 34 ++++++++++++++----- .../compatibility/Api29Compatibility.kt | 18 +++++----- 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt b/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt index 8d56d8068..aa2ed20d5 100644 --- a/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt +++ b/app/src/main/java/org/linphone/activities/call/fragments/ControlsFragment.kt @@ -88,6 +88,15 @@ class ControlsFragment : GenericFragment() { } }) + callsViewModel.askWriteExternalStoragePermissionEvent.observe(viewLifecycleOwner, { + it.consume { + if (!PermissionHelper.get().hasWriteExternalStorage()) { + Log.i("[Controls Fragment] Asking for WRITE_EXTERNAL_STORAGE permission") + requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1) + } + } + }) + callsViewModel.callUpdateEvent.observe(viewLifecycleOwner, { it.consume { call -> if (call.state == Call.State.StreamsRunning) { @@ -196,6 +205,8 @@ class ControlsFragment : GenericFragment() { } } } + } else if (requestCode == 1 && grantResults[0] == PERMISSION_GRANTED) { + callsViewModel.takeScreenshot() } super.onRequestPermissionsResult(requestCode, permissions, grantResults) } @@ -206,12 +217,12 @@ class ControlsFragment : GenericFragment() { if (!PermissionHelper.get().hasRecordAudioPermission()) { Log.i("[Controls Fragment] Asking for RECORD_AUDIO permission") - permissionsRequiredList.add(android.Manifest.permission.RECORD_AUDIO) + permissionsRequiredList.add(Manifest.permission.RECORD_AUDIO) } if (coreContext.isVideoCallOrConferenceActive() && !PermissionHelper.get().hasCameraPermission()) { Log.i("[Controls Fragment] Asking for CAMERA permission") - permissionsRequiredList.add(android.Manifest.permission.CAMERA) + permissionsRequiredList.add(Manifest.permission.CAMERA) } if (permissionsRequiredList.isNotEmpty()) { diff --git a/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt b/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt index 625ce903f..a0bb20e1d 100644 --- a/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/call/viewmodels/CallsViewModel.kt @@ -26,6 +26,7 @@ import org.linphone.core.Call import org.linphone.core.Core import org.linphone.core.CoreListenerStub import org.linphone.utils.Event +import org.linphone.utils.PermissionHelper class CallsViewModel : ViewModel() { val currentCallViewModel = MutableLiveData() @@ -46,6 +47,10 @@ class CallsViewModel : ViewModel() { MutableLiveData>() } + val askWriteExternalStoragePermissionEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + private val listener = object : CoreListenerStub() { override fun onCallStateChanged( core: Core, @@ -147,7 +152,11 @@ class CallsViewModel : ViewModel() { } fun takeScreenshot() { - currentCallViewModel.value?.takeScreenshot() + if (!PermissionHelper.get().hasWriteExternalStorage()) { + askWriteExternalStoragePermissionEvent.value = Event(true) + } else { + currentCallViewModel.value?.takeScreenshot() + } } private fun addCallToPausedList(call: Call) { diff --git a/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt index f0992d8a2..dbb7c6fd9 100644 --- a/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt @@ -35,6 +35,7 @@ import org.linphone.core.Content import org.linphone.core.tools.Log import org.linphone.utils.AppUtils import org.linphone.utils.FileUtils +import org.linphone.utils.PermissionHelper @Suppress("DEPRECATION") @TargetApi(21) @@ -65,9 +66,14 @@ class Api21Compatibility { } suspend fun addImageToMediaStore(context: Context, content: Content): Boolean { + if (!PermissionHelper.get().hasWriteExternalStorage()) { + Log.e("[Media Store] Write external storage permission denied") + return false + } + val filePath = content.filePath if (filePath == null) { - Log.e("[Chat Message] Content doesn't have a file path!") + Log.e("[Media Store] Content doesn't have a file path!") return false } @@ -75,7 +81,7 @@ class Api21Compatibility { val relativePath = "${Environment.DIRECTORY_PICTURES}/$appName" val fileName = content.name val mime = "${content.type}/${content.subtype}" - Log.i("[Chat Message] Adding image $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") + Log.i("[Media Store] Adding image $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") val values = ContentValues().apply { put(MediaStore.Images.Media.DISPLAY_NAME, fileName) @@ -85,7 +91,7 @@ class Api21Compatibility { val fileUri = context.contentResolver.insert(collection, values) if (fileUri == null) { - Log.e("[Chat Message] Failed to get a URI to where store the file, aborting") + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") return false } @@ -101,9 +107,14 @@ class Api21Compatibility { } suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean { + if (!PermissionHelper.get().hasWriteExternalStorage()) { + Log.e("[Media Store] Write external storage permission denied") + return false + } + val filePath = content.filePath if (filePath == null) { - Log.e("[Chat Message] Content doesn't have a file path!") + Log.e("[Media Store] Content doesn't have a file path!") return false } @@ -111,7 +122,7 @@ class Api21Compatibility { val relativePath = "${Environment.DIRECTORY_MOVIES}/$appName" val fileName = content.name val mime = "${content.type}/${content.subtype}" - Log.i("[Chat Message] Adding video $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") + Log.i("[Media Store] Adding video $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") val values = ContentValues().apply { put(MediaStore.Video.Media.TITLE, fileName) @@ -122,7 +133,7 @@ class Api21Compatibility { val fileUri = context.contentResolver.insert(collection, values) if (fileUri == null) { - Log.e("[Chat Message] Failed to get a URI to where store the file, aborting") + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") return false } @@ -138,9 +149,14 @@ class Api21Compatibility { } suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean { + if (!PermissionHelper.get().hasWriteExternalStorage()) { + Log.e("[Media Store] Write external storage permission denied") + return false + } + val filePath = content.filePath if (filePath == null) { - Log.e("[Chat Message] Content doesn't have a file path!") + Log.e("[Media Store] Content doesn't have a file path!") return false } @@ -148,7 +164,7 @@ class Api21Compatibility { val relativePath = "${Environment.DIRECTORY_MUSIC}/$appName" val fileName = content.name val mime = "${content.type}/${content.subtype}" - Log.i("[Chat Message] Adding audio $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") + Log.i("[Media Store] Adding audio $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") val values = ContentValues().apply { put(MediaStore.Audio.Media.TITLE, fileName) @@ -159,7 +175,7 @@ class Api21Compatibility { val fileUri = context.contentResolver.insert(collection, values) if (fileUri == null) { - Log.e("[Chat Message] Failed to get a URI to where store the file, aborting") + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") return false } diff --git a/app/src/main/java/org/linphone/compatibility/Api29Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api29Compatibility.kt index 47d8c7a73..4dde6a8ea 100644 --- a/app/src/main/java/org/linphone/compatibility/Api29Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Api29Compatibility.kt @@ -36,7 +36,7 @@ class Api29Compatibility { suspend fun addImageToMediaStore(context: Context, content: Content): Boolean { val filePath = content.filePath if (filePath == null) { - Log.e("[Chat Message] Content doesn't have a file path!") + Log.e("[Media Store] Content doesn't have a file path!") return false } @@ -44,7 +44,7 @@ class Api29Compatibility { val relativePath = "${Environment.DIRECTORY_PICTURES}/$appName" val fileName = content.name val mime = "${content.type}/${content.subtype}" - Log.i("[Chat Message] Adding image $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") + Log.i("[Media Store] Adding image $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") val values = ContentValues().apply { put(MediaStore.Images.Media.DISPLAY_NAME, fileName) @@ -56,7 +56,7 @@ class Api29Compatibility { val fileUri = context.contentResolver.insert(collection, values) if (fileUri == null) { - Log.e("[Chat Message] Failed to get a URI to where store the file, aborting") + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") return false } @@ -78,7 +78,7 @@ class Api29Compatibility { suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean { val filePath = content.filePath if (filePath == null) { - Log.e("[Chat Message] Content doesn't have a file path!") + Log.e("[Media Store] Content doesn't have a file path!") return false } @@ -86,7 +86,7 @@ class Api29Compatibility { val relativePath = "${Environment.DIRECTORY_MOVIES}/$appName" val fileName = content.name val mime = "${content.type}/${content.subtype}" - Log.i("[Chat Message] Adding video $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") + Log.i("[Media Store] Adding video $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") val values = ContentValues().apply { put(MediaStore.Video.Media.TITLE, fileName) @@ -99,7 +99,7 @@ class Api29Compatibility { val fileUri = context.contentResolver.insert(collection, values) if (fileUri == null) { - Log.e("[Chat Message] Failed to get a URI to where store the file, aborting") + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") return false } @@ -121,7 +121,7 @@ class Api29Compatibility { suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean { val filePath = content.filePath if (filePath == null) { - Log.e("[Chat Message] Content doesn't have a file path!") + Log.e("[Media Store] Content doesn't have a file path!") return false } @@ -129,7 +129,7 @@ class Api29Compatibility { val relativePath = "${Environment.DIRECTORY_MUSIC}/$appName" val fileName = content.name val mime = "${content.type}/${content.subtype}" - Log.i("[Chat Message] Adding audio $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") + Log.i("[Media Store] Adding audio $filePath to Media Store with name $fileName and MIME $mime, asking to be stored in $relativePath") val values = ContentValues().apply { put(MediaStore.Audio.Media.TITLE, fileName) @@ -142,7 +142,7 @@ class Api29Compatibility { val fileUri = context.contentResolver.insert(collection, values) if (fileUri == null) { - Log.e("[Chat Message] Failed to get a URI to where store the file, aborting") + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") return false }