Manage External Storage Permission - Android Studio - Compose

Manage External Storage Permission

Previously (before Android 11), we only needed to request the WRITE_EXTERNAL_STORAGE Permission to perform the storage-related tasks. However, for Android 11 and above, we must request the MANAGE_EXTERNAL_STORAGE Permission to perform storage-related tasks. Keep in mind that if you're 100% certain that your app requires this permission to function properly, then request it; otherwise, Google will not allow you to publish/update your app. For example, if your app is WhatsApp Status Saver, then you won't be allowed to use this permission as your app works with images and videos, not files.

After permission is granted, I'll input a name and create a folder with that name, just for example. You can do whatever you want instead.

>> Check For Java
>> Check For Kotlin
>> Check For Compose

Code:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!--Permissions for the Android below 11 (R).-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--Permission for the Android 11 (R) and above.-->
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

    <!--Android 10 only requires android:requestLegacyExternalStorage="true"-->

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:requestLegacyExternalStorage="true"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
    </application>

</manifest>

MainActivity.kt

package com.technifysoft.myapplication

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.Settings
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import com.technifysoft.myapplication.ui.theme.MyApplicationTheme
import java.io.File

class MainActivityCompose : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            MainUI()
        }
    }
}

const val TAG = "PERMISSION_TAG"

@Composable
fun MainUI() {
    val context = LocalContext.current
    var folderName by remember { mutableStateOf(TextFieldValue("")) }

    // Launcher for MANAGE_EXTERNAL_STORAGE intent (Android 11+)
    val storagePermissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (Environment.isExternalStorageManager()) {
                Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
                createFolder(context, folderName.text)
            } else {
                Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
            }
        }
    }

    // Launcher for normal permission request (Android < 11)
    val requestPermissionsLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestMultiplePermissions()
    ) { permissions ->
        val granted = permissions.values.all { it }
        if (granted) {
            Log.d(TAG, "Permissions granted")
            createFolder(context, folderName.text)
        } else {
            Toast.makeText(context, "Permission denied", Toast.LENGTH_SHORT).show()
        }
    }

    // UI
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(20.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        OutlinedTextField(
            value = folderName,
            onValueChange = { folderName = it },
            label = { Text("Folder Name...") },
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(16.dp))

        Button(
            onClick = {
                if (checkPermission(context)) {
                    Log.d(TAG, "Permission already granted, create folder")
                    createFolder(context, folderName.text)
                } else {
                    Log.d(TAG, "Permission not granted, requesting...")
                    requestPermission(
                        context,
                        storagePermissionLauncher,
                        requestPermissionsLauncher
                    )
                }
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Create Folder")
        }
    }
}

/*--------------------- Helper Functions ---------------------*/

fun createFolder(context: android.content.Context, folderName: String) {
    val trimmedName = folderName.trim()
    if (trimmedName.isEmpty()) {
        Toast.makeText(context, "Enter folder name", Toast.LENGTH_SHORT).show()
        return
    }

    val file = File("${Environment.getExternalStorageDirectory()}/$trimmedName")
    val created = file.mkdir()
    if (created) {
        Toast.makeText(context, "Folder Created: ${file.absolutePath}", Toast.LENGTH_LONG).show()
    } else {
        Toast.makeText(context, "Folder not created", Toast.LENGTH_SHORT).show()
    }
}

fun requestPermission(
    context: android.content.Context,
    storagePermissionLauncher: androidx.activity.result.ActivityResultLauncher<Intent>,
    requestPermissionsLauncher: androidx.activity.result.ActivityResultLauncher<Array<String>>
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        try {
            val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
            val uri = Uri.fromParts("package", context.packageName, null)
            intent.data = uri
            storagePermissionLauncher.launch(intent)
        } catch (e: Exception) {
            Log.e(TAG, "Error: ${e.message}")
            val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
            storagePermissionLauncher.launch(intent)
        }
    } else {
        requestPermissionsLauncher.launch(
            arrayOf(
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE
            )
        )
    }
}

fun checkPermission(context: android.content.Context): Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        Environment.isExternalStorageManager()
    } else {
        val write = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        val read = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE)
        write == PackageManager.PERMISSION_GRANTED && read == PackageManager.PERMISSION_GRANTED
    }
}

/**
 * GreetingPreview is a composable function for previewing the MainUI in Android Studio.
 * It is annotated with @Preview to enable live preview.
 *
 */
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        MainUI()
    }
}

Screenshots:

Manage External Storage Permission - Android Studio - Compose


Comments

Popular posts from this blog

Picture In Picture | Android Studio | Kotlin

Manage External Storage Permission - Android Studio - Kotlin

How to add AIDL folder | Android Studio