Skip to content

Commit

Permalink
implement granular media permissions on android 13 (leonlatsch#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
leonlatsch authored Oct 6, 2022
1 parent 7be049f commit f17a6fb
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 28 deletions.
2 changes: 2 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
package="dev.leonlatsch.photok">

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package dev.leonlatsch.photok.gallery.ui.importing

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.net.Uri
Expand All @@ -25,8 +24,9 @@ import dev.leonlatsch.photok.backup.ui.RestoreBackupDialogFragment
import dev.leonlatsch.photok.databinding.DialogImportMenuBinding
import dev.leonlatsch.photok.other.REQ_PERM_IMPORT_PHOTOS
import dev.leonlatsch.photok.other.REQ_PERM_IMPORT_VIDEOS
import dev.leonlatsch.photok.other.REQ_PERM_RESTORE
import dev.leonlatsch.photok.other.extensions.show
import dev.leonlatsch.photok.permissions.getReadImagesPermission
import dev.leonlatsch.photok.permissions.getReadVideosPermission
import dev.leonlatsch.photok.uicomponnets.Chooser
import dev.leonlatsch.photok.uicomponnets.bindings.BindableBottomSheetDialogFragment
import pub.devrel.easypermissions.AfterPermissionGranted
Expand All @@ -53,7 +53,7 @@ class ImportMenuDialog :
.allowMultiple()
.requestCode(REQ_CONTENT_PHOTOS)
.permissionCode(REQ_PERM_IMPORT_PHOTOS)
.permission(Manifest.permission.READ_EXTERNAL_STORAGE)
.permission(getReadImagesPermission())
.show(this)

/**
Expand All @@ -69,20 +69,17 @@ class ImportMenuDialog :
.allowMultiple()
.requestCode(REQ_CONTENT_VIDEOS)
.permissionCode(REQ_PERM_IMPORT_VIDEOS)
.permission(Manifest.permission.READ_EXTERNAL_STORAGE)
.permission(getReadVideosPermission())
.show(this)

/**
* Start restoring a backup.
* Requests permission and shows [RestoreBackupDialogFragment].
*/
@AfterPermissionGranted(REQ_PERM_RESTORE)
fun startSelectBackup() = Chooser.Builder()
.message("Select Backup")
.mimeType("application/zip")
.requestCode(REQ_CONTENT_BACKUP)
.permissionCode(REQ_PERM_RESTORE)
.permission(Manifest.permission.READ_EXTERNAL_STORAGE)
.show(this)

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,5 @@ class SharedUrisStore {

fun getUris(): List<Uri> = sharedUris.toList()

fun getUriCount(): Int = sharedUris.size

fun clear() = sharedUris.clear()
}
37 changes: 25 additions & 12 deletions app/src/main/java/dev/leonlatsch/photok/main/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@

package dev.leonlatsch.photok.main.ui

import android.Manifest
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import dev.leonlatsch.photok.ApplicationState
import dev.leonlatsch.photok.R
Expand All @@ -30,9 +30,12 @@ import dev.leonlatsch.photok.gallery.ui.importing.ImportBottomSheetDialogFragmen
import dev.leonlatsch.photok.other.REQ_PERM_SHARED_IMPORT
import dev.leonlatsch.photok.other.extensions.getBaseApplication
import dev.leonlatsch.photok.other.extensions.setNavBarColorRes
import dev.leonlatsch.photok.permissions.getReadImagesPermission
import dev.leonlatsch.photok.permissions.getReadVideosPermission
import dev.leonlatsch.photok.settings.data.Config
import dev.leonlatsch.photok.uicomponnets.Dialogs
import dev.leonlatsch.photok.uicomponnets.bindings.BindableActivity
import kotlinx.coroutines.flow.collectLatest
import pub.devrel.easypermissions.AfterPermissionGranted
import pub.devrel.easypermissions.EasyPermissions
import javax.inject.Inject
Expand Down Expand Up @@ -63,15 +66,21 @@ class MainActivity : BindableActivity<ActivityMainBinding>(R.layout.activity_mai
super.onPostCreate(savedInstanceState)
dispatchIntent()

getBaseApplication().rawApplicationState.observe(this) {
if (it == ApplicationState.UNLOCKED && viewModel.getUriCountFromStore() > 0) {
val urisToImport = viewModel.consumeSharedUris()

confirmImport(urisToImport) {
startImportOfSharedUris(urisToImport)
lifecycleScope.launchWhenCreated {
viewModel.consumedUrisFromStore.collectLatest {
if (it.isNotEmpty()) {
confirmImport(it.size) {
startImportOfSharedUris()
}
}
}
}

getBaseApplication().rawApplicationState.observe(this) {
if (it == ApplicationState.UNLOCKED) {
viewModel.consumeSharedUris()
}
}
}

private fun dispatchIntent() {
Expand All @@ -86,12 +95,12 @@ class MainActivity : BindableActivity<ActivityMainBinding>(R.layout.activity_mai
}
}

private fun confirmImport(urisToImport: List<Uri>, onImportConfirmed: () -> Unit) {
private fun confirmImport(amount: Int, onImportConfirmed: () -> Unit) {
Dialogs.showConfirmDialog(
this,
String.format(
getString(R.string.import_sharted_question),
urisToImport.size
amount
)
) { _, _ ->
onImportConfirmed()
Expand All @@ -102,10 +111,13 @@ class MainActivity : BindableActivity<ActivityMainBinding>(R.layout.activity_mai
* Start importing after the overview of photos.
*/
@AfterPermissionGranted(REQ_PERM_SHARED_IMPORT)
fun startImportOfSharedUris(urisToImport: List<Uri>) {
fun startImportOfSharedUris() {
val urisToImport = viewModel.consumedUrisFromStore.value

if (EasyPermissions.hasPermissions(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
getReadImagesPermission(),
getReadVideosPermission()
)
) {
ImportBottomSheetDialogFragment(urisToImport).show(
Expand All @@ -117,7 +129,8 @@ class MainActivity : BindableActivity<ActivityMainBinding>(R.layout.activity_mai
this,
getString(R.string.import_permission_rationale),
REQ_PERM_SHARED_IMPORT,
Manifest.permission.READ_EXTERNAL_STORAGE
getReadImagesPermission(),
getReadVideosPermission()
)
}
}
Expand Down
10 changes: 5 additions & 5 deletions app/src/main/java/dev/leonlatsch/photok/main/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.net.Uri
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.leonlatsch.photok.gallery.ui.importing.SharedUrisStore
import dev.leonlatsch.photok.uicomponnets.bindings.ObservableViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject

/**
Expand All @@ -35,13 +36,12 @@ class MainViewModel @Inject constructor(
private val sharedUrisStore: SharedUrisStore,
) : ObservableViewModel(app) {

val consumedUrisFromStore = MutableStateFlow(emptyList<Uri>())

fun addUriToSharedUriStore(uri: Uri) = sharedUrisStore.safeAddUri(uri)

fun consumeSharedUris(): List<Uri> {
val sharedUris = sharedUrisStore.getUris()
fun consumeSharedUris() {
consumedUrisFromStore.value = sharedUrisStore.getUris()
sharedUrisStore.clear()
return sharedUris
}

fun getUriCountFromStore(): Int = sharedUrisStore.getUriCount()
}
1 change: 0 additions & 1 deletion app/src/main/java/dev/leonlatsch/photok/other/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,4 @@ const val INTENT_PHOTO_ID = "intent.photo.id"
const val REQ_PERM_IMPORT_PHOTOS = 10
const val REQ_PERM_IMPORT_VIDEOS = 11
const val REQ_PERM_EXPORT = 12
const val REQ_PERM_RESTORE = 13
const val REQ_PERM_SHARED_IMPORT = 14
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2020-2022 Leon Latsch
*
* 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
*
* http://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 dev.leonlatsch.photok.permissions

import android.Manifest
import android.os.Build

fun getReadVideosPermission() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_VIDEO
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}

fun getReadImagesPermission() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ class Chooser {
* Or request the [permission]
*/
fun show(fragment: Fragment) {
if (EasyPermissions.hasPermissions(fragment.requireContext(), permission)) {
if (permission == null || EasyPermissions.hasPermissions(
fragment.requireContext(),
permission
)
) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = mimeType
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple)
Expand Down

0 comments on commit f17a6fb

Please sign in to comment.