Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/src/main/java/com/getcode/Session.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import com.getcode.network.repository.hexEncodedString
import com.getcode.network.repository.toPublicKey
import com.getcode.solana.organizer.GiftCardAccount
import com.getcode.solana.organizer.Organizer
import com.getcode.ui.components.PermissionResult
import com.getcode.util.CurrencyUtils
import com.getcode.util.IntentUtils
import com.getcode.util.Kin
Expand Down Expand Up @@ -465,8 +466,8 @@ class Session @Inject constructor(
uiFlow.update { it.copy(isCameraScanEnabled = scanning) }
}

fun onCameraPermissionChanged(isGranted: Boolean) {
uiFlow.update { it.copy(isCameraPermissionGranted = isGranted) }
fun onCameraPermissionResult(result: PermissionResult) {
uiFlow.update { it.copy(isCameraPermissionGranted = result == PermissionResult.Granted) }
}

fun showBill(
Expand Down
68 changes: 57 additions & 11 deletions app/src/main/java/com/getcode/ui/components/PermissionCheck.kt
Original file line number Diff line number Diff line change
@@ -1,39 +1,85 @@
package com.getcode.ui.components

import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.getcode.ui.utils.getActivity

enum class PermissionResult {
Granted, Denied, ShouldShowRationale
}

typealias PermissionsLauncher = ManagedActivityResultLauncher<String, Boolean>

@Composable
fun getPermissionLauncher(onPermissionResult: (isGranted: Boolean) -> Unit) =
rememberLauncherForActivityResult(
fun getPermissionLauncher(
permission: String,
onPermissionResult: (result: PermissionResult) -> Unit
): PermissionsLauncher {
val context = LocalContext.current
val activity = context as Activity

val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
onPermissionResult(isGranted)
// This block will be triggered after the user chooses to grant or deny the permission
// and we can tell if the user permanently declines or if we need to show rational
val permissionPermanentlyDenied = !ActivityCompat.shouldShowRequestPermissionRationale(
activity, permission
) && !isGranted

when {
permissionPermanentlyDenied -> {
onPermissionResult(PermissionResult.ShouldShowRationale)
}
!isGranted -> onPermissionResult(PermissionResult.Denied)
else -> onPermissionResult(PermissionResult.Granted)
}
}

object PermissionCheck {
fun requestPermission(
context: Context,
return launcher
}

@Composable
fun rememberPermissionChecker(): PermissionChecker {
val context = LocalContext.current
return remember(context) {
PermissionChecker(context)
}
}

class PermissionChecker(private val context: Context) {
fun request(
permission: String,
shouldRequest: Boolean,
onPermissionResult: (isGranted: Boolean) -> Unit,
launcher: PermissionsLauncher
shouldRequest: Boolean = true,
launcher: PermissionsLauncher,
onPermissionResult: (result: PermissionResult) -> Unit = { },
) {
val activity = context.getActivity()

when (ContextCompat.checkSelfPermission(context, permission)) {
PackageManager.PERMISSION_GRANTED -> {
onPermissionResult(true)
onPermissionResult(PermissionResult.Granted)
}
PackageManager.PERMISSION_DENIED -> {
if (shouldRequest) {
launcher.launch(permission)
} else {
onPermissionResult(false)
if (activity != null) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
onPermissionResult(PermissionResult.ShouldShowRationale)
return
}
}
onPermissionResult(PermissionResult.Denied)
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/com/getcode/view/login/AccessKey.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,16 @@ import com.getcode.navigation.core.LocalCodeNavigator
import com.getcode.navigation.screens.LoginArgs
import com.getcode.theme.CodeTheme
import com.getcode.theme.White
import com.getcode.ui.utils.measured
import com.getcode.ui.components.SelectionContainer
import com.getcode.ui.components.ButtonState
import com.getcode.ui.components.Cloudy
import com.getcode.ui.components.CodeButton
import com.getcode.ui.components.PermissionCheck
import com.getcode.ui.components.PermissionResult
import com.getcode.ui.components.SelectionContainer
import com.getcode.ui.components.getPermissionLauncher
import com.getcode.ui.components.rememberPermissionChecker
import com.getcode.ui.components.rememberSelectionState
import com.getcode.ui.utils.addIf
import com.getcode.ui.utils.measured
import com.getcode.util.launchAppSettings

@OptIn(ExperimentalComposeUiApi::class)
Expand All @@ -80,8 +81,8 @@ fun AccessKey(
var isStoragePermissionGranted by remember { mutableStateOf(false) }
val isAccessKeyVisible = remember { MutableTransitionState(false) }

val onPermissionResult = { isSuccess: Boolean ->
isStoragePermissionGranted = isSuccess
val onPermissionResult = { result: PermissionResult ->
isStoragePermissionGranted = result == PermissionResult.Granted

if (!isStoragePermissionGranted) {
TopBarManager.showMessage(
Expand All @@ -96,7 +97,8 @@ fun AccessKey(
}
}

val launcher = getPermissionLauncher(onPermissionResult)
val launcher = getPermissionLauncher(Manifest.permission.WRITE_EXTERNAL_STORAGE, onPermissionResult)
val permissionChecker = rememberPermissionChecker()

if (isExportSeedRequested && isStoragePermissionGranted) {
viewModel.onSubmit(navigator, true)
Expand All @@ -109,10 +111,8 @@ fun AccessKey(
if (Build.VERSION.SDK_INT > 29) {
isStoragePermissionGranted = true
} else {
PermissionCheck.requestPermission(
context = context,
permissionChecker.request(
permission = Manifest.permission.WRITE_EXTERNAL_STORAGE,
shouldRequest = true,
onPermissionResult = onPermissionResult,
launcher = launcher
)
Expand Down
19 changes: 12 additions & 7 deletions app/src/main/java/com/getcode/view/login/CameraPermissionCheck.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
package com.getcode.view.login

import android.Manifest
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import com.getcode.App
import com.getcode.R
import com.getcode.manager.TopBarManager
import com.getcode.ui.components.PermissionCheck
import com.getcode.ui.components.PermissionResult
import com.getcode.ui.components.getPermissionLauncher
import com.getcode.ui.components.rememberPermissionChecker
import com.getcode.util.launchAppSettings

@Composable
fun cameraPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Unit): (Boolean) -> Unit {
val permissionChecker = rememberPermissionChecker()
val context = LocalContext.current
var permissionRequested by remember { mutableStateOf(false) }
val onPermissionError = {
Expand All @@ -25,18 +30,18 @@ fun cameraPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Un
)
)
}
val onPermissionResult = { isGranted: Boolean ->
val onPermissionResult = { result: PermissionResult ->
val isGranted = result == PermissionResult.Granted
onResult(isGranted)
if (!isGranted && permissionRequested && isShowError) {
onPermissionError()
}
Unit
}
val launcher = getPermissionLauncher(onPermissionResult)
val launcher = getPermissionLauncher(Manifest.permission.CAMERA, onPermissionResult)
val permissionCheck = { shouldRequest: Boolean ->
permissionRequested = shouldRequest
PermissionCheck.requestPermission(
context = context,
permissionChecker.request(
permission = Manifest.permission.CAMERA,
shouldRequest = shouldRequest,
onPermissionResult = onPermissionResult,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,37 @@ package com.getcode.view.login

import android.Manifest
import android.os.Build
import androidx.compose.runtime.*
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import com.getcode.App
import com.getcode.R
import com.getcode.manager.TopBarManager
import com.getcode.ui.components.PermissionCheck
import com.getcode.ui.components.PermissionResult
import com.getcode.ui.components.getPermissionLauncher
import com.getcode.ui.components.rememberPermissionChecker
import com.getcode.util.launchAppSettings

@Composable
fun notificationPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Unit): (Boolean) -> Unit {
fun notificationPermissionCheck(isShowError: Boolean = true, onResult: (Boolean) -> Unit): (shouldRequest: Boolean) -> Unit {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
notificationPermissionCheckApi33(isShowError, onResult)
} else {
notificationPermissionCheckApiLegacy(isShowError, onResult)
}
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
private fun notificationPermissionCheckApi33(
isShowError: Boolean = true,
onResult: (Boolean) -> Unit
): (shouldRequest: Boolean) -> Unit {
val context = LocalContext.current
val permissionChecker = rememberPermissionChecker()
var permissionRequested by remember { mutableStateOf(false) }
val onPermissionError = {
TopBarManager.showMessage(
Expand All @@ -26,29 +45,65 @@ fun notificationPermissionCheck(isShowError: Boolean = true, onResult: (Boolean)
)
)
}
val onPermissionResult = { isGranted: Boolean ->
val onPermissionResult = { result: PermissionResult ->
val isGranted = result == PermissionResult.Granted
onResult(isGranted)
if (!isGranted && permissionRequested && isShowError) {
onPermissionError()
}
Unit
}
val launcher = getPermissionLauncher(onPermissionResult)

val launcher = getPermissionLauncher(
Manifest.permission.POST_NOTIFICATIONS, onPermissionResult
)

val permissionCheck = { shouldRequest: Boolean ->
if (Build.VERSION.SDK_INT < 33) {
onPermissionResult(true)
onPermissionResult(PermissionResult.Granted)
} else {
permissionRequested = shouldRequest
PermissionCheck.requestPermission(
context = context,
permissionChecker.request(
permission = Manifest.permission.POST_NOTIFICATIONS,
shouldRequest = shouldRequest,
onPermissionResult = onPermissionResult,
launcher = launcher
)
}
}

return permissionCheck
}

@Composable
private fun notificationPermissionCheckApiLegacy(
isShowError: Boolean = true,
onResult: (Boolean) -> Unit
): (shouldRequest: Boolean) -> Unit {
val context = LocalContext.current
var permissionRequested by remember { mutableStateOf(false) }
val onPermissionError = {
TopBarManager.showMessage(
TopBarManager.TopBarMessage(
title = context.getString(R.string.action_allowPushNotifications),
message = context.getString(R.string.permissions_description_push),
type = TopBarManager.TopBarMessageType.ERROR,
secondaryText = context.getString(R.string.action_openSettings),
secondaryAction = { context.launchAppSettings() }
)
)
}
val onPermissionResult = { result: PermissionResult ->
val isGranted = result == PermissionResult.Granted
onResult(isGranted)
if (!isGranted && permissionRequested && isShowError) {
onPermissionError()
}
Unit
}

val permissionCheck = { _: Boolean ->
onPermissionResult(PermissionResult.Granted)
}

return permissionCheck
Expand Down
11 changes: 2 additions & 9 deletions app/src/main/java/com/getcode/view/login/SeedInput.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ fun SeedInput(
val focusManager = LocalFocusManager.current
val focusRequester = FocusRequester()

val context = LocalContext.current
val launcher = getPermissionLauncher {}
val notificationPermissionCheck = notificationPermissionCheck(isShowError = false) { }

Column(
modifier = Modifier
Expand Down Expand Up @@ -123,13 +122,7 @@ fun SeedInput(
}

if (dataState.isSuccess) {
PermissionCheck.requestPermission(
context = context,
permission = Manifest.permission.POST_NOTIFICATIONS,
shouldRequest = true,
onPermissionResult = {},
launcher = launcher
)
notificationPermissionCheck(true)
}

CodeButton(
Expand Down
Loading