Add API to watch permission state

This commit is contained in:
MM20 2022-01-03 22:17:57 +01:00
parent b5005ee4ea
commit e9703ad778
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
12 changed files with 102 additions and 29 deletions

View File

@ -94,7 +94,7 @@ class CalendarEvent(
val results = mutableListOf<CalendarEvent>()
if (!query.isEmpty() && query.length < 3) return results
if (!LauncherPreferences.instance.searchCalendars) return listOf()
if (!permissionsManager.checkPermission(PermissionGroup.Calendar)) {
if (!permissionsManager.checkPermissionOnce(PermissionGroup.Calendar)) {
return emptyList()
}
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()

View File

@ -81,7 +81,7 @@ class Contact(
return mutableListOf()
}
val permissionsManager: PermissionsManager = get()
if (!permissionsManager.checkPermission(PermissionGroup.Contacts)) {
if (!permissionsManager.checkPermissionOnce(PermissionGroup.Contacts)) {
return mutableListOf()
}
val proj = arrayOf(

View File

@ -30,7 +30,7 @@ class LocalFileDeserializer(
) : SearchableDeserializer, KoinComponent {
override fun deserialize(serialized: String): Searchable? {
val permissionsManager: PermissionsManager = get()
if (!permissionsManager.checkPermission(
if (!permissionsManager.checkPermissionOnce(
PermissionGroup.ExternalStorage
)
) return null

View File

@ -5,7 +5,6 @@ import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.drawable.AdaptiveIconDrawable
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.location.Geocoder
import android.media.MediaMetadataRetriever
import android.media.ThumbnailUtils
@ -13,7 +12,6 @@ import android.os.Build
import android.provider.MediaStore
import android.text.format.DateUtils
import android.util.Size
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.database.getStringOrNull
import androidx.exifinterface.media.ExifInterface
@ -170,7 +168,7 @@ open class LocalFile(
if (!LauncherPreferences.instance.searchFiles) return results
if (query.isBlank()) return results
val permissionsManager: PermissionsManager = get()
if (!permissionsManager.checkPermission(
if (!permissionsManager.checkPermissionOnce(
PermissionGroup.ExternalStorage
)
) return results

View File

@ -35,7 +35,7 @@ android {
}
dependencies {
implementation(libs.kotlin.stdlib)
implementation(libs.bundles.kotlin)
implementation(libs.androidx.core)
implementation(libs.androidx.appcompat)

View File

@ -3,6 +3,7 @@ package de.mm20.launcher2.permissions
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
@ -11,11 +12,26 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import de.mm20.launcher2.ktx.checkPermission
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
interface PermissionsManager {
fun requestPermission(context: AppCompatActivity, permissionGroup: PermissionGroup)
fun checkPermission(permissionGroup: PermissionGroup): Boolean
/**
* 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 hasPermission(permissionGroup: PermissionGroup): Flow<Boolean>
}
enum class PermissionGroup {
@ -27,18 +43,43 @@ enum class PermissionGroup {
class PermissionsManagerImpl(
private val context: Context
): PermissionsManager {
) : PermissionsManager {
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)
)
override fun requestPermission(activity: AppCompatActivity, permissionGroup: PermissionGroup) {
when (permissionGroup) {
PermissionGroup.Calendar -> {
ActivityCompat.requestPermissions(activity, calendarPermissions, permissionGroup.ordinal)
ActivityCompat.requestPermissions(
activity,
calendarPermissions,
permissionGroup.ordinal
)
}
PermissionGroup.Location -> {
ActivityCompat.requestPermissions(activity, locationPermissions, permissionGroup.ordinal)
ActivityCompat.requestPermissions(
activity,
locationPermissions,
permissionGroup.ordinal
)
}
PermissionGroup.Contacts -> {
ActivityCompat.requestPermissions(activity, contactPermissions, permissionGroup.ordinal)
ActivityCompat.requestPermissions(
activity,
contactPermissions,
permissionGroup.ordinal
)
}
PermissionGroup.ExternalStorage -> {
if (isAtLeastApiLevel(Build.VERSION_CODES.R)) {
@ -58,7 +99,7 @@ class PermissionsManagerImpl(
}
}
override fun checkPermission(permissionGroup: PermissionGroup): Boolean {
override fun checkPermissionOnce(permissionGroup: PermissionGroup): Boolean {
return when (permissionGroup) {
PermissionGroup.Calendar -> {
calendarPermissions.all { context.checkPermission(it) }
@ -76,7 +117,30 @@ class PermissionsManagerImpl(
externalStoragePermissions.all { context.checkPermission(it) }
}
}
else -> false
}
}
override fun hasPermission(permissionGroup: PermissionGroup): Flow<Boolean> {
return when (permissionGroup) {
PermissionGroup.Calendar -> calendarPermissionState
PermissionGroup.Location -> locationPermissionState
PermissionGroup.Contacts -> contactsPermissionState
PermissionGroup.ExternalStorage -> externalStoragePermissionState
}
}
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
}
}

View File

@ -0,0 +1,19 @@
package de.mm20.launcher2.ui.base
import androidx.appcompat.app.AppCompatActivity
import de.mm20.launcher2.permissions.PermissionsManager
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
abstract class BaseActivity : AppCompatActivity(), KoinComponent {
private val permissionsManager: PermissionsManager by inject()
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
}

View File

@ -1,6 +1,5 @@
package de.mm20.launcher2.ui.legacy.activity
import android.Manifest
import android.animation.AnimatorSet
import android.animation.LayoutTransition
import android.animation.ObjectAnimator
@ -25,15 +24,12 @@ import android.view.inputmethod.InputMethodManager
import android.widget.LinearLayout
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import androidx.core.animation.doOnEnd
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.*
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.afollestad.materialdialogs.LayoutMode
import com.afollestad.materialdialogs.MaterialDialog
@ -48,11 +44,11 @@ import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.ktx.isBrightColor
import de.mm20.launcher2.legacy.helper.ActivityStarter
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.transition.ChangingLayoutTransition
import de.mm20.launcher2.transition.OneShotLayoutTransition
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.databinding.ActivityLauncherBinding
import de.mm20.launcher2.ui.launcher.modals.EditFavoritesView
import de.mm20.launcher2.ui.launcher.modals.HiddenItemsView
@ -60,7 +56,6 @@ import de.mm20.launcher2.ui.launcher.search.SearchViewModel
import de.mm20.launcher2.ui.legacy.component.WidgetView
import de.mm20.launcher2.ui.legacy.helper.ThemeHelper
import de.mm20.launcher2.ui.settings.SettingsActivity
import de.mm20.launcher2.weather.WeatherViewModel
import de.mm20.launcher2.widgets.Widget
import de.mm20.launcher2.widgets.WidgetType
import de.mm20.launcher2.widgets.WidgetViewModel
@ -71,7 +66,7 @@ import java.util.*
import kotlin.math.roundToInt
class LauncherActivity : AppCompatActivity() {
class LauncherActivity : BaseActivity() {
/**
* True if the search result list is visible

View File

@ -47,7 +47,7 @@ class CalendarView : FrameLayout, KoinComponent {
visibility = View.GONE
return@observe
}
if (it.isEmpty() && LauncherPreferences.instance.searchCalendars && !permissionsManager.checkPermission(
if (it.isEmpty() && LauncherPreferences.instance.searchCalendars && !permissionsManager.checkPermissionOnce(
PermissionGroup.Calendar
)
) {

View File

@ -6,7 +6,6 @@ import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.SearchView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
@ -47,7 +46,7 @@ class ContactView : FrameLayout, KoinComponent {
visibility = View.GONE
return@observe
}
if (it.isEmpty() && LauncherPreferences.instance.searchContacts && !permissionsManager.checkPermission(
if (it.isEmpty() && LauncherPreferences.instance.searchContacts && !permissionsManager.checkPermissionOnce(
PermissionGroup.Contacts
)
) {

View File

@ -45,7 +45,7 @@ class FileView : FrameLayout, KoinComponent {
visibility = View.GONE
return@observe
}
if (it.isEmpty() && !permissionsManager.checkPermission(
if (it.isEmpty() && !permissionsManager.checkPermissionOnce(
PermissionGroup.ExternalStorage
)
) {

View File

@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
@ -20,16 +19,15 @@ import de.mm20.launcher2.licenses.OpenSourceLicenses
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.LegacyLauncherTheme
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.legacy.helper.ThemeHelper
import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.screens.settings.SettingsLicenseScreen
import de.mm20.launcher2.ui.settings.about.AboutScreen
import de.mm20.launcher2.ui.settings.appearance.AppearanceScreen
import de.mm20.launcher2.ui.settings.license.LicenseScreen
import de.mm20.launcher2.ui.settings.main.MainScreen
import de.mm20.launcher2.ui.settings.weather.WeatherScreen
class SettingsActivity : AppCompatActivity() {
class SettingsActivity : BaseActivity() {
private val viewModel: SettingsActivityVM by viewModels()