From b8304f1c1c4dde43e597ce9ced9d9d5dcc1d6dcc Mon Sep 17 00:00:00 2001
From: MM20 <15646950+MM2-0@users.noreply.github.com>
Date: Wed, 26 Jan 2022 21:51:35 +0100
Subject: [PATCH] Fix permission banners in search results
---
i18n/src/main/res/values-de/strings.xml | 1 +
i18n/src/main/res/values/strings.xml | 2 +
.../data/search/MissingPermission.kt | 7 +-
.../ui/legacy/component/CalendarView.kt | 102 +++++++++++++-----
.../ui/legacy/component/ContactView.kt | 101 ++++++++++++-----
.../launcher2/ui/legacy/component/FileView.kt | 98 +++++++++++++----
.../search/PermissionListRepresentation.kt | 43 ++++++--
.../main/res/layout/view_permission_list.xml | 33 +-----
8 files changed, 270 insertions(+), 117 deletions(-)
diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml
index 2ab22b18..d059f51d 100644
--- a/i18n/src/main/res/values-de/strings.xml
+++ b/i18n/src/main/res/values-de/strings.xml
@@ -477,4 +477,5 @@
Sie haben noch kein Microsoft-Konto verbunden
Sie haben noch kein Google-Konto verbunden
Konto verbinden
+ Deaktivieren
\ No newline at end of file
diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index 711dc517..2aedad7a 100644
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -516,4 +516,6 @@
You haven\'t connected a Microsoft account yet
You haven\'t connected a Google account yet
Connect account
+
+ Turn off
diff --git a/permissions/src/main/java/de/mm20/launcher2/data/search/MissingPermission.kt b/permissions/src/main/java/de/mm20/launcher2/data/search/MissingPermission.kt
index 0de76f94..8c39c7b7 100644
--- a/permissions/src/main/java/de/mm20/launcher2/data/search/MissingPermission.kt
+++ b/permissions/src/main/java/de/mm20/launcher2/data/search/MissingPermission.kt
@@ -7,7 +7,12 @@ import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.R
-class MissingPermission(override val label: String, val permissionGroup: PermissionGroup): Searchable() {
+class MissingPermission(
+ override val label: String,
+ val permissionGroup: PermissionGroup,
+ val secondaryActionLabel: String? = null,
+ val secondaryAction: (() -> Unit)? = null
+ ): Searchable() {
override val key: String
get() = "permission://${permissionGroup.ordinal}"
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/CalendarView.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/CalendarView.kt
index 46df4e98..e9b93051 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/CalendarView.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/CalendarView.kt
@@ -8,15 +8,19 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
-import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.asLiveData
+import de.mm20.launcher2.ktx.lifecycleScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
-import de.mm20.launcher2.preferences.LauncherPreferences
-import de.mm20.launcher2.search.data.CalendarEvent
+import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.MissingPermission
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.legacy.search.SearchListView
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@@ -30,40 +34,84 @@ class CalendarView : FrameLayout, KoinComponent {
defStyleRes
)
- private val calendarEvents: LiveData?>
-
init {
val permissionsManager: PermissionsManager = get()
+ val dataStore: LauncherDataStore = get()
View.inflate(context, R.layout.view_search_category_list, this)
layoutTransition = LayoutTransition()
layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
val card = findViewById(R.id.card)
card.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
- val list = findViewById(R.id.list)
val viewModel: SearchVM by (context as AppCompatActivity).viewModels()
- calendarEvents = viewModel.calendarResults
- calendarEvents.observe(context as AppCompatActivity, {
- if (it == null) {
- visibility = View.GONE
- return@observe
+
+ val showMissingPermissionBanner = combine(
+ dataStore.data.map { it.calendarSearch.enabled },
+ permissionsManager.hasPermission(PermissionGroup.Calendar)
+ ) { calendarSearchEnabled, hasPermission ->
+ !hasPermission && calendarSearchEnabled
+ }.asLiveData()
+
+ val searchQuery = viewModel.searchQuery
+ val calendarResults = viewModel.calendarResults
+
+ val show = MediatorLiveData()
+ show.addSource(showMissingPermissionBanner) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !calendarResults.value.isNullOrEmpty())
+ }
+ show.addSource(calendarResults) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !calendarResults.value.isNullOrEmpty())
+ }
+ show.addSource(searchQuery) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !calendarResults.value.isNullOrEmpty())
+ }
+
+ show.observe(context as AppCompatActivity) {
+ visibility = if (it) {
+ View.VISIBLE
+ } else {
+ View.GONE
}
- if (it.isEmpty() && LauncherPreferences.instance.searchCalendars && !permissionsManager.checkPermissionOnce(
- PermissionGroup.Calendar
- )
- ) {
- visibility = View.VISIBLE
- list.submitItems(
- listOf(
- MissingPermission(
- context.getString(R.string.permission_calendar_search),
- PermissionGroup.Calendar
- )
+ }
+
+ val list = findViewById(R.id.list)
+ calendarResults.observe(context as AppCompatActivity) {
+ if (showMissingPermissionBanner.value == true) {
+ list.submitItems(listOf(
+ MissingPermission(
+ context.getString(R.string.permission_calendar_search),
+ PermissionGroup.Calendar,
+ secondaryActionLabel = context.getString(R.string.turn_off),
+ secondaryAction = {
+ lifecycleScope.launch {
+ dataStore.updateData {
+ it.toBuilder()
+ .setCalendarSearch(it.calendarSearch.toBuilder().setEnabled(false))
+ .build()
+ }
+ }
+ }
)
- )
- return@observe
+ ) + it)
+ } else {
+ list.submitItems(it)
}
- visibility = if (it.isEmpty()) View.GONE else View.VISIBLE
- list.submitItems(it)
- })
+ }
+
+ showMissingPermissionBanner.observe(context as AppCompatActivity) {
+ if (it == true) {
+ list.submitItems(listOf(
+ MissingPermission(
+ context.getString(R.string.permission_calendar_search),
+ PermissionGroup.Calendar,
+ secondaryActionLabel = context.getString(R.string.turn_off)
+ )
+ ) + calendarResults.value!!)
+ } else {
+ list.submitItems(calendarResults.value)
+ }
+ }
}
}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/ContactView.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/ContactView.kt
index 758ff0e8..7b603913 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/ContactView.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/ContactView.kt
@@ -8,20 +8,23 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
-import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.asLiveData
+import de.mm20.launcher2.ktx.lifecycleScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
-import de.mm20.launcher2.preferences.LauncherPreferences
-import de.mm20.launcher2.search.data.Contact
+import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.MissingPermission
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.legacy.search.SearchListView
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
class ContactView : FrameLayout, KoinComponent {
- private val contacts: LiveData?>
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
@@ -33,36 +36,82 @@ class ContactView : FrameLayout, KoinComponent {
init {
val permissionsManager: PermissionsManager = get()
+ val dataStore: LauncherDataStore = get()
View.inflate(context, R.layout.view_search_category_list, this)
layoutTransition = LayoutTransition()
layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
val card = findViewById(R.id.card)
card.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
val viewModel: SearchVM by (context as AppCompatActivity).viewModels()
- contacts = viewModel.contactResults
+
+ val showMissingPermissionBanner = combine(
+ dataStore.data.map { it.contactsSearch.enabled },
+ permissionsManager.hasPermission(PermissionGroup.Contacts)
+ ) { contactSearchEnabled, hasPermission ->
+ !hasPermission && contactSearchEnabled
+ }.asLiveData()
+
+ val searchQuery = viewModel.searchQuery
+ val contactResults = viewModel.contactResults
+
+ val show = MediatorLiveData()
+ show.addSource(showMissingPermissionBanner) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !contactResults.value.isNullOrEmpty())
+ }
+ show.addSource(contactResults) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !contactResults.value.isNullOrEmpty())
+ }
+ show.addSource(searchQuery) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !contactResults.value.isNullOrEmpty())
+ }
+
+ show.observe(context as AppCompatActivity) {
+ visibility = if (it) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+ }
+
val list = findViewById(R.id.list)
- contacts.observe(context as AppCompatActivity, {
- if (it == null) {
- visibility = View.GONE
- return@observe
- }
- if (it.isEmpty() && LauncherPreferences.instance.searchContacts && !permissionsManager.checkPermissionOnce(
- PermissionGroup.Contacts
- )
- ) {
- visibility = View.VISIBLE
- list.submitItems(
- listOf(
- MissingPermission(
- context.getString(R.string.permission_contact_search),
- PermissionGroup.Contacts
- )
+ contactResults.observe(context as AppCompatActivity) {
+ if (showMissingPermissionBanner.value == true) {
+ list.submitItems(listOf(
+ MissingPermission(
+ context.getString(R.string.permission_contact_search),
+ PermissionGroup.Contacts,
+ secondaryActionLabel = context.getString(R.string.turn_off),
+ secondaryAction = {
+ lifecycleScope.launch {
+ dataStore.updateData {
+ it.toBuilder()
+ .setContactsSearch(it.contactsSearch.toBuilder().setEnabled(false))
+ .build()
+ }
+ }
+ }
)
- )
- return@observe
+ ) + it)
+ } else {
+ list.submitItems(it)
}
- visibility = if (it.isEmpty()) View.GONE else View.VISIBLE
- list.submitItems(it)
- })
+ }
+
+ showMissingPermissionBanner.observe(context as AppCompatActivity) {
+ if (it == true) {
+ list.submitItems(listOf(
+ MissingPermission(
+ context.getString(R.string.permission_contact_search),
+ PermissionGroup.Contacts,
+ secondaryActionLabel = context.getString(R.string.turn_off)
+ )
+ ) + contactResults.value!!)
+ } else {
+ list.submitItems(contactResults.value)
+ }
+ }
}
}
\ No newline at end of file
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/FileView.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/FileView.kt
index 13ccd956..b49e194e 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/FileView.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/component/FileView.kt
@@ -9,18 +9,24 @@ import android.widget.FrameLayout
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.asLiveData
+import de.mm20.launcher2.ktx.lifecycleScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
+import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.File
import de.mm20.launcher2.search.data.MissingPermission
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.legacy.search.SearchListView
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
class FileView : FrameLayout, KoinComponent {
- private val files: LiveData?>
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
@@ -32,36 +38,82 @@ class FileView : FrameLayout, KoinComponent {
init {
val permissionsManager: PermissionsManager = get()
+ val dataStore: LauncherDataStore = get()
View.inflate(context, R.layout.view_search_category_list, this)
layoutTransition = LayoutTransition()
layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
val card = findViewById(R.id.card)
card.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
- val list = findViewById(R.id.list)
val viewModel: SearchVM by (context as AppCompatActivity).viewModels()
- files = viewModel.fileResults
- files.observe(context as AppCompatActivity, {
- if (it == null) {
- visibility = View.GONE
- return@observe
+
+ val showMissingPermissionBanner = combine(
+ dataStore.data.map { it.fileSearch.localFiles },
+ permissionsManager.hasPermission(PermissionGroup.ExternalStorage)
+ ) { localFileSearchEnabled, hasPermission ->
+ !hasPermission && localFileSearchEnabled
+ }.asLiveData()
+
+ val searchQuery = viewModel.searchQuery
+ val fileResults = viewModel.fileResults
+
+ val show = MediatorLiveData()
+ show.addSource(showMissingPermissionBanner) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !fileResults.value.isNullOrEmpty())
+ }
+ show.addSource(fileResults) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !fileResults.value.isNullOrEmpty())
+ }
+ show.addSource(searchQuery) {
+ show.value = !searchQuery.value.isNullOrBlank() &&
+ (showMissingPermissionBanner.value == true || !fileResults.value.isNullOrEmpty())
+ }
+
+ show.observe(context as AppCompatActivity) {
+ visibility = if (it) {
+ View.VISIBLE
+ } else {
+ View.GONE
}
- if (it.isEmpty() && !permissionsManager.checkPermissionOnce(
- PermissionGroup.ExternalStorage
- )
- ) {
- visibility = View.VISIBLE
- list.submitItems(
- listOf(
- MissingPermission(
- context.getString(R.string.permission_files_search),
- PermissionGroup.ExternalStorage
- )
+ }
+
+ val list = findViewById(R.id.list)
+ fileResults.observe(context as AppCompatActivity) {
+ if (showMissingPermissionBanner.value == true) {
+ list.submitItems(listOf(
+ MissingPermission(
+ context.getString(R.string.permission_files_search),
+ PermissionGroup.ExternalStorage,
+ secondaryActionLabel = context.getString(R.string.turn_off),
+ secondaryAction = {
+ lifecycleScope.launch {
+ dataStore.updateData {
+ it.toBuilder()
+ .setFileSearch(it.fileSearch.toBuilder().setLocalFiles(false))
+ .build()
+ }
+ }
+ }
)
- )
- return@observe
+ ) + it)
+ } else {
+ list.submitItems(it)
}
- visibility = if (it.isEmpty()) View.GONE else View.VISIBLE
- list.submitItems(it)
- })
+ }
+
+ showMissingPermissionBanner.observe(context as AppCompatActivity) {
+ if (it == true) {
+ list.submitItems(listOf(
+ MissingPermission(
+ context.getString(R.string.permission_files_search),
+ PermissionGroup.ExternalStorage,
+ secondaryActionLabel = context.getString(R.string.turn_off)
+ )
+ ) + fileResults.value!!)
+ } else {
+ list.submitItems(fileResults.value)
+ }
+ }
}
}
diff --git a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/PermissionListRepresentation.kt b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/PermissionListRepresentation.kt
index da0073ae..01295be8 100644
--- a/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/PermissionListRepresentation.kt
+++ b/ui/src/main/java/de/mm20/launcher2/ui/legacy/search/PermissionListRepresentation.kt
@@ -1,30 +1,53 @@
package de.mm20.launcher2.ui.legacy.search
-import android.app.Activity
-import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.ui.platform.ComposeView
import androidx.transition.Scene
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.search.data.MissingPermission
import de.mm20.launcher2.search.data.Searchable
+import de.mm20.launcher2.ui.LegacyLauncherTheme
import de.mm20.launcher2.ui.R
+import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.legacy.searchable.SearchableView
-import de.mm20.launcher2.ui.legacy.view.InnerCardView
-import de.mm20.launcher2.ui.legacy.view.LauncherIconView
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
class PermissionListRepresentation : Representation, KoinComponent {
- override fun getScene(rootView: SearchableView, searchable: Searchable, previousRepresentation: Int?): Scene {
+ override fun getScene(
+ rootView: SearchableView,
+ searchable: Searchable,
+ previousRepresentation: Int?
+ ): Scene {
val missingPermission = searchable as MissingPermission
val context = rootView.context
- val scene = Scene.getSceneForLayout(rootView, R.layout.view_permission_list, rootView.context)
+ val scene =
+ Scene.getSceneForLayout(rootView, R.layout.view_permission_list, rootView.context)
scene.setEnterAction {
val permissionsManager: PermissionsManager = get()
- rootView.findViewById(R.id.permissionText).text = missingPermission.label
- rootView.findViewById(R.id.permissionIcon).icon = missingPermission.getPlaceholderIcon(context)
- rootView.findViewById(R.id.card).setOnClickListener {
- permissionsManager.requestPermission(context as AppCompatActivity, missingPermission.permissionGroup)
+ rootView.findViewById(R.id.composeView).setContent {
+ LegacyLauncherTheme {
+ MissingPermissionBanner(
+ text = missingPermission.label,
+ onClick = {
+ permissionsManager.requestPermission(
+ context as AppCompatActivity,
+ missingPermission.permissionGroup
+ )
+ },
+ secondaryAction = {
+ val secondaryAction = missingPermission.secondaryAction
+ val secondaryActionLabel = missingPermission.secondaryActionLabel
+ if (secondaryAction != null && secondaryActionLabel != null)
+ TextButton(onClick = secondaryAction) {
+ Text(text = secondaryActionLabel, style = MaterialTheme.typography.labelLarge)
+ }
+ }
+ )
+ }
}
}
return scene
diff --git a/ui/src/main/res/layout/view_permission_list.xml b/ui/src/main/res/layout/view_permission_list.xml
index ccf662ff..6ed12f9c 100644
--- a/ui/src/main/res/layout/view_permission_list.xml
+++ b/ui/src/main/res/layout/view_permission_list.xml
@@ -1,37 +1,10 @@
-
-
-
-
-
-
-
\ No newline at end of file
+ android:transitionName="root" />
\ No newline at end of file