From 4218c5b2a9601b53dd764b514f99e8f0ed31a291 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 16 May 2022 12:00:47 +0200 Subject: [PATCH] Since app min target SDK is 23, remove all references to older versions --- .../fragments/AbstractPhoneFragment.kt | 3 - .../fragments/AccountLoginFragment.kt | 5 +- .../fragments/PhoneAccountCreationFragment.kt | 5 +- .../fragments/PhoneAccountLinkingFragment.kt | 5 +- .../ConferenceWaitingRoomFragment.kt | 6 +- .../main/dialer/fragments/DialerFragment.kt | 6 +- .../viewmodels/AdvancedSettingsViewModel.kt | 3 +- .../viewmodels/CallSettingsViewModel.kt | 4 +- .../linphone/activities/voip/CallActivity.kt | 6 +- .../compatibility/Api21Compatibility.kt | 270 ------------------ .../compatibility/Api23Compatibility.kt | 244 +++++++++++++++- .../linphone/compatibility/Compatibility.kt | 39 ++- 12 files changed, 267 insertions(+), 329 deletions(-) delete mode 100644 app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt diff --git a/app/src/main/java/org/linphone/activities/assistant/fragments/AbstractPhoneFragment.kt b/app/src/main/java/org/linphone/activities/assistant/fragments/AbstractPhoneFragment.kt index fbc2db617..477e5bbf5 100644 --- a/app/src/main/java/org/linphone/activities/assistant/fragments/AbstractPhoneFragment.kt +++ b/app/src/main/java/org/linphone/activities/assistant/fragments/AbstractPhoneFragment.kt @@ -20,7 +20,6 @@ package org.linphone.activities.assistant.fragments -import android.annotation.TargetApi import android.content.pm.PackageManager import androidx.databinding.ViewDataBinding import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -29,7 +28,6 @@ import org.linphone.activities.GenericFragment import org.linphone.activities.assistant.viewmodels.AbstractPhoneViewModel import org.linphone.compatibility.Compatibility import org.linphone.core.tools.Log -import org.linphone.mediastream.Version import org.linphone.utils.PermissionHelper import org.linphone.utils.PhoneNumberUtils @@ -55,7 +53,6 @@ abstract class AbstractPhoneFragment : GenericFragment() } } - @TargetApi(Version.API23_MARSHMALLOW_60) protected fun checkPermissions() { if (!resources.getBoolean(R.bool.isTablet)) { if (!PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()) { diff --git a/app/src/main/java/org/linphone/activities/assistant/fragments/AccountLoginFragment.kt b/app/src/main/java/org/linphone/activities/assistant/fragments/AccountLoginFragment.kt index 7c07f3755..49285d9e9 100644 --- a/app/src/main/java/org/linphone/activities/assistant/fragments/AccountLoginFragment.kt +++ b/app/src/main/java/org/linphone/activities/assistant/fragments/AccountLoginFragment.kt @@ -35,7 +35,6 @@ import org.linphone.activities.main.viewmodels.DialogViewModel import org.linphone.activities.navigateToEchoCancellerCalibration import org.linphone.activities.navigateToPhoneAccountValidation import org.linphone.databinding.AssistantAccountLoginFragmentBinding -import org.linphone.mediastream.Version import org.linphone.utils.DialogUtils class AccountLoginFragment : AbstractPhoneFragment() { @@ -136,8 +135,6 @@ class AccountLoginFragment : AbstractPhoneFragment() { @@ -80,8 +79,6 @@ class PhoneAccountCreationFragment : } } - if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { - checkPermissions() - } + checkPermissions() } } diff --git a/app/src/main/java/org/linphone/activities/assistant/fragments/PhoneAccountLinkingFragment.kt b/app/src/main/java/org/linphone/activities/assistant/fragments/PhoneAccountLinkingFragment.kt index a1932bfa6..7a95a4a35 100644 --- a/app/src/main/java/org/linphone/activities/assistant/fragments/PhoneAccountLinkingFragment.kt +++ b/app/src/main/java/org/linphone/activities/assistant/fragments/PhoneAccountLinkingFragment.kt @@ -30,7 +30,6 @@ import org.linphone.activities.navigateToEchoCancellerCalibration import org.linphone.activities.navigateToPhoneAccountValidation import org.linphone.core.tools.Log import org.linphone.databinding.AssistantPhoneAccountLinkingFragmentBinding -import org.linphone.mediastream.Version class PhoneAccountLinkingFragment : AbstractPhoneFragment() { private lateinit var sharedViewModel: SharedAssistantViewModel @@ -103,8 +102,6 @@ class PhoneAccountLinkingFragment : AbstractPhoneFragment() diff --git a/app/src/main/java/org/linphone/activities/main/dialer/fragments/DialerFragment.kt b/app/src/main/java/org/linphone/activities/main/dialer/fragments/DialerFragment.kt index ea6278279..aee2b87b6 100644 --- a/app/src/main/java/org/linphone/activities/main/dialer/fragments/DialerFragment.kt +++ b/app/src/main/java/org/linphone/activities/main/dialer/fragments/DialerFragment.kt @@ -192,9 +192,7 @@ class DialerFragment : SecureFragment() { checkForUpdate() - if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { - checkPermissions() - } + checkPermissions() } override fun onPause() { @@ -244,7 +242,6 @@ class DialerFragment : SecureFragment() { super.onRequestPermissionsResult(requestCode, permissions, grantResults) } - @TargetApi(Version.API23_MARSHMALLOW_60) private fun checkPermissions() { checkReadPhoneStatePermission() if (Version.sdkAboveOrEqual(Version.API26_O_80) && PermissionHelper.get().hasReadPhoneStatePermission()) { @@ -253,7 +250,6 @@ class DialerFragment : SecureFragment() { } } - @TargetApi(Version.API23_MARSHMALLOW_60) private fun checkReadPhoneStatePermission() { if (!PermissionHelper.get().hasReadPhoneStatePermission()) { Log.i("[Dialer] Asking for READ_PHONE_STATE permission") diff --git a/app/src/main/java/org/linphone/activities/main/settings/viewmodels/AdvancedSettingsViewModel.kt b/app/src/main/java/org/linphone/activities/main/settings/viewmodels/AdvancedSettingsViewModel.kt index c177e7386..118666070 100644 --- a/app/src/main/java/org/linphone/activities/main/settings/viewmodels/AdvancedSettingsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/settings/viewmodels/AdvancedSettingsViewModel.kt @@ -28,7 +28,6 @@ import org.linphone.activities.main.viewmodels.LogsUploadViewModel import org.linphone.core.CoreContext import org.linphone.core.Factory import org.linphone.core.LogLevel -import org.linphone.mediastream.Version import org.linphone.utils.Event class AdvancedSettingsViewModel : LogsUploadViewModel() { @@ -183,6 +182,6 @@ class AdvancedSettingsViewModel : LogsUploadViewModel() { vfs.value = prefs.vfsEnabled disableSecureFragment.value = prefs.disableSecureMode - batterySettingsVisibility.value = Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60) + batterySettingsVisibility.value = true } } diff --git a/app/src/main/java/org/linphone/activities/main/settings/viewmodels/CallSettingsViewModel.kt b/app/src/main/java/org/linphone/activities/main/settings/viewmodels/CallSettingsViewModel.kt index b52997f90..f37ec1b6c 100644 --- a/app/src/main/java/org/linphone/activities/main/settings/viewmodels/CallSettingsViewModel.kt +++ b/app/src/main/java/org/linphone/activities/main/settings/viewmodels/CallSettingsViewModel.kt @@ -114,9 +114,7 @@ class CallSettingsViewModel : GenericSettingsViewModel() { val systemWideOverlayListener = object : SettingListenerStub() { override fun onBoolValueChanged(newValue: Boolean) { - if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { - if (newValue) systemWideOverlayEnabledEvent.value = Event(true) - } + if (newValue) systemWideOverlayEnabledEvent.value = Event(true) prefs.systemWideCallOverlay = newValue } } diff --git a/app/src/main/java/org/linphone/activities/voip/CallActivity.kt b/app/src/main/java/org/linphone/activities/voip/CallActivity.kt index e2fb6b79d..821e72c7a 100644 --- a/app/src/main/java/org/linphone/activities/voip/CallActivity.kt +++ b/app/src/main/java/org/linphone/activities/voip/CallActivity.kt @@ -20,7 +20,6 @@ package org.linphone.activities.voip import android.Manifest -import android.annotation.TargetApi import android.content.Intent import android.content.pm.PackageManager import android.content.res.Configuration @@ -178,9 +177,7 @@ class CallActivity : ProximitySensorActivity() { } } - if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { - checkPermissions() - } + checkPermissions() } override fun onUserLeaveHint() { @@ -252,7 +249,6 @@ class CallActivity : ProximitySensorActivity() { super.onDestroy() } - @TargetApi(Version.API23_MARSHMALLOW_60) private fun checkPermissions() { val permissionsRequiredList = arrayListOf() diff --git a/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt deleted file mode 100644 index 48cf79da5..000000000 --- a/app/src/main/java/org/linphone/compatibility/Api21Compatibility.kt +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (c) 2010-2020 Belledonne Communications SARL. - * - * This file is part of linphone-android - * (see https://www.linphone.org). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.linphone.compatibility - -import android.annotation.SuppressLint -import android.annotation.TargetApi -import android.app.* -import android.bluetooth.BluetoothAdapter -import android.content.ContentValues -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.net.Uri -import android.os.Build -import android.os.Environment -import android.os.Vibrator -import android.provider.MediaStore -import android.provider.Settings -import android.view.View -import android.view.Window -import android.view.WindowManager -import android.view.inputmethod.EditorInfo -import org.linphone.R -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) -class Api21Compatibility { - companion object { - @SuppressLint("MissingPermission") - fun getDeviceName(context: Context): String { - val adapter = BluetoothAdapter.getDefaultAdapter() - var name = adapter?.name - if (name == null) { - name = Settings.Secure.getString( - context.contentResolver, - "bluetooth_name" - ) - } - if (name == null) { - name = Build.MANUFACTURER + " " + Build.MODEL - } - return name - } - - @SuppressLint("MissingPermission") - fun eventVibration(vibrator: Vibrator) { - val pattern = longArrayOf(0, 100, 100) - vibrator.vibrate(pattern, -1) - } - - fun getBitmapFromUri(context: Context, uri: Uri): Bitmap { - return MediaStore.Images.Media.getBitmap(context.contentResolver, uri) - } - - suspend fun addImageToMediaStore(context: Context, content: Content): Boolean { - if (!PermissionHelper.get().hasWriteExternalStoragePermission()) { - Log.e("[Media Store] Write external storage permission denied") - return false - } - - val plainFilePath = content.plainFilePath.orEmpty() - val isVfsEncrypted = plainFilePath.isNotEmpty() - Log.w("[Media Store] Content is encrypted, requesting plain file path") - val filePath = if (isVfsEncrypted) plainFilePath else content.filePath - if (filePath == null) { - Log.e("[Media Store] Content doesn't have a file path!") - return false - } - - val appName = AppUtils.getString(R.string.app_name) - val relativePath = "${Environment.DIRECTORY_PICTURES}/$appName" - val fileName = content.name - val mime = "${content.type}/${content.subtype}" - 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) - put(MediaStore.Images.Media.MIME_TYPE, mime) - } - val collection = MediaStore.Images.Media.getContentUri("external") - val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values) - if (isVfsEncrypted) { - Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath") - FileUtils.deleteFile(plainFilePath) - } - if (mediaStoreFilePath.isNotEmpty()) { - content.userData = mediaStoreFilePath - return true - } - return false - } - - suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean { - if (!PermissionHelper.get().hasWriteExternalStoragePermission()) { - Log.e("[Media Store] Write external storage permission denied") - return false - } - - val plainFilePath = content.plainFilePath.orEmpty() - val isVfsEncrypted = plainFilePath.isNotEmpty() - Log.w("[Media Store] Content is encrypted, requesting plain file path") - val filePath = if (isVfsEncrypted) plainFilePath else content.filePath - if (filePath == null) { - Log.e("[Media Store] Content doesn't have a file path!") - return false - } - - val appName = AppUtils.getString(R.string.app_name) - val relativePath = "${Environment.DIRECTORY_MOVIES}/$appName" - val fileName = content.name - val mime = "${content.type}/${content.subtype}" - 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) - put(MediaStore.Video.Media.DISPLAY_NAME, fileName) - put(MediaStore.Video.Media.MIME_TYPE, mime) - } - val collection = MediaStore.Video.Media.getContentUri("external") - val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values) - if (isVfsEncrypted) { - Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath") - FileUtils.deleteFile(plainFilePath) - } - if (mediaStoreFilePath.isNotEmpty()) { - content.userData = mediaStoreFilePath - return true - } - return false - } - - suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean { - if (!PermissionHelper.get().hasWriteExternalStoragePermission()) { - Log.e("[Media Store] Write external storage permission denied") - return false - } - - val plainFilePath = content.plainFilePath.orEmpty() - val isVfsEncrypted = plainFilePath.isNotEmpty() - Log.w("[Media Store] Content is encrypted, requesting plain file path") - val filePath = if (isVfsEncrypted) plainFilePath else content.filePath - if (filePath == null) { - Log.e("[Media Store] Content doesn't have a file path!") - return false - } - - val appName = AppUtils.getString(R.string.app_name) - val relativePath = "${Environment.DIRECTORY_MUSIC}/$appName" - val fileName = content.name - val mime = "${content.type}/${content.subtype}" - 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) - put(MediaStore.Audio.Media.DISPLAY_NAME, fileName) - put(MediaStore.Audio.Media.MIME_TYPE, mime) - } - val collection = MediaStore.Audio.Media.getContentUri("external") - - val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values) - if (isVfsEncrypted) { - Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath") - FileUtils.deleteFile(plainFilePath) - } - if (mediaStoreFilePath.isNotEmpty()) { - content.userData = mediaStoreFilePath - return true - } - return false - } - - private suspend fun addContentValuesToCollection( - context: Context, - filePath: String, - collection: Uri, - values: ContentValues - ): String { - try { - val fileUri = context.contentResolver.insert(collection, values) - if (fileUri == null) { - Log.e("[Media Store] Failed to get a URI to where store the file, aborting") - return "" - } - - context.contentResolver.openOutputStream(fileUri).use { out -> - if (FileUtils.copyFileTo(filePath, out)) { - return fileUri.toString() - } - } - } catch (e: Exception) { - Log.e("[Media Store] Exception: $e") - } - return "" - } - - fun setShowWhenLocked(activity: Activity, enable: Boolean) { - if (enable) { - activity.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) - } else { - activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) - } - } - - fun setTurnScreenOn(activity: Activity, enable: Boolean) { - if (enable) { - activity.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) - } else { - activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) - } - } - - fun requestDismissKeyguard(activity: Activity) { - activity.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) - } - - fun getUpdateCurrentPendingIntentFlag(): Int { - return PendingIntent.FLAG_UPDATE_CURRENT - } - - fun getImeFlagsForSecureChatRoom(): Int { - return EditorInfo.IME_FLAG_NO_EXTRACT_UI - } - - fun startForegroundService(context: Context, intent: Intent) { - context.startService(intent) - } - - fun startForegroundService(service: Service, notifId: Int, notif: Notification?) { - service.startForeground(notifId, notif) - } - - fun hideAndroidSystemUI(hide: Boolean, window: Window) { - val decorView = window.decorView - val uiOptions = if (hide) { - View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - } else { - View.SYSTEM_UI_FLAG_VISIBLE - } - decorView.systemUiVisibility = uiOptions - - if (hide) { - window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) - } else { - window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) - } - } - } -} diff --git a/app/src/main/java/org/linphone/compatibility/Api23Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Api23Compatibility.kt index 4a7505564..c230de7bf 100644 --- a/app/src/main/java/org/linphone/compatibility/Api23Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Api23Compatibility.kt @@ -20,13 +20,36 @@ package org.linphone.compatibility import android.Manifest +import android.annotation.SuppressLint import android.annotation.TargetApi +import android.app.Activity +import android.app.Notification +import android.app.PendingIntent +import android.app.Service +import android.bluetooth.BluetoothAdapter +import android.content.ContentValues import android.content.Context +import android.content.Intent import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.os.Vibrator +import android.provider.MediaStore import android.provider.Settings +import android.view.View +import android.view.Window +import android.view.WindowManager +import android.view.inputmethod.EditorInfo import androidx.fragment.app.Fragment +import org.linphone.R +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 -@TargetApi(23) class Api23Compatibility { companion object { fun hasPermission(context: Context, permission: String): Boolean { @@ -40,5 +63,224 @@ class Api23Compatibility { fun canDrawOverlay(context: Context): Boolean { return Settings.canDrawOverlays(context) } + + fun getBitmapFromUri(context: Context, uri: Uri): Bitmap { + return MediaStore.Images.Media.getBitmap(context.contentResolver, uri) + } + + @SuppressLint("MissingPermission") + fun eventVibration(vibrator: Vibrator) { + val pattern = longArrayOf(0, 100, 100) + vibrator.vibrate(pattern, -1) + } + + @SuppressLint("MissingPermission") + fun getDeviceName(context: Context): String { + val adapter = BluetoothAdapter.getDefaultAdapter() + var name = adapter?.name + if (name == null) { + name = Settings.Secure.getString( + context.contentResolver, + "bluetooth_name" + ) + } + if (name == null) { + name = Build.MANUFACTURER + " " + Build.MODEL + } + return name + } + + fun setShowWhenLocked(activity: Activity, enable: Boolean) { + if (enable) { + activity.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) + } else { + activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) + } + } + + fun setTurnScreenOn(activity: Activity, enable: Boolean) { + if (enable) { + activity.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) + } else { + activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) + } + } + + fun requestDismissKeyguard(activity: Activity) { + activity.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD) + } + + fun startForegroundService(context: Context, intent: Intent) { + context.startService(intent) + } + + fun startForegroundService(service: Service, notifId: Int, notif: Notification?) { + service.startForeground(notifId, notif) + } + + fun hideAndroidSystemUI(hide: Boolean, window: Window) { + val decorView = window.decorView + val uiOptions = if (hide) { + View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + } else { + View.SYSTEM_UI_FLAG_VISIBLE + } + decorView.systemUiVisibility = uiOptions + + if (hide) { + window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + } + } + + fun getUpdateCurrentPendingIntentFlag(): Int { + return PendingIntent.FLAG_UPDATE_CURRENT + } + + fun getImeFlagsForSecureChatRoom(): Int { + return EditorInfo.IME_FLAG_NO_EXTRACT_UI + } + + suspend fun addImageToMediaStore(context: Context, content: Content): Boolean { + if (!PermissionHelper.get().hasWriteExternalStoragePermission()) { + Log.e("[Media Store] Write external storage permission denied") + return false + } + + val plainFilePath = content.plainFilePath.orEmpty() + val isVfsEncrypted = plainFilePath.isNotEmpty() + Log.w("[Media Store] Content is encrypted, requesting plain file path") + val filePath = if (isVfsEncrypted) plainFilePath else content.filePath + if (filePath == null) { + Log.e("[Media Store] Content doesn't have a file path!") + return false + } + + val appName = AppUtils.getString(R.string.app_name) + val relativePath = "${Environment.DIRECTORY_PICTURES}/$appName" + val fileName = content.name + val mime = "${content.type}/${content.subtype}" + 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) + put(MediaStore.Images.Media.MIME_TYPE, mime) + } + val collection = MediaStore.Images.Media.getContentUri("external") + val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values) + if (isVfsEncrypted) { + Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath") + FileUtils.deleteFile(plainFilePath) + } + if (mediaStoreFilePath.isNotEmpty()) { + content.userData = mediaStoreFilePath + return true + } + return false + } + + suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean { + if (!PermissionHelper.get().hasWriteExternalStoragePermission()) { + Log.e("[Media Store] Write external storage permission denied") + return false + } + + val plainFilePath = content.plainFilePath.orEmpty() + val isVfsEncrypted = plainFilePath.isNotEmpty() + Log.w("[Media Store] Content is encrypted, requesting plain file path") + val filePath = if (isVfsEncrypted) plainFilePath else content.filePath + if (filePath == null) { + Log.e("[Media Store] Content doesn't have a file path!") + return false + } + + val appName = AppUtils.getString(R.string.app_name) + val relativePath = "${Environment.DIRECTORY_MOVIES}/$appName" + val fileName = content.name + val mime = "${content.type}/${content.subtype}" + 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) + put(MediaStore.Video.Media.DISPLAY_NAME, fileName) + put(MediaStore.Video.Media.MIME_TYPE, mime) + } + val collection = MediaStore.Video.Media.getContentUri("external") + val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values) + if (isVfsEncrypted) { + Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath") + FileUtils.deleteFile(plainFilePath) + } + if (mediaStoreFilePath.isNotEmpty()) { + content.userData = mediaStoreFilePath + return true + } + return false + } + + suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean { + if (!PermissionHelper.get().hasWriteExternalStoragePermission()) { + Log.e("[Media Store] Write external storage permission denied") + return false + } + + val plainFilePath = content.plainFilePath.orEmpty() + val isVfsEncrypted = plainFilePath.isNotEmpty() + Log.w("[Media Store] Content is encrypted, requesting plain file path") + val filePath = if (isVfsEncrypted) plainFilePath else content.filePath + if (filePath == null) { + Log.e("[Media Store] Content doesn't have a file path!") + return false + } + + val appName = AppUtils.getString(R.string.app_name) + val relativePath = "${Environment.DIRECTORY_MUSIC}/$appName" + val fileName = content.name + val mime = "${content.type}/${content.subtype}" + 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) + put(MediaStore.Audio.Media.DISPLAY_NAME, fileName) + put(MediaStore.Audio.Media.MIME_TYPE, mime) + } + val collection = MediaStore.Audio.Media.getContentUri("external") + + val mediaStoreFilePath = addContentValuesToCollection(context, filePath, collection, values) + if (isVfsEncrypted) { + Log.w("[Media Store] Content was encrypted, delete plain version: $plainFilePath") + FileUtils.deleteFile(plainFilePath) + } + if (mediaStoreFilePath.isNotEmpty()) { + content.userData = mediaStoreFilePath + return true + } + return false + } + + private suspend fun addContentValuesToCollection( + context: Context, + filePath: String, + collection: Uri, + values: ContentValues + ): String { + try { + val fileUri = context.contentResolver.insert(collection, values) + if (fileUri == null) { + Log.e("[Media Store] Failed to get a URI to where store the file, aborting") + return "" + } + + context.contentResolver.openOutputStream(fileUri).use { out -> + if (FileUtils.copyFileTo(filePath, out)) { + return fileUri.toString() + } + } + } catch (e: Exception) { + Log.e("[Media Store] Exception: $e") + } + return "" + } } } diff --git a/app/src/main/java/org/linphone/compatibility/Compatibility.kt b/app/src/main/java/org/linphone/compatibility/Compatibility.kt index ed0910ffa..8b941dfd2 100644 --- a/app/src/main/java/org/linphone/compatibility/Compatibility.kt +++ b/app/src/main/java/org/linphone/compatibility/Compatibility.kt @@ -25,7 +25,6 @@ import android.app.PendingIntent import android.app.Service import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.graphics.Bitmap import android.net.Uri import android.os.Build @@ -51,10 +50,7 @@ class Compatibility { const val BLUETOOTH_CONNECT = "android.permission.BLUETOOTH_CONNECT" fun hasPermission(context: Context, permission: String): Boolean { - return when (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { - true -> Api23Compatibility.hasPermission(context, permission) - else -> context.packageManager.checkPermission(permission, context.packageName) == PackageManager.PERMISSION_GRANTED - } + return Api23Compatibility.hasPermission(context, permission) } // See https://developer.android.com/about/versions/11/privacy/permissions#phone-numbers @@ -102,7 +98,7 @@ class Compatibility { fun getDeviceName(context: Context): String { return when (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) { true -> Api25Compatibility.getDeviceName(context) - else -> Api21Compatibility.getDeviceName(context) + else -> Api23Compatibility.getDeviceName(context) } } @@ -118,7 +114,7 @@ class Compatibility { fun setShowWhenLocked(activity: Activity, enable: Boolean) { if (Version.sdkStrictlyBelow(Version.API27_OREO_81)) { - Api21Compatibility.setShowWhenLocked(activity, enable) + Api23Compatibility.setShowWhenLocked(activity, enable) } else { Api27Compatibility.setShowWhenLocked(activity, enable) } @@ -126,7 +122,7 @@ class Compatibility { fun setTurnScreenOn(activity: Activity, enable: Boolean) { if (Version.sdkStrictlyBelow(Version.API27_OREO_81)) { - Api21Compatibility.setTurnScreenOn(activity, enable) + Api23Compatibility.setTurnScreenOn(activity, enable) } else { Api27Compatibility.setTurnScreenOn(activity, enable) } @@ -134,7 +130,7 @@ class Compatibility { fun requestDismissKeyguard(activity: Activity) { if (Version.sdkStrictlyBelow(Version.API27_OREO_81)) { - Api21Compatibility.requestDismissKeyguard(activity) + Api23Compatibility.requestDismissKeyguard(activity) } else { Api27Compatibility.requestDismissKeyguard(activity) } @@ -142,7 +138,7 @@ class Compatibility { fun getBitmapFromUri(context: Context, uri: Uri): Bitmap { return if (Version.sdkStrictlyBelow(Version.API29_ANDROID_10)) { - Api21Compatibility.getBitmapFromUri(context, uri) + Api23Compatibility.getBitmapFromUri(context, uri) } else { Api29Compatibility.getBitmapFromUri(context, uri) } @@ -224,7 +220,7 @@ class Compatibility { } else if (Version.sdkAboveOrEqual(Version.API26_O_80)) { Api26Compatibility.startForegroundService(context, intent) } else { - Api21Compatibility.startForegroundService(context, intent) + Api23Compatibility.startForegroundService(context, intent) } } @@ -232,17 +228,14 @@ class Compatibility { if (Version.sdkAboveOrEqual(Version.API31_ANDROID_12)) { Api31Compatibility.startForegroundService(service, notifId, notif) } else { - Api21Compatibility.startForegroundService(service, notifId, notif) + Api23Compatibility.startForegroundService(service, notifId, notif) } } /* Call */ fun canDrawOverlay(context: Context): Boolean { - if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { - return Api23Compatibility.canDrawOverlay(context) - } - return false + return Api23Compatibility.canDrawOverlay(context) } fun enterPipMode(activity: Activity) { @@ -261,7 +254,7 @@ class Compatibility { if (Version.sdkAboveOrEqual(Version.API26_O_80)) { Api26Compatibility.eventVibration(vibrator) } else { - Api21Compatibility.eventVibration(vibrator) + Api23Compatibility.eventVibration(vibrator) } } @@ -276,7 +269,7 @@ class Compatibility { if (Version.sdkAboveOrEqual(Version.API30_ANDROID_11)) { Api30Compatibility.hideAndroidSystemUI(hide, window) } else { - Api21Compatibility.hideAndroidSystemUI(hide, window) + Api23Compatibility.hideAndroidSystemUI(hide, window) } } @@ -332,35 +325,35 @@ class Compatibility { if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) { return Api29Compatibility.addImageToMediaStore(context, content) } - return Api21Compatibility.addImageToMediaStore(context, content) + return Api23Compatibility.addImageToMediaStore(context, content) } suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean { if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) { return Api29Compatibility.addVideoToMediaStore(context, content) } - return Api21Compatibility.addVideoToMediaStore(context, content) + return Api23Compatibility.addVideoToMediaStore(context, content) } suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean { if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10)) { return Api29Compatibility.addAudioToMediaStore(context, content) } - return Api21Compatibility.addAudioToMediaStore(context, content) + return Api23Compatibility.addAudioToMediaStore(context, content) } fun getUpdateCurrentPendingIntentFlag(): Int { if (Version.sdkAboveOrEqual(Version.API31_ANDROID_12)) { return Api31Compatibility.getUpdateCurrentPendingIntentFlag() } - return Api21Compatibility.getUpdateCurrentPendingIntentFlag() + return Api23Compatibility.getUpdateCurrentPendingIntentFlag() } fun getImeFlagsForSecureChatRoom(): Int { if (Version.sdkAboveOrEqual(Version.API26_O_80)) { return Api26Compatibility.getImeFlagsForSecureChatRoom() } - return Api21Compatibility.getImeFlagsForSecureChatRoom() + return Api23Compatibility.getImeFlagsForSecureChatRoom() } } }