Skip to content

Commit

Permalink
fix: [ANDROAPP-6764] Review MEDIA permissions to comply with Google P…
Browse files Browse the repository at this point in the history
…lay policy
  • Loading branch information
Balcan committed Jan 23, 2025
1 parent 64d5a7d commit 757199b
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 214 deletions.
3 changes: 0 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<uses-feature
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ fun provideParameterSelectorItem(
resources = resources,
focusManager = focusManager,
onNextClicked = onNextClicked,
onFileSelected = {
/*Not supported for search*/
},
)
},
status = status,
Expand Down
2 changes: 0 additions & 2 deletions form/src/main/java/org/dhis2/form/model/UiEventType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package org.dhis2.form.model

enum class UiEventType {
REQUEST_LOCATION_BY_MAP,
ADD_PICTURE,
ADD_FILE,
OPEN_FILE,
SHARE_IMAGE,
}
9 changes: 9 additions & 0 deletions form/src/main/java/org/dhis2/form/ui/Form.kt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ fun Form(
onNextSection,
)
},
onFileSelected = { path ->
intentHandler.invoke(
FormIntent.OnStoreFile(
uid = fieldUiModel.uid,
filePath = path,
valueType = fieldUiModel.valueType,
),
)
},
)
}
}
Expand Down
189 changes: 0 additions & 189 deletions form/src/main/java/org/dhis2/form/ui/FormView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package org.dhis2.form.ui
import android.Manifest
import android.app.Activity.RESULT_OK
import android.content.ActivityNotFoundException
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
Expand All @@ -15,7 +13,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
Expand All @@ -28,12 +25,7 @@ import androidx.fragment.app.viewModels
import androidx.paging.compose.collectAsLazyPagingItems
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.journeyapps.barcodescanner.ScanOptions
import org.dhis2.commons.ActivityResultObservable
import org.dhis2.commons.ActivityResultObserver
import org.dhis2.commons.Constants
import org.dhis2.commons.bindings.getFileFrom
import org.dhis2.commons.bindings.getFileFromGallery
import org.dhis2.commons.bindings.rotateImage
import org.dhis2.commons.data.FileHandler
import org.dhis2.commons.data.FormFileProvider
import org.dhis2.commons.date.DateUtils
Expand Down Expand Up @@ -77,7 +69,6 @@ import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialog
import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialogUiModel
import org.dhis2.ui.dialogs.bottomsheet.DialogButtonStyle
import org.dhis2.ui.dialogs.bottomsheet.FieldWithIssue
import org.hisp.dhis.android.core.arch.helpers.FileResourceDirectoryHelper
import org.hisp.dhis.android.core.common.ValueType
import org.hisp.dhis.android.core.common.ValueTypeRenderingType
import org.hisp.dhis.android.core.event.EventStatus
Expand Down Expand Up @@ -131,91 +122,6 @@ class FormView : Fragment() {
}
}

private val requestCameraPermissions =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
if (it.values.all { isGranted -> isGranted }) {
showAddImageOptions()
(context as ActivityResultObservable?)?.subscribe(object : ActivityResultObserver {
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?,
) {
if (resultCode != RESULT_OK) {
showAddImageOptions()
}
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray,
) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
showAddImageOptions()
}
}
})
} else {
Toast.makeText(
requireContext(),
requireContext().getString(R.string.camera_permission_denied),
Toast.LENGTH_LONG,
).show()
}
}

private val takePicture =
registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) {
val imageFile = File(
FileResourceDirectoryHelper.getFileResourceDirectory(requireContext()),
TEMP_FILE,
).rotateImage(requireContext())
onSavePicture?.invoke(imageFile.path)

viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
} else {
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
}
}

private val pickImage =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
if (activityResult.resultCode == RESULT_OK) {
getFileFromGallery(requireContext(), activityResult.data?.data)?.also { file ->
onSavePicture?.invoke(file.path)
}
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
} else {
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
}
}

private val pickFile =
registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
if (uri != null) {
getFileFrom(requireContext(), uri)?.also { file ->
onSavePicture?.invoke(file.path)
}
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
} else {
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
}
}

private val requestStoragePermissions =
registerForActivityResult(
ActivityResultContracts.RequestPermission(),
Expand Down Expand Up @@ -245,21 +151,6 @@ class FormView : Fragment() {
private lateinit var formSectionMapper: FormSectionMapper
var scrollCallback: ((Boolean) -> Unit)? = null
private var displayConfErrors = true
private var onSavePicture: ((String) -> Unit)? = null

private val storagePermissions = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
)

@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
private val storagePermissions33 = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.READ_MEDIA_AUDIO,
Manifest.permission.READ_MEDIA_VIDEO,
)

private val fileHandler = FileHandler()

Expand Down Expand Up @@ -553,9 +444,7 @@ class FormView : Fragment() {
is RecyclerViewUiEvents.DisplayQRCode -> displayQRImage(uiEvent)
is RecyclerViewUiEvents.ScanQRCode -> requestQRScan(uiEvent)
is RecyclerViewUiEvents.OpenOrgUnitDialog -> showOrgUnitDialog(uiEvent)
is RecyclerViewUiEvents.AddImage -> requestAddImage(uiEvent)
is RecyclerViewUiEvents.OpenFile -> openFile(uiEvent)
is RecyclerViewUiEvents.OpenFileSelector -> openFileSelector(uiEvent)
is RecyclerViewUiEvents.OpenChooserIntent -> openChooserIntent(uiEvent)
is RecyclerViewUiEvents.SelectPeriod -> showPeriodDialog(uiEvent)
}
Expand Down Expand Up @@ -699,84 +588,6 @@ class FormView : Fragment() {
)
}

private fun requestAddImage(event: RecyclerViewUiEvents.AddImage) {
onSavePicture = { picture ->
intentHandler(
FormIntent.OnStoreFile(
event.uid,
picture,
ValueType.IMAGE,
),
)
}
requestCameraPermissions.launch(permissions())
}

private fun permissions() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
storagePermissions33
} else {
storagePermissions
}

private fun showAddImageOptions() {
val options = arrayOf<CharSequence>(
requireContext().getString(R.string.take_photo),
requireContext().getString(R.string.from_gallery),
requireContext().getString(R.string.cancel),
)
MaterialAlertDialogBuilder(requireActivity(), R.style.MaterialDialog)
.setTitle(requireContext().getString(R.string.select_option))
.setOnCancelListener {
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
}
.setItems(options) { dialog: DialogInterface, item: Int ->
run {
when (options[item]) {
requireContext().getString(R.string.take_photo) -> {
val photoUri = FileProvider.getUriForFile(
requireContext(),
FormFileProvider.fileProviderAuthority,
File(
FileResourceDirectoryHelper.getFileResourceDirectory(
requireContext(),
),
TEMP_FILE,
),
)
takePicture.launch(photoUri)
}

requireContext().getString(R.string.from_gallery) -> {
pickImage.launch(Intent(Intent.ACTION_PICK).apply { type = "image/*" })
}

requireContext().getString(R.string.cancel) -> {
viewModel.getFocusedItemUid()?.let {
viewModel.submitIntent(FormIntent.OnAddImageFinished(it))
}
}
}
dialog.dismiss()
}
}
.show()
}

private fun openFileSelector(event: RecyclerViewUiEvents.OpenFileSelector) {
onSavePicture = { file ->
intentHandler(
FormIntent.OnStoreFile(
event.field.uid,
file,
event.field.valueType,
),
)
}
pickFile.launch("*/*")
}

private fun openFile(event: RecyclerViewUiEvents.OpenFile) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
downloadFile(event.field.displayName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.dhis2.form.ui.dialog

import android.content.Context
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import org.dhis2.form.R
import org.hisp.dhis.mobile.ui.designsystem.component.BottomSheetShell
import org.hisp.dhis.mobile.ui.designsystem.theme.Spacing

@Composable
internal fun ImagePickerOptionsDialog(
showImageOptions: Boolean,
onDismiss: () -> Unit,
onTakePicture: (Context) -> Unit,
onSelectFromGallery: () -> Unit,
) {
AnimatedVisibility(
visible = showImageOptions,
enter = slideInVertically() + fadeIn(),
exit = slideOutVertically() + fadeOut(),
) {
BottomSheetShell(
title = stringResource(R.string.select_option),
onDismiss = onDismiss,
content = {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = Spacing.Spacing16,
vertical = Spacing.Spacing24,
),
verticalArrangement = spacedBy(Spacing.Spacing16),
) {
val context = LocalContext.current
Box(
modifier = Modifier
.fillMaxWidth()
.clickable {
onDismiss()
onTakePicture(context)
},
contentAlignment = Alignment.CenterStart,
) {
Text(stringResource(R.string.take_photo))
}

Box(
modifier = Modifier
.fillMaxWidth()
.clickable {
onDismiss()
onSelectFromGallery()
},
contentAlignment = Alignment.CenterStart,
) {
Text(stringResource(R.string.from_gallery))
}
}
},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,6 @@ sealed class RecyclerViewUiEvents {
val orgUnitSelectorScope: OrgUnitSelectorScope?,
) : RecyclerViewUiEvents()

data class AddImage(
val uid: String,
) : RecyclerViewUiEvents()

data class OpenFileSelector(
val field: FieldUiModel,
) : RecyclerViewUiEvents()

data class OpenFile(
val field: FieldUiModel,
) : RecyclerViewUiEvents()
Expand Down
Loading

0 comments on commit 757199b

Please sign in to comment.