1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.jetpackcamera.permissions 17 18 import android.Manifest 19 import androidx.lifecycle.ViewModel 20 import com.google.accompanist.permissions.ExperimentalPermissionsApi 21 import com.google.accompanist.permissions.MultiplePermissionsState 22 import com.google.accompanist.permissions.isGranted 23 import com.google.accompanist.permissions.shouldShowRationale 24 import dagger.assisted.Assisted 25 import dagger.assisted.AssistedFactory 26 import dagger.assisted.AssistedInject 27 import dagger.hilt.android.lifecycle.HiltViewModel 28 import kotlin.collections.removeFirst as ktRemoveFirst // alias must be used now. see https://issuetracker.google.com/348683480 29 import kotlinx.coroutines.flow.MutableStateFlow 30 import kotlinx.coroutines.flow.StateFlow 31 import kotlinx.coroutines.flow.asStateFlow 32 import kotlinx.coroutines.flow.update 33 34 /** 35 * A [ViewModel] for [PermissionsScreen]] 36 */ 37 @OptIn(ExperimentalPermissionsApi::class) 38 @HiltViewModel(assistedFactory = PermissionsViewModel.Factory::class) 39 class PermissionsViewModel @AssistedInject constructor( 40 @Assisted permissionStates: MultiplePermissionsState 41 ) : ViewModel() { 42 @AssistedFactory 43 interface Factory { createnull44 fun create(runtimeArg: MultiplePermissionsState): PermissionsViewModel 45 } 46 47 private val permissionQueue = mutableListOf<PermissionEnum>() 48 49 init { 50 permissionQueue.addAll(getRequestablePermissions(permissionStates)) 51 } 52 53 private val _permissionsUiState: MutableStateFlow<PermissionsUiState> = 54 MutableStateFlow(getCurrentPermission()) 55 val permissionsUiState: StateFlow<PermissionsUiState> = _permissionsUiState.asStateFlow() 56 getCurrentPermissionnull57 private fun getCurrentPermission(): PermissionsUiState { 58 return if (permissionQueue.isEmpty()) { 59 PermissionsUiState.AllPermissionsGranted 60 } else { 61 PermissionsUiState.PermissionsNeeded(permissionQueue.first()) 62 } 63 } 64 dismissPermissionnull65 fun dismissPermission() { 66 if (permissionQueue.isNotEmpty()) { 67 permissionQueue.ktRemoveFirst() 68 } 69 _permissionsUiState.update { 70 (getCurrentPermission()) 71 } 72 } 73 } 74 75 /** 76 * 77 * Provides a set of [PermissionEnum] representing the permissions that can still be requested. 78 * Permissions that can be requested are: 79 * - mandatory permissions that have not been granted 80 * - optional permissions that have not yet been denied by the user 81 */ 82 @OptIn(ExperimentalPermissionsApi::class) getRequestablePermissionsnull83fun getRequestablePermissions( 84 permissionStates: MultiplePermissionsState 85 ): MutableSet<PermissionEnum> { 86 val unGrantedPermissions = mutableSetOf<PermissionEnum>() 87 for (permission in permissionStates.permissions) { 88 // camera is always required 89 if (!permission.status.isGranted && permission.permission == Manifest.permission.CAMERA) { 90 unGrantedPermissions.add(PermissionEnum.CAMERA) 91 } 92 // audio is optional 93 else if ((!permission.status.shouldShowRationale && !permission.status.isGranted) && 94 permission.permission == 95 Manifest.permission.RECORD_AUDIO 96 ) { 97 unGrantedPermissions.add(PermissionEnum.RECORD_AUDIO) 98 } 99 } 100 return unGrantedPermissions 101 } 102