Add API to watch permission state
This commit is contained in:
parent
b5005ee4ea
commit
e9703ad778
@ -94,7 +94,7 @@ class CalendarEvent(
|
|||||||
val results = mutableListOf<CalendarEvent>()
|
val results = mutableListOf<CalendarEvent>()
|
||||||
if (!query.isEmpty() && query.length < 3) return results
|
if (!query.isEmpty() && query.length < 3) return results
|
||||||
if (!LauncherPreferences.instance.searchCalendars) return listOf()
|
if (!LauncherPreferences.instance.searchCalendars) return listOf()
|
||||||
if (!permissionsManager.checkPermission(PermissionGroup.Calendar)) {
|
if (!permissionsManager.checkPermissionOnce(PermissionGroup.Calendar)) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
|
val builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
|
||||||
|
|||||||
@ -81,7 +81,7 @@ class Contact(
|
|||||||
return mutableListOf()
|
return mutableListOf()
|
||||||
}
|
}
|
||||||
val permissionsManager: PermissionsManager = get()
|
val permissionsManager: PermissionsManager = get()
|
||||||
if (!permissionsManager.checkPermission(PermissionGroup.Contacts)) {
|
if (!permissionsManager.checkPermissionOnce(PermissionGroup.Contacts)) {
|
||||||
return mutableListOf()
|
return mutableListOf()
|
||||||
}
|
}
|
||||||
val proj = arrayOf(
|
val proj = arrayOf(
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class LocalFileDeserializer(
|
|||||||
) : SearchableDeserializer, KoinComponent {
|
) : SearchableDeserializer, KoinComponent {
|
||||||
override fun deserialize(serialized: String): Searchable? {
|
override fun deserialize(serialized: String): Searchable? {
|
||||||
val permissionsManager: PermissionsManager = get()
|
val permissionsManager: PermissionsManager = get()
|
||||||
if (!permissionsManager.checkPermission(
|
if (!permissionsManager.checkPermissionOnce(
|
||||||
PermissionGroup.ExternalStorage
|
PermissionGroup.ExternalStorage
|
||||||
)
|
)
|
||||||
) return null
|
) return null
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import android.content.Intent
|
|||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.graphics.drawable.AdaptiveIconDrawable
|
import android.graphics.drawable.AdaptiveIconDrawable
|
||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.location.Geocoder
|
import android.location.Geocoder
|
||||||
import android.media.MediaMetadataRetriever
|
import android.media.MediaMetadataRetriever
|
||||||
import android.media.ThumbnailUtils
|
import android.media.ThumbnailUtils
|
||||||
@ -13,7 +12,6 @@ import android.os.Build
|
|||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.util.Size
|
import android.util.Size
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
@ -170,7 +168,7 @@ open class LocalFile(
|
|||||||
if (!LauncherPreferences.instance.searchFiles) return results
|
if (!LauncherPreferences.instance.searchFiles) return results
|
||||||
if (query.isBlank()) return results
|
if (query.isBlank()) return results
|
||||||
val permissionsManager: PermissionsManager = get()
|
val permissionsManager: PermissionsManager = get()
|
||||||
if (!permissionsManager.checkPermission(
|
if (!permissionsManager.checkPermissionOnce(
|
||||||
PermissionGroup.ExternalStorage
|
PermissionGroup.ExternalStorage
|
||||||
)
|
)
|
||||||
) return results
|
) return results
|
||||||
|
|||||||
@ -35,7 +35,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.kotlin.stdlib)
|
implementation(libs.bundles.kotlin)
|
||||||
implementation(libs.androidx.core)
|
implementation(libs.androidx.core)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package de.mm20.launcher2.permissions
|
|||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
@ -11,11 +12,26 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import de.mm20.launcher2.ktx.checkPermission
|
import de.mm20.launcher2.ktx.checkPermission
|
||||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
interface PermissionsManager {
|
interface PermissionsManager {
|
||||||
fun requestPermission(context: AppCompatActivity, permissionGroup: PermissionGroup)
|
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 {
|
enum class PermissionGroup {
|
||||||
@ -27,18 +43,43 @@ enum class PermissionGroup {
|
|||||||
|
|
||||||
class PermissionsManagerImpl(
|
class PermissionsManagerImpl(
|
||||||
private val context: Context
|
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) {
|
override fun requestPermission(activity: AppCompatActivity, permissionGroup: PermissionGroup) {
|
||||||
when (permissionGroup) {
|
when (permissionGroup) {
|
||||||
PermissionGroup.Calendar -> {
|
PermissionGroup.Calendar -> {
|
||||||
ActivityCompat.requestPermissions(activity, calendarPermissions, permissionGroup.ordinal)
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
calendarPermissions,
|
||||||
|
permissionGroup.ordinal
|
||||||
|
)
|
||||||
}
|
}
|
||||||
PermissionGroup.Location -> {
|
PermissionGroup.Location -> {
|
||||||
ActivityCompat.requestPermissions(activity, locationPermissions, permissionGroup.ordinal)
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
locationPermissions,
|
||||||
|
permissionGroup.ordinal
|
||||||
|
)
|
||||||
}
|
}
|
||||||
PermissionGroup.Contacts -> {
|
PermissionGroup.Contacts -> {
|
||||||
ActivityCompat.requestPermissions(activity, contactPermissions, permissionGroup.ordinal)
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
contactPermissions,
|
||||||
|
permissionGroup.ordinal
|
||||||
|
)
|
||||||
}
|
}
|
||||||
PermissionGroup.ExternalStorage -> {
|
PermissionGroup.ExternalStorage -> {
|
||||||
if (isAtLeastApiLevel(Build.VERSION_CODES.R)) {
|
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) {
|
return when (permissionGroup) {
|
||||||
PermissionGroup.Calendar -> {
|
PermissionGroup.Calendar -> {
|
||||||
calendarPermissions.all { context.checkPermission(it) }
|
calendarPermissions.all { context.checkPermission(it) }
|
||||||
@ -76,7 +117,30 @@ class PermissionsManagerImpl(
|
|||||||
externalStoragePermissions.all { context.checkPermission(it) }
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
ui/src/main/java/de/mm20/launcher2/ui/base/BaseActivity.kt
Normal file
19
ui/src/main/java/de/mm20/launcher2/ui/base/BaseActivity.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
package de.mm20.launcher2.ui.legacy.activity
|
package de.mm20.launcher2.ui.legacy.activity
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.animation.AnimatorSet
|
import android.animation.AnimatorSet
|
||||||
import android.animation.LayoutTransition
|
import android.animation.LayoutTransition
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
@ -25,15 +24,12 @@ import android.view.inputmethod.InputMethodManager
|
|||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.*
|
import androidx.core.view.*
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.afollestad.materialdialogs.LayoutMode
|
import com.afollestad.materialdialogs.LayoutMode
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
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.isAtLeastApiLevel
|
||||||
import de.mm20.launcher2.ktx.isBrightColor
|
import de.mm20.launcher2.ktx.isBrightColor
|
||||||
import de.mm20.launcher2.legacy.helper.ActivityStarter
|
import de.mm20.launcher2.legacy.helper.ActivityStarter
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
|
||||||
import de.mm20.launcher2.preferences.LauncherPreferences
|
import de.mm20.launcher2.preferences.LauncherPreferences
|
||||||
import de.mm20.launcher2.transition.ChangingLayoutTransition
|
import de.mm20.launcher2.transition.ChangingLayoutTransition
|
||||||
import de.mm20.launcher2.transition.OneShotLayoutTransition
|
import de.mm20.launcher2.transition.OneShotLayoutTransition
|
||||||
import de.mm20.launcher2.ui.R
|
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.databinding.ActivityLauncherBinding
|
||||||
import de.mm20.launcher2.ui.launcher.modals.EditFavoritesView
|
import de.mm20.launcher2.ui.launcher.modals.EditFavoritesView
|
||||||
import de.mm20.launcher2.ui.launcher.modals.HiddenItemsView
|
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.component.WidgetView
|
||||||
import de.mm20.launcher2.ui.legacy.helper.ThemeHelper
|
import de.mm20.launcher2.ui.legacy.helper.ThemeHelper
|
||||||
import de.mm20.launcher2.ui.settings.SettingsActivity
|
import de.mm20.launcher2.ui.settings.SettingsActivity
|
||||||
import de.mm20.launcher2.weather.WeatherViewModel
|
|
||||||
import de.mm20.launcher2.widgets.Widget
|
import de.mm20.launcher2.widgets.Widget
|
||||||
import de.mm20.launcher2.widgets.WidgetType
|
import de.mm20.launcher2.widgets.WidgetType
|
||||||
import de.mm20.launcher2.widgets.WidgetViewModel
|
import de.mm20.launcher2.widgets.WidgetViewModel
|
||||||
@ -71,7 +66,7 @@ import java.util.*
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
|
||||||
class LauncherActivity : AppCompatActivity() {
|
class LauncherActivity : BaseActivity() {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the search result list is visible
|
* True if the search result list is visible
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class CalendarView : FrameLayout, KoinComponent {
|
|||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
return@observe
|
return@observe
|
||||||
}
|
}
|
||||||
if (it.isEmpty() && LauncherPreferences.instance.searchCalendars && !permissionsManager.checkPermission(
|
if (it.isEmpty() && LauncherPreferences.instance.searchCalendars && !permissionsManager.checkPermissionOnce(
|
||||||
PermissionGroup.Calendar
|
PermissionGroup.Calendar
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import android.util.AttributeSet
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.SearchView
|
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
@ -47,7 +46,7 @@ class ContactView : FrameLayout, KoinComponent {
|
|||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
return@observe
|
return@observe
|
||||||
}
|
}
|
||||||
if (it.isEmpty() && LauncherPreferences.instance.searchContacts && !permissionsManager.checkPermission(
|
if (it.isEmpty() && LauncherPreferences.instance.searchContacts && !permissionsManager.checkPermissionOnce(
|
||||||
PermissionGroup.Contacts
|
PermissionGroup.Contacts
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class FileView : FrameLayout, KoinComponent {
|
|||||||
visibility = View.GONE
|
visibility = View.GONE
|
||||||
return@observe
|
return@observe
|
||||||
}
|
}
|
||||||
if (it.isEmpty() && !permissionsManager.checkPermission(
|
if (it.isEmpty() && !permissionsManager.checkPermissionOnce(
|
||||||
PermissionGroup.ExternalStorage
|
PermissionGroup.ExternalStorage
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.tween
|
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.preferences.Settings
|
||||||
import de.mm20.launcher2.ui.LegacyLauncherTheme
|
import de.mm20.launcher2.ui.LegacyLauncherTheme
|
||||||
import de.mm20.launcher2.ui.R
|
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.locals.LocalNavController
|
||||||
import de.mm20.launcher2.ui.screens.settings.SettingsLicenseScreen
|
|
||||||
import de.mm20.launcher2.ui.settings.about.AboutScreen
|
import de.mm20.launcher2.ui.settings.about.AboutScreen
|
||||||
import de.mm20.launcher2.ui.settings.appearance.AppearanceScreen
|
import de.mm20.launcher2.ui.settings.appearance.AppearanceScreen
|
||||||
import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
||||||
import de.mm20.launcher2.ui.settings.main.MainScreen
|
import de.mm20.launcher2.ui.settings.main.MainScreen
|
||||||
import de.mm20.launcher2.ui.settings.weather.WeatherScreen
|
import de.mm20.launcher2.ui.settings.weather.WeatherScreen
|
||||||
|
|
||||||
class SettingsActivity : AppCompatActivity() {
|
class SettingsActivity : BaseActivity() {
|
||||||
|
|
||||||
private val viewModel: SettingsActivityVM by viewModels()
|
private val viewModel: SettingsActivityVM by viewModels()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user