diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 279b00951..c9cbe6f53 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -71,7 +71,12 @@
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29"
tools:ignore="ScopedStorage" />
-
+
+
+
+
+
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
index 711be18ea..48a693b6b 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt
@@ -801,7 +801,7 @@ class ChatController(args: Bundle) :
requestRecordAudioPermissions()
return true
}
- if (!UploadAndShareFilesWorker.isStoragePermissionGranted(context)) {
+ if (!permissionUtil.isFilesPermissionGranted()) {
UploadAndShareFilesWorker.requestStoragePermission(this@ChatController)
return true
}
@@ -1312,6 +1312,26 @@ class ChatController(args: Bundle) :
)
}
+ private fun requestReadFilesPermissions() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ requestPermissions(
+ arrayOf(
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.READ_MEDIA_AUDIO
+ ),
+ REQUEST_SHARE_FILE_PERMISSION
+ )
+ } else {
+ requestPermissions(
+ arrayOf(
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ ),
+ REQUEST_SHARE_FILE_PERMISSION
+ )
+ }
+ }
+
private fun checkShowCallButtons() {
if (isAlive()) {
if (isReadOnlyConversation() || shouldShowLobby()) {
@@ -1490,7 +1510,7 @@ class ChatController(args: Bundle) :
.setTitle(confirmationQuestion)
.setMessage(filenamesWithLineBreaks.toString())
.setPositiveButton(R.string.nc_yes) { _, _ ->
- if (UploadAndShareFilesWorker.isStoragePermissionGranted(context)) {
+ if (permissionUtil.isFilesPermissionGranted()) {
uploadFiles(filesToUpload)
} else {
UploadAndShareFilesWorker.requestStoragePermission(this)
@@ -1557,7 +1577,7 @@ class ChatController(args: Bundle) :
throw IllegalStateException("Failed to get data from intent and uri")
}
- if (UploadAndShareFilesWorker.isStoragePermissionGranted(context)) {
+ if (permissionUtil.isFilesPermissionGranted()) {
uploadFiles(filesToUpload)
} else {
UploadAndShareFilesWorker.requestStoragePermission(this)
@@ -1618,6 +1638,10 @@ class ChatController(args: Bundle) :
}
}
+ private fun hasGrantedPermissions(grantResults: IntArray): Boolean {
+ return permissionUtil.isFilesPermissionGranted()
+ }
+
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
if (requestCode == UploadAndShareFilesWorker.REQUEST_PERMISSION) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
@@ -1630,6 +1654,16 @@ class ChatController(args: Bundle) :
.makeText(context, context.getString(R.string.read_storage_no_permission), Toast.LENGTH_LONG)
.show()
}
+ } else if (requestCode == REQUEST_SHARE_FILE_PERMISSION) {
+ if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ showLocalFilePicker()
+ } else {
+ Toast.makeText(
+ context,
+ context.getString(R.string.nc_file_storage_permission),
+ Toast.LENGTH_LONG
+ ).show()
+ }
} else if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// do nothing. user will tap on the microphone again if he wants to record audio..
@@ -1696,7 +1730,7 @@ class ChatController(args: Bundle) :
}
}
- fun sendSelectLocalFileIntent() {
+ private fun showLocalFilePicker() {
val action = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "*/*"
addCategory(Intent.CATEGORY_OPENABLE)
@@ -1713,6 +1747,14 @@ class ChatController(args: Bundle) :
)
}
+ fun sendSelectLocalFileIntent() {
+ if (!permissionUtil.isFilesPermissionGranted()) {
+ requestReadFilesPermissions()
+ } else {
+ showLocalFilePicker()
+ }
+ }
+
fun sendChooseContactIntent() {
requestReadContacts()
}
@@ -3490,6 +3532,7 @@ class ChatController(args: Bundle) :
private const val REQUEST_CODE_CHOOSE_FILE: Int = 555
private const val REQUEST_CODE_SELECT_CONTACT: Int = 666
private const val REQUEST_CODE_MESSAGE_SEARCH: Int = 777
+ private const val REQUEST_SHARE_FILE_PERMISSION: Int = 221
private const val REQUEST_RECORD_AUDIO_PERMISSION = 222
private const val REQUEST_READ_CONTACT_PERMISSION = 234
private const val REQUEST_CAMERA_PERMISSION = 223
diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
index f675124b3..ed538dab8 100644
--- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
+++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt
@@ -87,7 +87,6 @@ import com.nextcloud.talk.jobs.AccountRemovalWorker
import com.nextcloud.talk.jobs.ContactAddressBookWorker.Companion.run
import com.nextcloud.talk.jobs.DeleteConversationWorker
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
-import com.nextcloud.talk.jobs.UploadAndShareFilesWorker.Companion.isStoragePermissionGranted
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker.Companion.requestStoragePermission
import com.nextcloud.talk.messagesearch.MessageSearchHelper
import com.nextcloud.talk.messagesearch.MessageSearchHelper.MessageSearchResults
@@ -119,6 +118,7 @@ import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatu
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isServerEOL
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUnifiedSearchAvailable
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.isUserStatusAvailable
+import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.remapchat.ConductorRemapping.remapChatController
import com.nextcloud.talk.utils.rx.SearchViewObservable.Companion.observeSearchView
import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder
@@ -159,6 +159,9 @@ class ConversationsListController(bundle: Bundle) :
@Inject
lateinit var unifiedSearchRepository: UnifiedSearchRepository
+ @Inject
+ lateinit var platformPermissionUtil: PlatformPermissionUtil
+
private val binding: ControllerConversationsRvBinding? by viewBinding(ControllerConversationsRvBinding::bind)
override val title: String
@@ -960,7 +963,7 @@ class ConversationsListController(bundle: Bundle) :
}
private fun showSendFilesConfirmDialog() {
- if (isStoragePermissionGranted(context)) {
+ if (platformPermissionUtil.isFilesPermissionGranted()) {
val fileNamesWithLineBreaks = StringBuilder("\n")
for (file in filesToShare!!) {
val filename = FileUtils.getFileName(Uri.parse(file), context)
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt
index bf157467b..4f106c609 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt
@@ -32,7 +32,6 @@ import android.os.Bundle
import android.os.SystemClock
import android.util.Log
import androidx.core.app.NotificationCompat
-import androidx.core.content.PermissionChecker
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
@@ -57,6 +56,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CA
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew
+import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.preferences.AppPreferences
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
@@ -78,6 +78,9 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
@Inject
lateinit var okHttpClient: OkHttpClient
+ @Inject
+ lateinit var platformPermissionUtil: PlatformPermissionUtil
+
lateinit var fileName: String
private var mNotifyManager: NotificationManager? = null
@@ -93,7 +96,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
override fun doWork(): Result {
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
- if (!isStoragePermissionGranted(context)) {
+ if (!platformPermissionUtil.isFilesPermissionGranted()) {
Log.w(
TAG,
"Storage permission is not granted. As a developer please make sure you check for" +
@@ -285,39 +288,19 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
private const val ZERO_PERCENT = 0
const val REQUEST_PERMISSION = 3123
- fun isStoragePermissionGranted(context: Context): Boolean {
- return when {
- Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> {
- if (PermissionChecker.checkSelfPermission(
- context,
- Manifest.permission.READ_EXTERNAL_STORAGE
- ) == PermissionChecker.PERMISSION_GRANTED
- ) {
- Log.d(TAG, "Permission is granted (SDK 30 or greater)")
- true
- } else {
- Log.d(TAG, "Permission is revoked (SDK 30 or greater)")
- false
- }
- }
- else -> {
- if (PermissionChecker.checkSelfPermission(
- context,
- Manifest.permission.WRITE_EXTERNAL_STORAGE
- ) == PermissionChecker.PERMISSION_GRANTED
- ) {
- Log.d(TAG, "Permission is granted")
- true
- } else {
- Log.d(TAG, "Permission is revoked")
- false
- }
- }
- }
- }
-
fun requestStoragePermission(controller: Controller) {
when {
+ Build.VERSION
+ .SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
+ controller.requestPermissions(
+ arrayOf(
+ Manifest.permission.READ_MEDIA_IMAGES,
+ Manifest.permission.READ_MEDIA_VIDEO,
+ Manifest.permission.READ_MEDIA_AUDIO
+ ),
+ REQUEST_PERMISSION
+ )
+ }
Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> {
controller.requestPermissions(
arrayOf(
diff --git a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt
index aa7ddc4f6..0acc73f19 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt
@@ -24,4 +24,6 @@ package com.nextcloud.talk.utils.permissions
interface PlatformPermissionUtil {
val privateBroadcastPermission: String
fun isCameraPermissionGranted(): Boolean
+
+ fun isFilesPermissionGranted(): Boolean
}
diff --git a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt
index 27f4a6f71..3a358f3dc 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtilImpl.kt
@@ -23,6 +23,8 @@ package com.nextcloud.talk.utils.permissions
import android.Manifest
import android.content.Context
+import android.os.Build
+import android.util.Log
import androidx.core.content.PermissionChecker
import com.nextcloud.talk.BuildConfig
@@ -36,4 +38,55 @@ class PlatformPermissionUtilImpl(private val context: Context) : PlatformPermiss
Manifest.permission.CAMERA
) == PermissionChecker.PERMISSION_GRANTED
}
+
+ override fun isFilesPermissionGranted(): Boolean {
+ return when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> {
+ if (
+ PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_MEDIA_IMAGES)
+ == PermissionChecker.PERMISSION_GRANTED ||
+ PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_MEDIA_VIDEO)
+ == PermissionChecker.PERMISSION_GRANTED ||
+ PermissionChecker.checkSelfPermission(context, Manifest.permission.READ_MEDIA_AUDIO)
+ == PermissionChecker.PERMISSION_GRANTED
+ ) {
+ Log.d(TAG, "Permission is granted (SDK 33 or greater)")
+ true
+ } else {
+ Log.d(TAG, "Permission is revoked (SDK 33 or greater)")
+ false
+ }
+ }
+ Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> {
+ if (PermissionChecker.checkSelfPermission(
+ context,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ ) == PermissionChecker.PERMISSION_GRANTED
+ ) {
+ Log.d(TAG, "Permission is granted (SDK 30 or greater)")
+ true
+ } else {
+ Log.d(TAG, "Permission is revoked (SDK 30 or greater)")
+ false
+ }
+ }
+ else -> {
+ if (PermissionChecker.checkSelfPermission(
+ context,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ ) == PermissionChecker.PERMISSION_GRANTED
+ ) {
+ Log.d(TAG, "Permission is granted")
+ true
+ } else {
+ Log.d(TAG, "Permission is revoked")
+ false
+ }
+ }
+ }
+ }
+
+ companion object {
+ private val TAG = PlatformPermissionUtilImpl::class.simpleName
+ }
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 05b0bb30f..6e39f6c44 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -464,6 +464,7 @@ How to translate with transifex:
%1$s to %2$s - %3$s\%%
Failure
Failed to upload %1$s
+ Permission for file access is required
Video recording from %1$s