• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
getRequestablePermissionsnull83 fun 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