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() } }
Comments
Post a Comment