Add Tasks integration settings screen
This commit is contained in:
parent
c7acd7ad24
commit
28b33398cd
@ -69,6 +69,7 @@ import de.mm20.launcher2.ui.settings.plugins.PluginsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.searchactions.SearchActionsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.tags.TagsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.tasks.TasksIntegrationSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterHelpSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.weather.WeatherIntegrationSettingsScreen
|
||||
@ -240,6 +241,9 @@ class SettingsActivity : BaseActivity() {
|
||||
composable("settings/integrations/owncloud") {
|
||||
OwncloudSettingsScreen()
|
||||
}
|
||||
composable("settings/integrations/tasks") {
|
||||
TasksIntegrationSettingsScreen()
|
||||
}
|
||||
composable("settings/plugins") {
|
||||
PluginsSettingsScreen()
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package de.mm20.launcher2.ui.settings.integrations
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.rounded.LightMode
|
||||
import androidx.compose.material.icons.rounded.PlayCircleOutline
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -49,6 +50,13 @@ fun IntegrationsSettingsScreen() {
|
||||
navController?.navigate("settings/integrations/owncloud")
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_tasks_integration),
|
||||
icon = Icons.Default.Check,
|
||||
onClick = {
|
||||
navController?.navigate("settings/integrations/tasks")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,7 +63,7 @@ fun SearchSettingsScreen() {
|
||||
val hasCalendarPlugins by remember { derivedStateOf { plugins?.any { it.plugin.type == PluginType.Calendar } } }
|
||||
val hasLocationPlugins by remember { derivedStateOf { plugins?.any { it.plugin.type == PluginType.LocationSearch } } }
|
||||
val hasContactPlugins by remember { derivedStateOf { plugins?.any { it.plugin.type == PluginType.ContactSearch } } }
|
||||
|
||||
val isTasksAppInstalled by viewModel.isTasksAppInstalled.collectAsStateWithLifecycle()
|
||||
|
||||
val hasAppShortcutsPermission by viewModel.hasAppShortcutPermission.collectAsStateWithLifecycle(null)
|
||||
val hasContactsPermission by viewModel.hasContactsPermission.collectAsStateWithLifecycle(null)
|
||||
@ -145,7 +145,7 @@ fun SearchSettingsScreen() {
|
||||
)
|
||||
}
|
||||
|
||||
if (hasCalendarPlugins != false) {
|
||||
if (hasCalendarPlugins != false || isTasksAppInstalled != false) {
|
||||
Preference(
|
||||
title = stringResource(R.string.preference_search_calendar),
|
||||
summary = stringResource(R.string.preference_search_calendar_summary),
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package de.mm20.launcher2.ui.settings.search
|
||||
|
||||
import android.os.Process
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.applications.AppRepository
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.plugins.PluginService
|
||||
@ -18,6 +20,7 @@ import de.mm20.launcher2.preferences.search.WikipediaSearchSettings
|
||||
import de.mm20.launcher2.preferences.ui.SearchUiSettings
|
||||
import de.mm20.launcher2.search.SearchFilters
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
@ -34,6 +37,8 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val locationSearchSettings: LocationSearchSettings by inject()
|
||||
private val searchFilterSettings: SearchFilterSettings by inject()
|
||||
|
||||
private val appRepository: AppRepository by inject()
|
||||
|
||||
private val pluginService: PluginService by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
|
||||
@ -159,4 +164,8 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
|
||||
val plugins = pluginService.getPluginsWithState(enabled = true)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val isTasksAppInstalled = appRepository.findOne("org.tasks", Process.myUserHandle())
|
||||
.map { it != null }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package de.mm20.launcher2.ui.settings.tasks
|
||||
|
||||
import androidx.activity.compose.LocalActivity
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
|
||||
import androidx.compose.material.icons.rounded.CheckCircle
|
||||
import androidx.compose.material.icons.rounded.Info
|
||||
import androidx.compose.material.icons.rounded.TaskAlt
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
|
||||
@Composable
|
||||
fun TasksIntegrationSettingsScreen() {
|
||||
val viewModel: TasksSettingsScreenVM = viewModel()
|
||||
val activity = LocalActivity.current
|
||||
val navController = LocalNavController.current
|
||||
|
||||
val isTasksInstalled by viewModel.isTasksAppInstalled.collectAsStateWithLifecycle(null)
|
||||
val hasTasksPermission by viewModel.hasTasksPermission.collectAsStateWithLifecycle(null)
|
||||
val isTasksSearchEnabled by viewModel.isTasksSearchEnabled.collectAsStateWithLifecycle(false)
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_tasks_integration)
|
||||
) {
|
||||
if (isTasksInstalled == false) {
|
||||
item {
|
||||
Banner(
|
||||
text = stringResource(
|
||||
R.string.preference_tasks_integration_description,
|
||||
stringResource(R.string.app_name)
|
||||
),
|
||||
icon = Icons.Rounded.Info,
|
||||
modifier = Modifier.padding(16.dp),
|
||||
primaryAction = {
|
||||
Button(onClick = {
|
||||
viewModel.downloadTasksApp(activity as AppCompatActivity)
|
||||
}) {
|
||||
Text(stringResource(R.string.action_install))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isTasksInstalled == true) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
if (hasTasksPermission == false) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.missing_permission_tasks_integration),
|
||||
onClick = {
|
||||
viewModel.requestTasksPermission(activity as AppCompatActivity)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (hasTasksPermission == true) {
|
||||
Banner(
|
||||
text = stringResource(R.string.preference_tasks_integration_ready),
|
||||
icon = Icons.Rounded.CheckCircle,
|
||||
modifier = Modifier.padding(16.dp),
|
||||
)
|
||||
}
|
||||
PreferenceWithSwitch(
|
||||
icon = Icons.Rounded.TaskAlt,
|
||||
title = stringResource(R.string.preference_search_tasks),
|
||||
summary = stringResource(R.string.preference_search_tasks_summary),
|
||||
switchValue = isTasksSearchEnabled == true && hasTasksPermission == true,
|
||||
onSwitchChanged = {
|
||||
viewModel.setTasksSearchEnabled(it)
|
||||
},
|
||||
enabled = hasTasksPermission == true,
|
||||
onClick = {
|
||||
navController?.navigate("settings/search/calendar/tasks.org")
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
title = "Open Tasks app",
|
||||
icon = Icons.AutoMirrored.Rounded.OpenInNew,
|
||||
onClick = {
|
||||
viewModel.launchTasksApp(activity as AppCompatActivity)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package de.mm20.launcher2.ui.settings.tasks
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Process
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.applications.AppRepository
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class TasksSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val appRepository: AppRepository by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val iconService: IconService by inject()
|
||||
private val calendarSearchSettings: CalendarSearchSettings by inject()
|
||||
|
||||
val tasksApp = appRepository.findOne("org.tasks", Process.myUserHandle())
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
val isTasksAppInstalled = tasksApp
|
||||
.map { it != null }
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
val hasTasksPermission = permissionsManager.hasPermission(PermissionGroup.Tasks)
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
fun requestTasksPermission(activity: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(activity, PermissionGroup.Tasks)
|
||||
}
|
||||
|
||||
fun downloadTasksApp(activity: AppCompatActivity) {
|
||||
activity.startActivity(
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
data = "https://tasks.org/".toUri()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun launchTasksApp(activity: AppCompatActivity) {
|
||||
viewModelScope.launch {
|
||||
tasksApp.first()?.launch(activity, null)
|
||||
}
|
||||
}
|
||||
|
||||
val isTasksSearchEnabled = calendarSearchSettings.isProviderEnabled("tasks.org")
|
||||
.shareIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
|
||||
fun setTasksSearchEnabled(enabled: Boolean) {
|
||||
calendarSearchSettings.setProviderEnabled("tasks.org", enabled)
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@
|
||||
<string name="action_done">Done</string>
|
||||
<string name="action_more_actions">More actions</string>
|
||||
<string name="action_clear">Clear</string>
|
||||
<string name="action_install">Install</string>
|
||||
<!-- Delete something (a file or a web search shortcut) -->
|
||||
<string name="menu_delete">Delete</string>
|
||||
<string name="menu_remove">Remove</string>
|
||||
@ -389,6 +390,7 @@
|
||||
<!-- Missing calendar permission in search settings screen -->
|
||||
<string name="missing_permission_calendar_search_settings">Calendar permission is required to search calendar</string>
|
||||
<string name="missing_permission_tasks_search_settings">Tasks permission is required to search tasks</string>
|
||||
<string name="missing_permission_tasks_integration">Tasks permission is required to use the Tasks integration</string>
|
||||
<!-- Missing calendar permission in calendar widget settings screen -->
|
||||
<string name="missing_permission_calendar_widget_settings">This widget requires calendar permission</string>
|
||||
<string name="widget_config_calendar_missing_calendars_hint">Can\'t find your calendars?</string>
|
||||
@ -610,7 +612,7 @@
|
||||
<string name="preference_search_calendar_summary">Search upcoming appointments and events</string>
|
||||
<string name="preference_search_local_calendar_summary">Search calendars on this device</string>
|
||||
<string name="preference_search_tasks">Tasks</string>
|
||||
<string name="preference_search_tasks_summary">Search tasks in the Tasks.org app</string>
|
||||
<string name="preference_search_tasks_summary">Search tasks in the Tasks app</string>
|
||||
<string name="preference_search_appshortcuts">App shortcuts</string>
|
||||
<string name="preference_search_appshortcuts_summary">Search app shortcuts</string>
|
||||
<string name="preference_search_calculator">Calculator</string>
|
||||
@ -665,6 +667,10 @@
|
||||
<string name="preference_screen_icons_summary">Grid, icon size, icon packs, badges</string>
|
||||
<string name="preference_weather_integration">Weather</string>
|
||||
<string name="preference_media_integration">Media control</string>
|
||||
<string name="preference_tasks_integration">Tasks</string>
|
||||
<!-- %1$s: app name (Kvaesitso) -->
|
||||
<string name="preference_tasks_integration_description">Tasks is a free and open source to-do list and reminders app. If installed, %1$s can display and search your tasks from the Tasks app.</string>
|
||||
<string name="preference_tasks_integration_ready">Tasks integration is fully set up and ready to use.</string>
|
||||
<string name="preference_contacts_call_on_tap">Tap to call</string>
|
||||
<string name="preference_contacts_call_on_tap_summary">Immediately start a call when tapping a phone number</string>
|
||||
<!-- Used in an info banner if a specific feature requires a Nextcloud account -->
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user