Skip to content

Commit

Permalink
replace ManageExternalStorage with ReadMedia
Browse files Browse the repository at this point in the history
add Permission Handling
remove GMS and DependencyInfoBlock
  • Loading branch information
beradeep committed Jul 12, 2024
1 parent 15a4fef commit 1b670e7
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 64 deletions.
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ android {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
}

}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs> {
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"
tools:ignore="SelectedPhotoAccess" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
Expand Down
215 changes: 164 additions & 51 deletions app/src/main/java/com/bera/whitehole/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
package com.bera.whitehole.ui

import android.Manifest
import android.app.PendingIntent
import android.app.Activity
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 androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.animation.AnimatedContent
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.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.unit.dp
import androidx.core.content.ContextCompat
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
Expand All @@ -33,53 +45,143 @@ import com.bera.whitehole.ui.theme.AppTheme

class MainActivity : ComponentActivity() {

private var hasStoragePerm: Boolean = false
private val activityResultLauncher =
registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { _ ->
hasStoragePerm = Environment.isExternalStorageManager()
}
private val isSdkAbove33 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
private val permissionsToRequest = if (isSdkAbove33) {
arrayOf(
Manifest.permission.READ_MEDIA_IMAGES,
Manifest.permission.POST_NOTIFICATIONS
)
} else {
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE
)
}
private var hasPhotosPerm by mutableStateOf(false)

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

hasStoragePerm = Environment.isExternalStorageManager()
hasPhotosPerm = if (isSdkAbove33) {
ContextCompat.checkSelfPermission(
this@MainActivity,
Manifest.permission.READ_MEDIA_IMAGES
) == PackageManager.PERMISSION_GRANTED
} else {
ContextCompat.checkSelfPermission(
this@MainActivity,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
}
val startDestination =
if (Preferences.getLong(Preferences.channelId, 0L) == 0L) "onboard" else "main"
if (Preferences.getLong(
Preferences.channelId,
0L
) == 0L
) ScreenFlow.Onboarding.route else ScreenFlow.Main.route
setContent {
AppTheme {
val context = LocalContext.current
var hasNotificationPerm by remember {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mutableStateOf(
ContextCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
)
} else mutableStateOf(true)
}
val permissionLauncher =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isGranted ->
hasNotificationPerm = isGranted
}
)
LaunchedEffect(Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
val topNavController = rememberNavController()
NavHost(navController = topNavController, startDestination = startDestination) {
composable("onboard") {
composable(ScreenFlow.Onboarding.route) {
OnboardingPage(navController = topNavController)
}
composable("main") {
composable(ScreenFlow.Main.route) {
val viewModel: MainViewModel = screenScopedViewModel()
MainPage(viewModel)
val dialogQueue = viewModel.visiblePermissionDialogQueue

val multiplePermissionResultLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestMultiplePermissions(),
onResult = { perms ->
permissionsToRequest.forEach { permission ->
viewModel.onPermissionResult(
permission = permission,
isGranted = perms[permission] == true
)
}
if (isSdkAbove33) {
perms[Manifest.permission.READ_MEDIA_IMAGES]?.let { isGranted ->
hasPhotosPerm = isGranted
}
} else {
perms[Manifest.permission.READ_EXTERNAL_STORAGE]?.let { isGranted ->
hasPhotosPerm = isGranted
}
}
}
)
AnimatedContent(
targetState = !hasPhotosPerm,
label = "PermissionsPage"
) { showPerms ->
if (showPerms) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(40.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text(
text = "Permissions Required",
style = MaterialTheme.typography.headlineLarge
)
Spacer(modifier = Modifier.size(40.dp))
Text(
text = "This app requires permission to photos and notifications " +
"to work as intended. Please grant the permissions to continue."
)
Spacer(modifier = Modifier.size(40.dp))
Button(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
onClick = {
multiplePermissionResultLauncher.launch(
permissionsToRequest
)
},
shape = RoundedCornerShape(16.dp)
) {
Text(
text = "Grant Permissions".uppercase(),
style = MaterialTheme.typography.bodyLarge
)
}
}

dialogQueue
.reversed()
.forEach { permission ->
PermissionDialog(
permissionTextProvider = when (permission) {
Manifest.permission.READ_MEDIA_IMAGES -> {
PhotosPermissionTextProvider()
}

Manifest.permission.READ_EXTERNAL_STORAGE -> {
PhotosPermissionTextProvider()
}

else -> return@forEach
},
isPermanentlyDeclined = !shouldShowRequestPermissionRationale(
permission
),
onDismiss = viewModel::dismissDialog,
onOkClick = {
viewModel.dismissDialog()
multiplePermissionResultLauncher.launch(
arrayOf(permission)
)
},
onGoToAppSettingsClick = ::openAppSettings
)
}
} else {
MainPage(viewModel)
}
}
}
}
}
Expand All @@ -88,18 +190,29 @@ class MainActivity : ComponentActivity() {

override fun onResume() {
super.onResume()

if (!hasStoragePerm) {
val intent = Intent(
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
Uri.fromParts("package", packageName, null)
)
val pendingIntent = PendingIntent.getActivity(
this@MainActivity, 0, intent,
PendingIntent.FLAG_IMMUTABLE
)
val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent).build()
activityResultLauncher.launch(intentSenderRequest)
hasPhotosPerm = if (isSdkAbove33) {
ContextCompat.checkSelfPermission(
this@MainActivity,
Manifest.permission.READ_MEDIA_IMAGES
) == PackageManager.PERMISSION_GRANTED
} else {
ContextCompat.checkSelfPermission(
this@MainActivity,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
}
}
}

sealed class ScreenFlow(val route: String) {
data object Onboarding : ScreenFlow("onboard")
data object Main : ScreenFlow("main")
}


fun Activity.openAppSettings() {
Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null)
).also(::startActivity)
}
81 changes: 81 additions & 0 deletions app/src/main/java/com/bera/whitehole/ui/PermissionDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.bera.whitehole.ui

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp

@Composable
fun PermissionDialog(
permissionTextProvider: PermissionTextProvider,
isPermanentlyDeclined: Boolean,
onDismiss: () -> Unit,
onOkClick: () -> Unit,
onGoToAppSettingsClick: () -> Unit,
modifier: Modifier = Modifier
) {
AlertDialog(
onDismissRequest = onDismiss,
confirmButton = {
Column(
modifier = Modifier.fillMaxWidth()
) {
Divider()
Text(
text = if(isPermanentlyDeclined) {
"Grant permission"
} else {
"OK"
},
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.clickable {
if (isPermanentlyDeclined) {
onGoToAppSettingsClick()
} else {
onOkClick()
}
}
.padding(8.dp)
)
}
},
title = {
Text(text = "Permission required")
},
text = {
Text(
text = permissionTextProvider.getDescription(
isPermanentlyDeclined = isPermanentlyDeclined
)
)
},
modifier = modifier
)
}

interface PermissionTextProvider {
fun getDescription(isPermanentlyDeclined: Boolean): String
}

class PhotosPermissionTextProvider: PermissionTextProvider {
override fun getDescription(isPermanentlyDeclined: Boolean): String {
return if(isPermanentlyDeclined) {
"It seems you permanently declined photos permission. " +
"You can go to the app settings to grant it."
} else {
"This app needs access to your photos so that you can back " +
"them up to Telegram."
}
}
}
Loading

0 comments on commit 1b670e7

Please sign in to comment.