hi all! how can I avoid Android to request the pe...
# android
m
hi all! how can I avoid Android to request the permissions again when the phone is rotated?
😶 2
for example in my
MainActivity
I have this:
Copy code
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PSPDFKitAssignmentTheme {
                MainScreen()
            }
        }
    }

    @OptIn(ExperimentalAnimationApi::class)
    @SuppressLint("UnusedMaterialScaffoldPaddingParameter")
    @Composable
    fun MainScreen(
        mainViewModel: MainViewModel = viewModel(),
        savePDFViewModel: SavePDFViewModel = viewModel()
    ) {
        val navController = rememberAnimatedNavController()
        val navBackStackEntry by navController.currentBackStackEntryAsState()

        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.MANAGE_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            OpenDownloadsFolderAsLaunchedEffect()
        } else {
            RequestPermissions()
        }
        Scaffold(...
But the problem is that of course, when I rotate the device, the activity gets recreated again and if the permissions are already granted the method
OpenDownloadsFolderAsLaunchedEffect()
is called again and that breaks the app
j
@Manuel Lorenzo Hi, can you show the implementation of
OpenDownloadsFolderAsLaunchedEffect
?
m
Copy code
LaunchedEffect(true) {
    mainViewModel.openFolder(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS))
}
@Jan Starczewski
j
oh well sorry
so
m
no worries, thanks for the help by the way 🙂
j
@Manuel Lorenzo 1. Long story short effects do not survive configuration changes and after the change they will be relaunched, what you can do to quickly fix the issue is to save state of a
boolean
indicating whether you made a certain job inside effect or not with
rememverSaveable
so It will not be launched on configuration change
Copy code
val wasLaunched = rememberSaveable { mutableStateOf(false) }
            if (wasLaunched.value.not()) {
                LaunchedEffect(Unit) {
                    // do your calls
                    // mainViewModel.openFolder(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS))
                    // change value
                    wasLaunched.value = true
                }
            }
2. Other solution is to move the state into viewModel assuming it is properly scoped, so I also survives configuration changes, and then you can relay on the value of it to launch an effect or not
m
and how can I move it there? I’ve been trying to find out how to handle the permissions there but I can’t find any example
j
What do you mean by “how can I move it there”?
m
how to handle the permissions in the view model
j
Do not handle permission in the view model, just keep some kind of information (state) that you have already handled it inside view or not
I can try to provide a full example, but just give me some time
m
oh OK thanks!
j
@Manuel Lorenzo but I am wondering, you are asking how to keep the state in ViewModel, or how to handle permissions?
m
to be honest, I have a huge mess here, and in particular two problems: • I need to show a list of downloaded PDFs, and I need to request the permissions based on the API level. When the permissions are granted and I get back to my app’s screen, the documents aren’t there so I need to close and open the app again. • When I have my app and I rotate the
OpenDownloadsFolderAsLaunchedEffect
is called again, which kind of restarts the data structure that holds the list of documents of the app, and thus the state isn’t saved
j
Allright, and when you get back to the app it crashes?
m
no, it just doesn’t show anything
j
right, so as I understand the problem is only the fact that the folder is being opened twice as you mentioned, yes?
because of the second launch
m
yeah, I would consider the second problem the most important one actually
Copy code
@Composable
    fun MainScreen(
        mainViewModel: MainViewModel = viewModel(),
        savePDFViewModel: SavePDFViewModel = viewModel()
    ) {
        val navController = rememberAnimatedNavController()
        val navBackStackEntry by navController.currentBackStackEntryAsState()

        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.MANAGE_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            OpenDownloadsFolderAsLaunchedEffect()
        } else {
            RequestPermissions()
        }
        Scaffold(...
As you can see here it’s my MainScreen and of course, when rotated the
checkSelfPermission
is called again because the activity was rotated
Copy code
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PSPDFKitAssignmentTheme {
                MainScreen()
            }
        }
    }
Not the best way I guess
j
So as I said, you can prevent the launch with saved value by
rememberSaveable
but to be honest it that case it may not be the best thing to do, because after process death when app is opened again it will prevent downloading the pdfs, I think we should go with viewModel approach
Could you please send me the impl of
openFolder
function ?
m
Copy code
fun openFolder(selectedItem: File) {
        viewModelScope.launch(ioDispatcher) {
            val filesList = getFilesList(selectedItem)
            _documentListLiveData.postValue(buildDocumentListToShow(selectedItem, filesList))
        }
    }
j
I think the easiest solution for you is to check whether your files represented by
_documentListLiveData
is has an empty value before downloading it with
getFilesList
or expose other live data like
canOpenFolder
that can represent the state informing whether you should open the folder or not.
Copy code
@Composable
fun OpenDownloadsFolderAsLaunchedEffectIfClosed(isClosed: Boolean) {
    if (isClosed) {
        LaunchedEffect(Unit) {
            // do your calls
            // mainViewModel.openFolder(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS))
        }
    }
}
And as the value of
isClosed
use the value stored in your viewModel
@Manuel Lorenzo But it all depends on you what you try to achieve, adding a simple check whether opening a folder is necessary would fix the issue with your crash on configuration change, because the viewModel will survive it and I it wont survive process death, so your folder then will be opened again assuming that’s the behaviour you want
m
that’s true indeed
thanks a lot for your help @Jan Starczewski
👍 1