From 5e50362a9b38fc7616bdbc1932ed243460c78ada Mon Sep 17 00:00:00 2001 From: Yacine Rezgui Date: Wed, 13 Apr 2022 07:43:45 +0100 Subject: [PATCH 1/6] Add storage permissions checking as Context extension functions --- .../permissions/RequestAccess.kt | 11 +- .../permissions/StoragePermissions.kt | 146 ++++++++++++++++++ 2 files changed, 152 insertions(+), 5 deletions(-) diff --git a/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt b/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt index 8e62b5f2..154679f3 100644 --- a/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt +++ b/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt @@ -21,7 +21,7 @@ import android.content.Intent import android.content.pm.PackageManager import androidx.activity.result.contract.ActivityResultContract import androidx.activity.result.contract.ActivityResultContracts -import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSION_GRANT_RESULTS +//import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSION_GRANT_RESULTS import androidx.core.content.ContextCompat class RequestAccess : ActivityResultContract() { @@ -69,9 +69,10 @@ class RequestAccess : ActivityResultContract() { if (intent == null) return false val permissions = intent.getStringArrayExtra(ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSIONS) - val grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS) - if (grantResults == null || permissions == null) return false - - return grantResults.all { result -> result == PackageManager.PERMISSION_GRANTED } + return false +// val grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS) +// if (grantResults == null || permissions == null) return false +// +// return grantResults.all { result -> result == PackageManager.PERMISSION_GRANTED } } } diff --git a/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt b/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt index 4d603f6c..4b9d68db 100644 --- a/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt +++ b/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt @@ -17,6 +17,9 @@ package com.google.modernstorage.permissions import android.Manifest.permission.MANAGE_EXTERNAL_STORAGE import android.Manifest.permission.READ_EXTERNAL_STORAGE +import android.Manifest.permission.READ_MEDIA_IMAGE +import android.Manifest.permission.READ_MEDIA_VIDEO +import android.Manifest.permission.READ_MEDIA_AUDIO import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.content.Context import android.content.pm.PackageManager @@ -24,6 +27,149 @@ import android.os.Build import android.os.Environment import androidx.core.content.ContextCompat +/** + * Type of files + */ +enum class FileType { + Image, Video, Audio, Document +} + +/** + * Type of file ownership + */ +enum class CreatedBy { + Self, AllApps +} + +/** + * Type of file actions + */ +enum class Action { + READ, READ_AND_WRITE +} + +/** + * Check if app can read shared files + */ +fun Context.hasStorageReadAccessFor( + types: List, + createdBy: CreatedBy +): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { + return true + } + + val targetSdk = applicationInfo.targetSdkVersion + + return getPermissions(Action.READ_AND_WRITE, types, createdBy, targetSdk).all { + ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + } +} + +/** + * Check if app can read & write shared files + */ +fun Context.hasStorageReadWriteAccessFor( + types: List, + createdBy: CreatedBy +): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { + return true + } + + val targetSdk = applicationInfo.targetSdkVersion + + return getPermissions(Action.READ_AND_WRITE, types, createdBy, targetSdk).all { + ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + } +} + +internal fun getPermissions( + action: Action, + types: List, + createdBy: CreatedBy, + targetSdk: Int +): Set { + val permissions = mutableSetOf() + + when (createdBy) { + CreatedBy.Self -> { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + permissions += when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE + } + } + } + CreatedBy.AllApps -> { + if (types.contains(FileType.Image)) { + permissions += when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { + READ_MEDIA_IMAGE + } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + READ_EXTERNAL_STORAGE + } + else -> { + when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE + } + } + } + } + + if (types.contains(FileType.Video)) { + permissions += when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { + READ_MEDIA_VIDEO + } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + READ_EXTERNAL_STORAGE + } + else -> { + when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE + } + } + } + } + + if (types.contains(FileType.Audio)) { + permissions += when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { + READ_MEDIA_AUDIO + } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + READ_EXTERNAL_STORAGE + } + else -> { + when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE + } + } + } + } + + if (types.contains(FileType.Document)) { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return setOf(MANAGE_EXTERNAL_STORAGE) + } else { + permissions += when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE + } + } + } + } + } + + return permissions +} + + class StoragePermissions(private val context: Context) { companion object { private const val READ_EXTERNAL_STORAGE_MASK = 0b0001 From 952ada9a9bf1f67c111354e9445973d436fce429 Mon Sep 17 00:00:00 2001 From: Yacine Rezgui Date: Wed, 20 Apr 2022 13:58:19 +0100 Subject: [PATCH 2/6] Update permissions library API --- permissions/build.gradle.kts | 6 +- .../permissions/RequestAccess.kt | 78 ---- .../permissions/RequestStorageAccessFor.kt | 87 ++++ .../permissions/StoragePermissions.kt | 418 +++++------------- 4 files changed, 195 insertions(+), 394 deletions(-) delete mode 100644 permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt create mode 100644 permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt diff --git a/permissions/build.gradle.kts b/permissions/build.gradle.kts index 0149fc1a..d7b9c6d3 100644 --- a/permissions/build.gradle.kts +++ b/permissions/build.gradle.kts @@ -21,11 +21,13 @@ plugins { } android { - compileSdk = 31 +// compileSdk = 31 + compileSdkPreview = "Tiramisu" defaultConfig { minSdk = 21 - targetSdk = 31 +// targetSdk = 31 + targetSdkPreview = "Tiramisu" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") diff --git a/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt b/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt deleted file mode 100644 index 154679f3..00000000 --- a/permissions/src/main/java/com/google/modernstorage/permissions/RequestAccess.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2022 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.google.modernstorage.permissions - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.content.pm.PackageManager -import androidx.activity.result.contract.ActivityResultContract -import androidx.activity.result.contract.ActivityResultContracts -//import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSION_GRANT_RESULTS -import androidx.core.content.ContextCompat - -class RequestAccess : ActivityResultContract() { - class Args( - val action: StoragePermissions.Action, - val types: List, - val createdBy: StoragePermissions.CreatedBy - ) - - override fun createIntent(context: Context, input: Args): Intent { - val permissions = - StoragePermissions.getPermissions(input.action, input.types, input.createdBy) - return Intent(ActivityResultContracts.RequestMultiplePermissions.ACTION_REQUEST_PERMISSIONS).putExtra( - ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSIONS, - permissions.toTypedArray() - ) - } - - override fun getSynchronousResult( - context: Context, - input: Args - ): SynchronousResult? { - val permissions = - StoragePermissions.getPermissions(input.action, input.types, input.createdBy) - - if (permissions.isEmpty()) { - return SynchronousResult(true) - } - - val allGranted = permissions.all { permission -> - ContextCompat.checkSelfPermission( - context, - permission - ) == PackageManager.PERMISSION_GRANTED - } - - return if (allGranted) SynchronousResult(true) else null - } - - override fun parseResult( - resultCode: Int, - intent: Intent? - ): Boolean { - if (resultCode != Activity.RESULT_OK) return false - if (intent == null) return false - val permissions = - intent.getStringArrayExtra(ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSIONS) - return false -// val grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS) -// if (grantResults == null || permissions == null) return false -// -// return grantResults.all { result -> result == PackageManager.PERMISSION_GRANTED } - } -} diff --git a/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt b/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt new file mode 100644 index 00000000..72046ebe --- /dev/null +++ b/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.modernstorage.permissions + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import androidx.activity.result.contract.ActivityResultContract +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.ACTION_REQUEST_PERMISSIONS +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSIONS +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSION_GRANT_RESULTS +import androidx.core.content.ContextCompat + +abstract class RequestStorageAccessFor( + private val action: StoragePermissions.Action, + private val fileTypes: List, + private val createdBy: StoragePermissions.CreatedBy +) : ActivityResultContract() { + + override fun createIntent(context: Context, input: Void?): Intent { + val targetSdk = context.applicationInfo.targetSdkVersion + val permissions = + StoragePermissions.getPermissions(action, fileTypes, createdBy, targetSdk) + return Intent(ACTION_REQUEST_PERMISSIONS).apply { + putExtra(EXTRA_PERMISSIONS, permissions.toTypedArray()) + } + } + + override fun getSynchronousResult( + context: Context, + input: Void? + ): SynchronousResult? { + val targetSdk = context.applicationInfo.targetSdkVersion + val permissions = + StoragePermissions.getPermissions(action, fileTypes, createdBy, targetSdk) + + if (permissions.isEmpty()) { + return SynchronousResult(true) + } + + val allGranted = permissions.all { permission -> + ContextCompat.checkSelfPermission( + context, + permission + ) == PackageManager.PERMISSION_GRANTED + } + + return if (allGranted) SynchronousResult(true) else null + } + + override fun parseResult( + resultCode: Int, + intent: Intent? + ): Boolean { + if (resultCode != Activity.RESULT_OK) return false + if (intent == null) return false + val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS) + val grantResults = intent.getIntArrayExtra(EXTRA_PERMISSION_GRANT_RESULTS) + if (grantResults == null || permissions == null) return false + + return grantResults.all { result -> result == PackageManager.PERMISSION_GRANTED } + } +} + +class RequestStorageReadAccessFor( + fileTypes: List, + createdBy: StoragePermissions.CreatedBy +) : RequestStorageAccessFor(StoragePermissions.Action.READ, fileTypes, createdBy) + +class RequestStorageReadWriteAccessFor( + fileTypes: List, + createdBy: StoragePermissions.CreatedBy +) : RequestStorageAccessFor(StoragePermissions.Action.READ_AND_WRITE, fileTypes, createdBy) diff --git a/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt b/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt index 4b9d68db..fb7b95cf 100644 --- a/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt +++ b/permissions/src/main/java/com/google/modernstorage/permissions/StoragePermissions.kt @@ -17,9 +17,9 @@ package com.google.modernstorage.permissions import android.Manifest.permission.MANAGE_EXTERNAL_STORAGE import android.Manifest.permission.READ_EXTERNAL_STORAGE +import android.Manifest.permission.READ_MEDIA_AUDIO import android.Manifest.permission.READ_MEDIA_IMAGE import android.Manifest.permission.READ_MEDIA_VIDEO -import android.Manifest.permission.READ_MEDIA_AUDIO import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.content.Context import android.content.pm.PackageManager @@ -27,356 +27,146 @@ import android.os.Build import android.os.Environment import androidx.core.content.ContextCompat -/** - * Type of files - */ -enum class FileType { - Image, Video, Audio, Document -} - -/** - * Type of file ownership - */ -enum class CreatedBy { - Self, AllApps -} - -/** - * Type of file actions - */ -enum class Action { - READ, READ_AND_WRITE -} - -/** - * Check if app can read shared files - */ -fun Context.hasStorageReadAccessFor( - types: List, - createdBy: CreatedBy -): Boolean { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { - return true +object StoragePermissions { + /** + * Type of files + */ + enum class FileType { + Image, Video, Audio, Document } - val targetSdk = applicationInfo.targetSdkVersion - - return getPermissions(Action.READ_AND_WRITE, types, createdBy, targetSdk).all { - ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + /** + * Type of file ownership + */ + enum class CreatedBy { + Self, AllApps } -} -/** - * Check if app can read & write shared files - */ -fun Context.hasStorageReadWriteAccessFor( - types: List, - createdBy: CreatedBy -): Boolean { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { - return true + /** + * Type of file actions + */ + enum class Action { + READ, READ_AND_WRITE } - val targetSdk = applicationInfo.targetSdkVersion + /** + * Check if app can read shared files + */ + fun Context.hasStorageReadAccessFor( + fileTypes: List, + createdBy: CreatedBy + ): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { + return true + } - return getPermissions(Action.READ_AND_WRITE, types, createdBy, targetSdk).all { - ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED - } -} + val targetSdk = applicationInfo.targetSdkVersion -internal fun getPermissions( - action: Action, - types: List, - createdBy: CreatedBy, - targetSdk: Int -): Set { - val permissions = mutableSetOf() + return getPermissions(Action.READ, fileTypes, createdBy, targetSdk).all { + ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + } + } - when (createdBy) { - CreatedBy.Self -> { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - permissions += when (action) { - Action.READ -> READ_EXTERNAL_STORAGE - Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE - } - } + /** + * Check if app can read & write shared files + */ + fun Context.hasStorageReadWriteAccessFor( + fileTypes: List, + createdBy: CreatedBy + ): Boolean { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { + return true } - CreatedBy.AllApps -> { - if (types.contains(FileType.Image)) { - permissions += when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { - READ_MEDIA_IMAGE - } - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - READ_EXTERNAL_STORAGE - } - else -> { - when (action) { - Action.READ -> READ_EXTERNAL_STORAGE - Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE - } - } - } - } - if (types.contains(FileType.Video)) { - permissions += when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { - READ_MEDIA_VIDEO - } - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - READ_EXTERNAL_STORAGE - } - else -> { - when (action) { - Action.READ -> READ_EXTERNAL_STORAGE - Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE - } - } - } - } + val targetSdk = applicationInfo.targetSdkVersion - if (types.contains(FileType.Audio)) { - permissions += when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { - READ_MEDIA_AUDIO - } - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - READ_EXTERNAL_STORAGE - } - else -> { - when (action) { - Action.READ -> READ_EXTERNAL_STORAGE - Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE - } - } - } - } + return getPermissions(Action.READ_AND_WRITE, fileTypes, createdBy, targetSdk).all { + ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED + } + } - if (types.contains(FileType.Document)) { - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - return setOf(MANAGE_EXTERNAL_STORAGE) - } else { + internal fun getPermissions( + action: Action, + types: List, + createdBy: CreatedBy, + targetSdk: Int + ): Set { + val permissions = mutableSetOf() + + when (createdBy) { + CreatedBy.Self -> { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { permissions += when (action) { Action.READ -> READ_EXTERNAL_STORAGE Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE } } } - } - } - - return permissions -} - - -class StoragePermissions(private val context: Context) { - companion object { - private const val READ_EXTERNAL_STORAGE_MASK = 0b0001 - private const val WRITE_EXTERNAL_STORAGE_MASK = 0b0011 - private const val SCOPED_STORAGE_READ_EXTERNAL_STORAGE_MASK = 0b0100 - private const val MANAGE_EXTERNAL_STORAGE_MASK = 0b1111 - - private fun getPermissionMask( - action: Action, - types: List, - createdBy: CreatedBy - ): Int { - var permissionMask = 0 - - when (createdBy) { - CreatedBy.Self -> { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - permissionMask = when (action) { - Action.READ -> permissionMask or READ_EXTERNAL_STORAGE_MASK - Action.READ_AND_WRITE -> permissionMask or WRITE_EXTERNAL_STORAGE_MASK + CreatedBy.AllApps -> { + if (types.contains(FileType.Image)) { + permissions += when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { + READ_MEDIA_IMAGE } - } - } - CreatedBy.AllApps -> { - if (types.contains(FileType.Image)) { - permissionMask = when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - permissionMask or SCOPED_STORAGE_READ_EXTERNAL_STORAGE_MASK - } - else -> { - when (action) { - Action.READ -> permissionMask or READ_EXTERNAL_STORAGE_MASK - Action.READ_AND_WRITE -> permissionMask or WRITE_EXTERNAL_STORAGE_MASK - } - } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + READ_EXTERNAL_STORAGE } - } - - if (types.contains(FileType.Video)) { - permissionMask = when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - permissionMask or SCOPED_STORAGE_READ_EXTERNAL_STORAGE_MASK - } - else -> { - when (action) { - Action.READ -> permissionMask or READ_EXTERNAL_STORAGE_MASK - Action.READ_AND_WRITE -> permissionMask or WRITE_EXTERNAL_STORAGE_MASK - } + else -> { + when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE } } } + } - if (types.contains(FileType.Audio)) { - permissionMask = when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - permissionMask or SCOPED_STORAGE_READ_EXTERNAL_STORAGE_MASK - } - else -> { - when (action) { - Action.READ -> permissionMask or READ_EXTERNAL_STORAGE_MASK - Action.READ_AND_WRITE -> permissionMask or WRITE_EXTERNAL_STORAGE_MASK - } + if (types.contains(FileType.Video)) { + permissions += when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { + READ_MEDIA_VIDEO + } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + READ_EXTERNAL_STORAGE + } + else -> { + when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE } } } + } - if (types.contains(FileType.Document)) { - permissionMask = when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { - permissionMask or MANAGE_EXTERNAL_STORAGE_MASK - } - else -> { - when (action) { - Action.READ -> permissionMask or READ_EXTERNAL_STORAGE_MASK - Action.READ_AND_WRITE -> permissionMask or WRITE_EXTERNAL_STORAGE_MASK - } + if (types.contains(FileType.Audio)) { + permissions += when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && targetSdk >= Build.VERSION_CODES.TIRAMISU -> { + READ_MEDIA_AUDIO + } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { + READ_EXTERNAL_STORAGE + } + else -> { + when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE } } } } - } - - return permissionMask - } - /** - * Get list of required permissions for given usage - */ - @JvmStatic - fun getPermissions( - action: Action, - types: List, - createdBy: CreatedBy - ): List { - val permissionMask = getPermissionMask(action, types, createdBy) - val requiredPermissions = mutableListOf() - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && permissionMask and MANAGE_EXTERNAL_STORAGE_MASK == MANAGE_EXTERNAL_STORAGE_MASK) { - requiredPermissions.add(MANAGE_EXTERNAL_STORAGE) - } else { - when { - Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && permissionMask and MANAGE_EXTERNAL_STORAGE_MASK == MANAGE_EXTERNAL_STORAGE_MASK -> requiredPermissions.add( - MANAGE_EXTERNAL_STORAGE - ) - permissionMask and SCOPED_STORAGE_READ_EXTERNAL_STORAGE_MASK == SCOPED_STORAGE_READ_EXTERNAL_STORAGE_MASK -> requiredPermissions.add( - READ_EXTERNAL_STORAGE - ) - permissionMask and WRITE_EXTERNAL_STORAGE_MASK == WRITE_EXTERNAL_STORAGE_MASK -> requiredPermissions.add( - WRITE_EXTERNAL_STORAGE - ) - permissionMask and READ_EXTERNAL_STORAGE_MASK == READ_EXTERNAL_STORAGE_MASK -> requiredPermissions.add( - READ_EXTERNAL_STORAGE - ) + if (types.contains(FileType.Document)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return setOf(MANAGE_EXTERNAL_STORAGE) + } else { + permissions += when (action) { + Action.READ -> READ_EXTERNAL_STORAGE + Action.READ_AND_WRITE -> WRITE_EXTERNAL_STORAGE + } + } } } - - return requiredPermissions } - /** - * Get list of required permissions for given read usage - */ - @Deprecated( - "Use the new getPermissions() method", - ReplaceWith("getPermissions(Action.READ, types, createdBy)"), - DeprecationLevel.WARNING - ) - fun getReadFilesPermissions(types: List, createdBy: CreatedBy): List { - return getPermissions(Action.READ, types, createdBy) - } - - /** - * Get list of required permissions for given read usage - */ - @Deprecated( - "Use the new getPermissions() method", - ReplaceWith("getPermissions(Action.READ_AND_WRITE, types, createdBy)"), - DeprecationLevel.WARNING - ) - fun getReadAndWriteFilesPermissions( - types: List, - createdBy: CreatedBy - ): List { - return getPermissions(Action.READ_AND_WRITE, types, createdBy) - } - } - - /** - * Type of files - */ - enum class FileType { - Image, Video, Audio, Document - } - - /** - * Type of file ownership - */ - enum class CreatedBy { - Self, AllApps - } - - /** - * Type of file actions - */ - enum class Action { - READ, READ_AND_WRITE - } - - private fun hasPermission(permission: String): Boolean { - return ContextCompat.checkSelfPermission( - context, - permission - ) == PackageManager.PERMISSION_GRANTED - } - - /** - * Check if app can access shared files - */ - fun hasAccess(action: Action, types: List, createdBy: CreatedBy): Boolean { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && Environment.isExternalStorageManager()) { - return true - } - - return getPermissions(action, types, createdBy).all { hasPermission(it) } - } - - /** - * Check if app can read shared files - */ - @Deprecated( - "Use the new hasAccess() method", - ReplaceWith("hasAccess(Action.READ, types, createdBy)"), - DeprecationLevel.WARNING - ) - fun canReadFiles(types: List, createdBy: CreatedBy): Boolean { - return hasAccess(Action.READ, types, createdBy) - } - - /** - * Check if app can read and write shared files - */ - @Deprecated( - "Use the new hasAccess() method", - ReplaceWith("hasAccess(Action.READ_AND_WRITE, types, createdBy)"), - DeprecationLevel.WARNING - ) - fun canReadAndWriteFiles(types: List, createdBy: CreatedBy): Boolean { - return hasAccess(Action.READ_AND_WRITE, types, createdBy) + return permissions } } From 1bce268d29b98b0b21fd15519487bafb6c6a7e6d Mon Sep 17 00:00:00 2001 From: Yacine Rezgui Date: Wed, 20 Apr 2022 13:59:30 +0100 Subject: [PATCH 3/6] Update permissions usage in sample app --- .../mediastore/AddFileToDownloadsScreen.kt | 26 +++--- .../sample/mediastore/AddMediaScreen.kt | 32 ++++--- .../permissions/CheckPermissionScreen.kt | 85 ++++++++----------- 3 files changed, 64 insertions(+), 79 deletions(-) diff --git a/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddFileToDownloadsScreen.kt b/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddFileToDownloadsScreen.kt index 8984087a..d85bf2d3 100644 --- a/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddFileToDownloadsScreen.kt +++ b/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddFileToDownloadsScreen.kt @@ -16,6 +16,7 @@ package com.google.modernstorage.sample.mediastore import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.launch import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -44,9 +45,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import com.google.modernstorage.permissions.RequestAccess +import com.google.modernstorage.permissions.RequestStorageReadWriteAccessFor import com.google.modernstorage.permissions.StoragePermissions -import com.google.modernstorage.permissions.StoragePermissions.Action +import com.google.modernstorage.permissions.StoragePermissions.hasStorageReadWriteAccessFor import com.google.modernstorage.sample.Demos import com.google.modernstorage.sample.HomeRoute import com.google.modernstorage.sample.R @@ -64,11 +65,11 @@ fun AddFileToDownloadsScreen( var openPermissionDialog by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() + val context = LocalContext.current val scaffoldState = rememberScaffoldState() - val permissions = StoragePermissions(LocalContext.current) val toastMessage = stringResource(R.string.authorization_dialog_success_toast) - val requestPermission = - rememberLauncherForActivityResult(RequestAccess()) { hasAccess -> + val requestReadWritePermission = + rememberLauncherForActivityResult(RequestStorageReadWriteAccessFor(listOf(StoragePermissions.FileType.Document), StoragePermissions.CreatedBy.Self)) { hasAccess -> if (hasAccess) { scope.launch { scaffoldState.snackbarHostState.showSnackbar(toastMessage) @@ -84,13 +85,7 @@ fun AddFileToDownloadsScreen( TextButton( onClick = { openPermissionDialog = false - requestPermission.launch( - RequestAccess.Args( - action = Action.READ_AND_WRITE, - types = listOf(StoragePermissions.FileType.Document), - createdBy = StoragePermissions.CreatedBy.Self - ) - ) + requestReadWritePermission.launch() } ) { Text(stringResource(R.string.authorization_dialog_confirm_label)) @@ -106,9 +101,10 @@ fun AddFileToDownloadsScreen( } fun checkAndRequestStoragePermission(onSuccess: () -> Unit) { - val isGranted = permissions.hasAccess( - action = Action.READ_AND_WRITE, - types = listOf(StoragePermissions.FileType.Document), + val isGranted = context.hasStorageReadWriteAccessFor( + fileTypes = listOf( + StoragePermissions.FileType.Document + ), createdBy = StoragePermissions.CreatedBy.Self ) diff --git a/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddMediaScreen.kt b/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddMediaScreen.kt index 114c5612..3a5caf14 100644 --- a/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddMediaScreen.kt +++ b/sample/src/main/java/com/google/modernstorage/sample/mediastore/AddMediaScreen.kt @@ -16,6 +16,7 @@ package com.google.modernstorage.sample.mediastore import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.launch import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -44,8 +45,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import com.google.modernstorage.permissions.RequestAccess +import com.google.modernstorage.permissions.RequestStorageReadWriteAccessFor import com.google.modernstorage.permissions.StoragePermissions +import com.google.modernstorage.permissions.StoragePermissions.hasStorageReadWriteAccessFor import com.google.modernstorage.sample.Demos import com.google.modernstorage.sample.HomeRoute import com.google.modernstorage.sample.R @@ -60,11 +62,20 @@ fun AddMediaScreen(navController: NavController, viewModel: MediaStoreViewModel var openPermissionDialog by remember { mutableStateOf(false) } val scope = rememberCoroutineScope() + val context = LocalContext.current val scaffoldState = rememberScaffoldState() - val permissions = StoragePermissions(LocalContext.current) val toastMessage = stringResource(R.string.authorization_dialog_success_toast) - val requestPermission = - rememberLauncherForActivityResult(RequestAccess()) { hasAccess -> + val requestReadWritePermission = + rememberLauncherForActivityResult( + RequestStorageReadWriteAccessFor( + fileTypes = listOf( + StoragePermissions.FileType.Image, + StoragePermissions.FileType.Video, + StoragePermissions.FileType.Audio + ), + createdBy = StoragePermissions.CreatedBy.Self + ) + ) { hasAccess -> if (hasAccess) { scope.launch { scaffoldState.snackbarHostState.showSnackbar(toastMessage) @@ -80,13 +91,7 @@ fun AddMediaScreen(navController: NavController, viewModel: MediaStoreViewModel TextButton( onClick = { openPermissionDialog = false - requestPermission.launch( - RequestAccess.Args( - action = StoragePermissions.Action.READ_AND_WRITE, - types = listOf(StoragePermissions.FileType.Document), - createdBy = StoragePermissions.CreatedBy.Self - ) - ) + requestReadWritePermission.launch() } ) { Text(stringResource(R.string.authorization_dialog_confirm_label)) @@ -102,9 +107,8 @@ fun AddMediaScreen(navController: NavController, viewModel: MediaStoreViewModel } fun checkAndRequestStoragePermission(onSuccess: () -> Unit) { - val isGranted = permissions.hasAccess( - action = StoragePermissions.Action.READ_AND_WRITE, - types = listOf( + val isGranted = context.hasStorageReadWriteAccessFor( + fileTypes = listOf( StoragePermissions.FileType.Image, StoragePermissions.FileType.Video, StoragePermissions.FileType.Audio diff --git a/sample/src/main/java/com/google/modernstorage/sample/permissions/CheckPermissionScreen.kt b/sample/src/main/java/com/google/modernstorage/sample/permissions/CheckPermissionScreen.kt index 430c8ab0..1c2a0c2f 100644 --- a/sample/src/main/java/com/google/modernstorage/sample/permissions/CheckPermissionScreen.kt +++ b/sample/src/main/java/com/google/modernstorage/sample/permissions/CheckPermissionScreen.kt @@ -45,10 +45,11 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.google.modernstorage.permissions.StoragePermissions import com.google.modernstorage.permissions.StoragePermissions.Action import com.google.modernstorage.permissions.StoragePermissions.CreatedBy import com.google.modernstorage.permissions.StoragePermissions.FileType +import com.google.modernstorage.permissions.StoragePermissions.hasStorageReadAccessFor +import com.google.modernstorage.permissions.StoragePermissions.hasStorageReadWriteAccessFor import com.google.modernstorage.sample.Demos import com.google.modernstorage.sample.HomeRoute import com.google.modernstorage.sample.R @@ -57,7 +58,7 @@ import com.google.modernstorage.sample.R @ExperimentalFoundationApi @Composable fun CheckPermissionScreen(navController: NavController) { - val check = StoragePermissions(LocalContext.current) + val context = LocalContext.current Scaffold( topBar = { @@ -84,8 +85,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Image), CreatedBy.Self ) @@ -93,8 +93,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Image), CreatedBy.AllApps ) @@ -103,8 +102,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Image), CreatedBy.Self ) @@ -112,8 +110,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Image), CreatedBy.AllApps ) @@ -130,8 +127,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Video), CreatedBy.Self ) @@ -139,8 +135,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Video), CreatedBy.AllApps ) @@ -149,8 +144,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Video), CreatedBy.Self ) @@ -158,8 +152,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Video), CreatedBy.AllApps ) @@ -176,8 +169,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Audio), CreatedBy.Self ) @@ -185,8 +177,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Audio), CreatedBy.AllApps ) @@ -195,8 +186,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Audio), CreatedBy.Self ) @@ -204,8 +194,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Audio), CreatedBy.AllApps ) @@ -222,8 +211,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Document), CreatedBy.Self ) @@ -231,8 +219,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ, + enabled = context.hasStorageReadAccessFor( listOf(FileType.Document), CreatedBy.AllApps ) @@ -241,8 +228,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.Self, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Document), CreatedBy.Self ) @@ -250,8 +236,7 @@ fun CheckPermissionScreen(navController: NavController) { CreatedByLabel( action = Action.READ_AND_WRITE, createdBy = CreatedBy.AllApps, - enabled = check.hasAccess( - Action.READ_AND_WRITE, + enabled = context.hasStorageReadWriteAccessFor( listOf(FileType.Document), CreatedBy.AllApps ) @@ -268,24 +253,24 @@ fun CheckPermissionScreen(navController: NavController) { @Composable fun CreatedByLabel(action: Action, createdBy: CreatedBy, enabled: Boolean) { - Text( - buildAnnotatedString { - append(if (enabled) "✅ " else "❌ ") - when (action) { - Action.READ -> append(stringResource(R.string.demo_check_permission_read_label)) - Action.READ_AND_WRITE -> append(stringResource(R.string.demo_check_permission_read_and_write_label)) - } + val textContent = buildAnnotatedString { + append(if (enabled) "✅ " else "❌ ") + when (action) { + Action.READ -> append(stringResource(R.string.demo_check_permission_read_label)) + Action.READ_AND_WRITE -> append(stringResource(R.string.demo_check_permission_read_and_write_label)) + } - append(" ") - append(stringResource(R.string.demo_check_permission_created_by_label)) - append(" ") + append(" ") + append(stringResource(R.string.demo_check_permission_created_by_label)) + append(" ") - withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) { - when (createdBy) { - CreatedBy.Self -> append(stringResource(R.string.demo_check_permission_created_by_self_label)) - CreatedBy.AllApps -> append(stringResource(R.string.demo_check_permission_created_by_all_apps_label)) - } + withStyle(SpanStyle(fontWeight = FontWeight.SemiBold)) { + when (createdBy) { + CreatedBy.Self -> append(stringResource(R.string.demo_check_permission_created_by_self_label)) + CreatedBy.AllApps -> append(stringResource(R.string.demo_check_permission_created_by_all_apps_label)) } } - ) + } + + Text(textContent) } From 0234d400ac69abacacefc04663656c088e1f653f Mon Sep 17 00:00:00 2001 From: Yacine Rezgui Date: Wed, 20 Apr 2022 18:04:36 +0100 Subject: [PATCH 4/6] Upgrade dependencies --- versions.properties | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/versions.properties b/versions.properties index 45b95553..2622d33d 100644 --- a/versions.properties +++ b/versions.properties @@ -35,7 +35,7 @@ version.org.jacoco..org.jacoco.ant=0.8.3 version.okio=3.0.0 -version.kotlinx.coroutines=1.6.0 +version.kotlinx.coroutines=1.6.1 version.kotlin=1.6.10 @@ -43,7 +43,7 @@ version.junit.junit=4.13.2 version.google.android.material=1.5.0 -version.com.github.skydoves..landscapist-glide=1.5.0 +version.com.github.skydoves..landscapist-glide=1.5.1 version.androidx.test.uiautomator=2.2.0 @@ -55,7 +55,7 @@ version.androidx.test.ext.junit=1.1.3 version.androidx.test.espresso=3.4.0 -version.androidx.navigation-compose=2.4.1 +version.androidx.navigation-compose=2.4.2 version.androidx.lifecycle=2.4.1 @@ -65,6 +65,7 @@ version.androidx.compose.ui=1.1.1 version.androidx.compose.material=1.1.1 +## unused version.androidx.compose.compiler=1.1.1 version.androidx.appcompat=1.4.1 @@ -73,10 +74,10 @@ version.androidx.activity=1.4.0 plugin.org.jetbrains.dokka=1.6.10 -plugin.me.tylerbwong.gradle.metalava=0.2.1 +plugin.me.tylerbwong.gradle.metalava=0.2.3 plugin.com.vanniktech.maven.publish=0.19.0 -plugin.com.diffplug.spotless=6.3.0 +plugin.com.diffplug.spotless=6.4.2 -plugin.android=7.1.2 +plugin.android=7.1.3 From 74e5788a4f000e1b2e2be81f86f385c0d954b84f Mon Sep 17 00:00:00 2001 From: Yacine Rezgui Date: Wed, 20 Apr 2022 18:41:13 +0100 Subject: [PATCH 5/6] Revert dependencies management system --- bom/build.gradle.kts | 2 +- build.gradle.kts | 14 ++++++------- gradle/androidx.versions.toml | 11 ++++++++++ gradle/compose.versions.toml | 12 +++++++++++ gradle/libs.versions.toml | 12 +++++++++++ permissions/build.gradle.kts | 13 ++++++------ photopicker/build.gradle.kts | 10 ++++----- sample/build.gradle.kts | 38 +++++++++++++++++------------------ settings.gradle.kts | 14 ++++++++++--- storage/build.gradle.kts | 26 ++++++++++++------------ 10 files changed, 97 insertions(+), 55 deletions(-) create mode 100644 gradle/androidx.versions.toml create mode 100644 gradle/compose.versions.toml create mode 100644 gradle/libs.versions.toml diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index 6e97a1af..e2100dfd 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -8,7 +8,7 @@ dependencies { api(project(":permissions")) api(project(":photopicker")) api(project(":storage")) - api(Square.okio) + api(libs.okio) } } diff --git a/build.gradle.kts b/build.gradle.kts index 66afcd5a..475bcf9c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,8 +23,8 @@ buildscript { mavenCentral() } dependencies { - classpath(Android.tools.build.gradlePlugin) - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:_") + classpath("com.android.tools.build:gradle:7.1.3") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -32,11 +32,11 @@ buildscript { } plugins { - id("com.diffplug.spotless") - id("org.jetbrains.dokka") - id("me.tylerbwong.gradle.metalava") apply false - id("com.vanniktech.maven.publish") apply false - id("org.jetbrains.kotlin.plugin.parcelize") apply false + id("com.diffplug.spotless") version "6.4.2" + id("org.jetbrains.dokka") version "1.6.10" + id("me.tylerbwong.gradle.metalava") version "0.2.3" apply false + id("com.vanniktech.maven.publish") version "0.19.0" apply false + id("org.jetbrains.kotlin.plugin.parcelize") version "1.4.32" apply false } allprojects { diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml new file mode 100644 index 00000000..6a79fb97 --- /dev/null +++ b/gradle/androidx.versions.toml @@ -0,0 +1,11 @@ +[libraries] +corektx = { module = "androidx.core:core-ktx", version = "1.7.0" } +appcompat = { module = "androidx.appcompat:appcompat", version = "1.4.1" } +material = { module = "com.google.android.material:material", version = "1.5.0" } +lifecycleruntimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version = "2.4.1" } +junit = { module = "androidx.test.ext:junit", version = "1.1.3" } +junitktx = { module = "androidx.test.ext:junit-ktx", version = "1.1.3" } +espresso = { module = "androidx.test.espresso:espresso-core", version = "3.4.0" } +rules = { module = "androidx.test:rules", version = "1.4.0" } +uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.2.0" } +runner = { module = "androidx.test:runner", version = "1.4.0" } diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml new file mode 100644 index 00000000..dab9fcc5 --- /dev/null +++ b/gradle/compose.versions.toml @@ -0,0 +1,12 @@ +[versions] +current = "1.1.1" + +[libraries] +ui = { module = "androidx.compose.ui:ui", version.ref = "current" } +material = { module = "androidx.compose.material:material", version.ref = "current" } +icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "current" } +uitoolingpreview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "current" } +activity = { module = "androidx.activity:activity-compose", version = "1.4.0" } +navigation = { module = "androidx.navigation:navigation-compose", version = "2.4.2" } +uitooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "current" } +junit = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "current" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..d7faac8f --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,12 @@ +[versions] +kotlin = "1.6.10" + +[libraries] +stdlib-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } +coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version = "1.6.1" } + +okio = { module = "com.squareup.okio:okio", version = "3.0.0" } +glide = { module = "com.github.skydoves:landscapist-glide", version = "1.5.1" } + +junit = { module = "junit:junit", version = "4.13.2" } +robolectric = { module = "org.robolectric:robolectric", version = "4.7.3" } diff --git a/permissions/build.gradle.kts b/permissions/build.gradle.kts index d7b9c6d3..2e03717a 100644 --- a/permissions/build.gradle.kts +++ b/permissions/build.gradle.kts @@ -60,10 +60,11 @@ android { } dependencies { - implementation(AndroidX.core.ktx) - implementation(AndroidX.activity.ktx) - testImplementation(Testing.junit4) - testImplementation(Testing.robolectric) - androidTestImplementation(AndroidX.test.ext.junit) - androidTestImplementation(AndroidX.test.ext.junitKtx) + implementation(androidx.corektx) + implementation(androidx.appcompat) + implementation(androidx.material) + testImplementation(libs.junit) + testImplementation(libs.robolectric) + androidTestImplementation(androidx.junit) + androidTestImplementation(androidx.junitktx) } diff --git a/photopicker/build.gradle.kts b/photopicker/build.gradle.kts index a8666a05..3364dbcd 100644 --- a/photopicker/build.gradle.kts +++ b/photopicker/build.gradle.kts @@ -51,9 +51,9 @@ android { dependencies { - implementation(AndroidX.core.ktx) - implementation(AndroidX.activity.ktx) - testImplementation(Testing.junit4) - androidTestImplementation(AndroidX.test.ext.junit) - androidTestImplementation(AndroidX.test.ext.junitKtx) + implementation(androidx.corektx) + implementation(androidx.appcompat) + testImplementation(libs.junit) + androidTestImplementation(androidx.junit) + androidTestImplementation(androidx.junitktx) } diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 9b522e3e..d0d7536f 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -1,5 +1,3 @@ -import de.fayard.refreshVersions.core.versionFor - /* * Copyright 2021 Google LLC * @@ -60,7 +58,7 @@ android { } composeOptions { - kotlinCompilerExtensionVersion = versionFor(AndroidX.compose.compiler) + kotlinCompilerExtensionVersion = compose.versions.current.get() } packagingOptions { @@ -72,16 +70,16 @@ android { dependencies { - implementation(AndroidX.core.ktx) - implementation(AndroidX.appCompat) - implementation(Google.android.material) - implementation(AndroidX.compose.ui) - implementation(AndroidX.compose.material) - implementation(AndroidX.compose.material.icons.extended) - implementation(AndroidX.compose.ui.toolingPreview) - implementation(AndroidX.lifecycle.runtimeKtx) - implementation(AndroidX.activity.compose) - implementation(AndroidX.navigation.compose) + implementation(androidx.corektx) + implementation(androidx.appcompat) + implementation(androidx.material) + implementation(compose.ui) + implementation(compose.material) + implementation(compose.icons) + implementation(compose.uitoolingpreview) + implementation(androidx.lifecycleruntimektx) + implementation(compose.activity) + implementation(compose.navigation) /* * In a real world project you can use the BOM to import the different dependencies without needing @@ -94,14 +92,14 @@ dependencies { */ implementation(project(":permissions")) implementation(project(":photopicker")) - implementation(Square.okio) + implementation(libs.okio) implementation(project(":storage")) - implementation("com.github.skydoves:landscapist-glide:_") + implementation(libs.glide) - testImplementation(Testing.junit4) - androidTestImplementation(AndroidX.test.ext.junit) - androidTestImplementation(AndroidX.test.espresso.core) - androidTestImplementation(AndroidX.compose.ui.testJunit4) - debugImplementation(AndroidX.compose.ui.tooling) + testImplementation(libs.junit) + androidTestImplementation(androidx.junit) + androidTestImplementation(androidx.espresso) + androidTestImplementation(compose.junit) + debugImplementation(compose.uitooling) } diff --git a/settings.gradle.kts b/settings.gradle.kts index a7f7d83c..180a2bd1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,9 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -plugins { - // See https://jmfayard.github.io/refreshVersions - id("de.fayard.refreshVersions") version "0.40.1" +enableFeaturePreview("VERSION_CATALOGS") + +dependencyResolutionManagement { + versionCatalogs { + create("compose") { + from(files("gradle/compose.versions.toml")) + } + create("androidx") { + from(files("gradle/androidx.versions.toml")) + } + } } rootProject.name = "ModernStorage" diff --git a/storage/build.gradle.kts b/storage/build.gradle.kts index b699f232..2b4522a3 100644 --- a/storage/build.gradle.kts +++ b/storage/build.gradle.kts @@ -50,21 +50,21 @@ android { } dependencies { - implementation(Kotlin.stdlib.jdk7) - implementation(KotlinX.coroutines.android) - androidTestImplementation(KotlinX.coroutines.android) + implementation(libs.stdlib.jdk7) + implementation(libs.coroutines.android) + androidTestImplementation(libs.coroutines.android) - implementation(AndroidX.core.ktx) - implementation(AndroidX.appCompat) - implementation(Square.okio) + implementation(androidx.corektx) + implementation(androidx.appcompat) + implementation(libs.okio) - testImplementation(Testing.junit4) - androidTestImplementation(AndroidX.test.ext.junit) - androidTestImplementation(AndroidX.test.ext.junitKtx) - androidTestImplementation(AndroidX.test.espresso.core) - androidTestImplementation(AndroidX.test.rules) - androidTestImplementation(AndroidX.test.uiAutomator) - androidTestImplementation(AndroidX.test.runner) + testImplementation(libs.junit) + androidTestImplementation(androidx.junit) + androidTestImplementation(androidx.junitktx) + androidTestImplementation(androidx.espresso) + androidTestImplementation(androidx.rules) + androidTestImplementation(androidx.uiautomator) + androidTestImplementation(androidx.runner) } repositories { mavenCentral() From 1380a61e156dc8cc8e235ea9a7d8168632f6516f Mon Sep 17 00:00:00 2001 From: Yacine Rezgui Date: Wed, 20 Apr 2022 19:19:51 +0100 Subject: [PATCH 6/6] Fix dependencies imports --- .../modernstorage/permissions/RequestStorageAccessFor.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt b/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt index 72046ebe..72909211 100644 --- a/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt +++ b/permissions/src/main/java/com/google/modernstorage/permissions/RequestStorageAccessFor.kt @@ -20,9 +20,9 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import androidx.activity.result.contract.ActivityResultContract -import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.ACTION_REQUEST_PERMISSIONS -import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSIONS -import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.Companion.EXTRA_PERMISSION_GRANT_RESULTS +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.ACTION_REQUEST_PERMISSIONS +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSIONS +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions.EXTRA_PERMISSION_GRANT_RESULTS import androidx.core.content.ContextCompat abstract class RequestStorageAccessFor(