Reorganize and group modules

This commit is contained in:
MM20
2022-12-13 17:37:26 +01:00
parent bac24baad2
commit 3f8880a90a
995 changed files with 501 additions and 298 deletions
+1
View File
@@ -0,0 +1 @@
/build
+47
View File
@@ -0,0 +1,47 @@
plugins {
id("com.android.library")
id("kotlin-android")
}
android {
compileSdk = sdk.versions.compileSdk.get().toInt()
defaultConfig {
minSdk = sdk.versions.minSdk.get().toInt()
targetSdk = sdk.versions.targetSdk.get().toInt()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
namespace = "de.mm20.launcher2.permissions"
}
dependencies {
implementation(libs.bundles.kotlin)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)
implementation(libs.koin.android)
implementation(project(":core:ktx"))
implementation(project(":core:base"))
implementation(project(":core:crashreporter"))
}
View File
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,8 @@
package de.mm20.launcher2.permissions
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val permissionsModule = module {
single<PermissionsManager> { PermissionsManagerImpl(androidContext()) }
}
@@ -0,0 +1,226 @@
package de.mm20.launcher2.permissions
import android.Manifest
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.getSystemService
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.ktx.checkPermission
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.ktx.tryStartActivity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
interface PermissionsManager {
fun requestPermission(context: AppCompatActivity, permissionGroup: PermissionGroup)
/**
* Check if this permission is granted right now without receiving further updates
* about the granted state.
* @return true if the given permission group is fully granted
*/
fun checkPermissionOnce(permissionGroup: PermissionGroup): Boolean
fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
)
fun onResume() {
}
fun hasPermission(permissionGroup: PermissionGroup): Flow<Boolean>
/**
* Special function for the Notification listener to report its status.
* May not be called by anything else.
*/
fun reportNotificationListenerState(running: Boolean)
}
enum class PermissionGroup {
Calendar,
Location,
Contacts,
ExternalStorage,
Notifications,
AppShortcuts,
}
internal class PermissionsManagerImpl(
private val context: Context
) : PermissionsManager {
private val pendingPermissionRequests = mutableSetOf<PermissionGroup>()
private val calendarPermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.Calendar)
)
private val contactsPermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.Contacts)
)
private val externalStoragePermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.ExternalStorage)
)
private val locationPermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.Location)
)
private val notificationsPermissionState = MutableStateFlow(false)
private val appShortcutsPermissionState = MutableStateFlow(
checkPermissionOnce(PermissionGroup.AppShortcuts)
)
override fun requestPermission(context: AppCompatActivity, permissionGroup: PermissionGroup) {
when (permissionGroup) {
PermissionGroup.Calendar -> {
ActivityCompat.requestPermissions(
context,
calendarPermissions,
permissionGroup.ordinal
)
}
PermissionGroup.Location -> {
ActivityCompat.requestPermissions(
context,
locationPermissions,
permissionGroup.ordinal
)
}
PermissionGroup.Contacts -> {
ActivityCompat.requestPermissions(
context,
contactPermissions,
permissionGroup.ordinal
)
}
PermissionGroup.ExternalStorage -> {
if (isAtLeastApiLevel(Build.VERSION_CODES.R)) {
val intent =
Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).also {
it.data = Uri.parse("package:${context.packageName}")
}
context.tryStartActivity(intent)
pendingPermissionRequests.add(PermissionGroup.ExternalStorage)
} else {
ActivityCompat.requestPermissions(
context,
externalStoragePermissions,
permissionGroup.ordinal
)
}
}
PermissionGroup.Notifications -> {
try {
context.startActivity(Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS))
} catch (e: ActivityNotFoundException) {
CrashReporter.logException(e)
}
}
PermissionGroup.AppShortcuts -> {
context.tryStartActivity(Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS))
pendingPermissionRequests.add(PermissionGroup.AppShortcuts)
}
}
}
override fun checkPermissionOnce(permissionGroup: PermissionGroup): Boolean {
return when (permissionGroup) {
PermissionGroup.Calendar -> {
calendarPermissions.all { context.checkPermission(it) }
}
PermissionGroup.Location -> {
locationPermissions.all { context.checkPermission(it) }
}
PermissionGroup.Contacts -> {
contactPermissions.all { context.checkPermission(it) }
}
PermissionGroup.ExternalStorage -> {
if (isAtLeastApiLevel(Build.VERSION_CODES.R)) {
Environment.isExternalStorageManager()
} else {
externalStoragePermissions.all { context.checkPermission(it) }
}
}
PermissionGroup.Notifications -> {
notificationsPermissionState.value
}
PermissionGroup.AppShortcuts -> {
context.getSystemService<LauncherApps>()?.hasShortcutHostPermission() == true
}
}
}
override fun hasPermission(permissionGroup: PermissionGroup): Flow<Boolean> {
return when (permissionGroup) {
PermissionGroup.Calendar -> calendarPermissionState
PermissionGroup.Location -> locationPermissionState
PermissionGroup.Contacts -> contactsPermissionState
PermissionGroup.ExternalStorage -> externalStoragePermissionState
PermissionGroup.Notifications -> notificationsPermissionState
PermissionGroup.AppShortcuts -> appShortcutsPermissionState
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
val permissionGroup = PermissionGroup.values().getOrNull(requestCode) ?: return
val granted = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
when (permissionGroup) {
PermissionGroup.Calendar -> calendarPermissionState.value = granted
PermissionGroup.Location -> locationPermissionState.value = granted
PermissionGroup.Contacts -> contactsPermissionState.value = granted
PermissionGroup.ExternalStorage -> externalStoragePermissionState.value = granted
PermissionGroup.Notifications -> notificationsPermissionState.value = granted
PermissionGroup.AppShortcuts -> appShortcutsPermissionState.value = granted
}
}
override fun onResume() {
val iterator = pendingPermissionRequests.iterator()
while (iterator.hasNext()) {
when (iterator.next()) {
PermissionGroup.ExternalStorage -> {
externalStoragePermissionState.value =
checkPermissionOnce(PermissionGroup.ExternalStorage)
}
PermissionGroup.AppShortcuts -> {
appShortcutsPermissionState.value =
checkPermissionOnce(PermissionGroup.AppShortcuts)
}
else -> {}
}
iterator.remove()
}
}
override fun reportNotificationListenerState(running: Boolean) {
notificationsPermissionState.value = running
}
companion object {
private val calendarPermissions = arrayOf(Manifest.permission.READ_CALENDAR)
private val locationPermissions = arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
private val contactPermissions = arrayOf(Manifest.permission.READ_CONTACTS)
private val externalStoragePermissions = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
}