Add preference to hide widget edit button
This commit is contained in:
parent
9309cd4891
commit
fc6d1d382c
@ -622,6 +622,7 @@
|
|||||||
<string name="preference_favorites_frequently_used_summary">Show frequently used items in favorites</string>
|
<string name="preference_favorites_frequently_used_summary">Show frequently used items in favorites</string>
|
||||||
<string name="preference_edit_button">Edit button</string>
|
<string name="preference_edit_button">Edit button</string>
|
||||||
<string name="preference_favorites_edit_button_summary">Show a button to rearrange the favorites</string>
|
<string name="preference_favorites_edit_button_summary">Show a button to rearrange the favorites</string>
|
||||||
|
<string name="preference_widgets_edit_button_summary">Show a button to add, remove and rearrange widgets</string>
|
||||||
<!-- Used in an info banner if a specific feature requires a Nextcloud account -->
|
<!-- Used in an info banner if a specific feature requires a Nextcloud account -->
|
||||||
<string name="no_account_nextcloud">You haven\'t connected a Nextcloud account yet</string>
|
<string name="no_account_nextcloud">You haven\'t connected a Nextcloud account yet</string>
|
||||||
<!-- Used in an info banner if a specific feature requires an Owncloud account -->
|
<!-- Used in an info banner if a specific feature requires an Owncloud account -->
|
||||||
|
|||||||
@ -155,6 +155,10 @@ fun createFactorySettings(context: Context): Settings {
|
|||||||
.setRadius(8)
|
.setRadius(8)
|
||||||
.setOpacity(1f)
|
.setOpacity(1f)
|
||||||
)
|
)
|
||||||
|
.setWidgets(
|
||||||
|
Settings.WidgetSettings.newBuilder()
|
||||||
|
.setEditButton(true)
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,18 @@ package de.mm20.launcher2.preferences.migrations
|
|||||||
|
|
||||||
import de.mm20.launcher2.preferences.Settings
|
import de.mm20.launcher2.preferences.Settings
|
||||||
|
|
||||||
class Migration_9_10: VersionedMigration(9, 10) {
|
class Migration_9_10 : VersionedMigration(9, 10) {
|
||||||
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
|
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
|
||||||
return builder.setFavorites(
|
return builder
|
||||||
builder.favorites.toBuilder()
|
.setFavorites(
|
||||||
.setFrequentlyUsed(true)
|
builder.favorites.toBuilder()
|
||||||
.setFrequentlyUsedRows(1)
|
.setFrequentlyUsed(true)
|
||||||
.setEditButton(true)
|
.setFrequentlyUsedRows(1)
|
||||||
)
|
.setEditButton(true)
|
||||||
|
)
|
||||||
|
.setWidgets(
|
||||||
|
Settings.WidgetSettings.newBuilder()
|
||||||
|
.setEditButton(true)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,4 +259,9 @@ message Settings {
|
|||||||
bool enabled = 1;
|
bool enabled = 1;
|
||||||
}
|
}
|
||||||
AppShortcutSearchSettings app_shortcut_search = 25;
|
AppShortcutSearchSettings app_shortcut_search = 25;
|
||||||
|
|
||||||
|
message WidgetSettings {
|
||||||
|
bool edit_button = 1;
|
||||||
|
}
|
||||||
|
WidgetSettings widgets = 26;
|
||||||
}
|
}
|
||||||
@ -7,20 +7,37 @@ import android.content.Intent
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
|
|
||||||
import androidx.compose.animation.graphics.res.animatedVectorResource
|
import androidx.compose.animation.graphics.res.animatedVectorResource
|
||||||
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
||||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.rememberDraggableState
|
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Add
|
import androidx.compose.material.icons.rounded.Add
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.onPlaced
|
import androidx.compose.ui.layout.onPlaced
|
||||||
@ -35,10 +52,9 @@ import androidx.compose.ui.window.Dialog
|
|||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.clock.ClockWidget
|
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.ktx.animateTo
|
import de.mm20.launcher2.ui.ktx.animateTo
|
||||||
import de.mm20.launcher2.ui.ktx.conditional
|
import de.mm20.launcher2.ui.launcher.widgets.clock.ClockWidget
|
||||||
import de.mm20.launcher2.ui.launcher.widgets.picker.PickAppWidgetActivity
|
import de.mm20.launcher2.ui.launcher.widgets.picker.PickAppWidgetActivity
|
||||||
import de.mm20.launcher2.widgets.ExternalWidget
|
import de.mm20.launcher2.widgets.ExternalWidget
|
||||||
import kotlinx.coroutines.awaitCancellation
|
import kotlinx.coroutines.awaitCancellation
|
||||||
@ -69,17 +85,18 @@ fun WidgetColumn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val pickWidgetLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
|
val pickWidgetLauncher =
|
||||||
val data = it.data ?: return@rememberLauncherForActivityResult
|
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
|
||||||
val widgetId = data.getIntExtra(
|
val data = it.data ?: return@rememberLauncherForActivityResult
|
||||||
AppWidgetManager.EXTRA_APPWIDGET_ID,
|
val widgetId = data.getIntExtra(
|
||||||
AppWidgetManager.INVALID_APPWIDGET_ID
|
AppWidgetManager.EXTRA_APPWIDGET_ID,
|
||||||
)
|
AppWidgetManager.INVALID_APPWIDGET_ID
|
||||||
if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) return@rememberLauncherForActivityResult
|
)
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) return@rememberLauncherForActivityResult
|
||||||
viewModel.addAppWidget(context, widgetId)
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
|
viewModel.addAppWidget(context, widgetId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@ -168,43 +185,47 @@ fun WidgetColumn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val icon =
|
val editButton by viewModel.editButton.observeAsState()
|
||||||
AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_edit_add)
|
if (editButton == true) {
|
||||||
ExtendedFloatingActionButton(
|
val icon =
|
||||||
modifier = Modifier
|
AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_edit_add)
|
||||||
.padding(16.dp)
|
ExtendedFloatingActionButton(
|
||||||
.align(Alignment.CenterHorizontally),
|
modifier = Modifier
|
||||||
icon = {
|
.padding(16.dp)
|
||||||
Icon(
|
.align(Alignment.CenterHorizontally),
|
||||||
painter = rememberAnimatedVectorPainter(
|
icon = {
|
||||||
animatedImageVector = icon,
|
Icon(
|
||||||
atEnd = !editMode
|
painter = rememberAnimatedVectorPainter(
|
||||||
), contentDescription = null
|
animatedImageVector = icon,
|
||||||
)
|
atEnd = !editMode
|
||||||
},
|
), contentDescription = null
|
||||||
text = {
|
|
||||||
Text(
|
|
||||||
stringResource(
|
|
||||||
if (editMode) R.string.widget_add_widget
|
|
||||||
else R.string.menu_edit_widgets
|
|
||||||
)
|
)
|
||||||
)
|
},
|
||||||
}, onClick = {
|
text = {
|
||||||
if (!editMode) {
|
Text(
|
||||||
onEditModeChange(true)
|
stringResource(
|
||||||
} else {
|
if (editMode) R.string.widget_add_widget
|
||||||
if (viewModel.getAvailableBuiltInWidgets().isEmpty()) {
|
else R.string.menu_edit_widgets
|
||||||
pickWidgetLauncher.launch(
|
|
||||||
Intent(
|
|
||||||
context,
|
|
||||||
PickAppWidgetActivity::class.java
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
}, onClick = {
|
||||||
|
if (!editMode) {
|
||||||
|
onEditModeChange(true)
|
||||||
} else {
|
} else {
|
||||||
showAddDialog = true
|
if (viewModel.getAvailableBuiltInWidgets().isEmpty()) {
|
||||||
|
pickWidgetLauncher.launch(
|
||||||
|
Intent(
|
||||||
|
context,
|
||||||
|
PickAppWidgetActivity::class.java
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
showAddDialog = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
}
|
||||||
|
|
||||||
if (showAddDialog) {
|
if (showAddDialog) {
|
||||||
val availableBuiltInWidgets =
|
val availableBuiltInWidgets =
|
||||||
|
|||||||
@ -6,18 +6,24 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.liveData
|
import androidx.lifecycle.liveData
|
||||||
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.widgets.ExternalWidget
|
import de.mm20.launcher2.widgets.ExternalWidget
|
||||||
import de.mm20.launcher2.widgets.Widget
|
import de.mm20.launcher2.widgets.Widget
|
||||||
import de.mm20.launcher2.widgets.WidgetRepository
|
import de.mm20.launcher2.widgets.WidgetRepository
|
||||||
import de.mm20.launcher2.widgets.WidgetType
|
import de.mm20.launcher2.widgets.WidgetType
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
class WidgetsVM : ViewModel(), KoinComponent {
|
class WidgetsVM : ViewModel(), KoinComponent {
|
||||||
private val widgetRepository: WidgetRepository by inject()
|
private val widgetRepository: WidgetRepository by inject()
|
||||||
|
|
||||||
|
private val dataStore: LauncherDataStore by inject()
|
||||||
|
|
||||||
|
val editButton = dataStore.data.map { it.widgets.editButton }.asLiveData()
|
||||||
|
|
||||||
val widgets = widgetRepository.getWidgets().asLiveData()
|
val widgets = widgetRepository.getWidgets().asLiveData()
|
||||||
|
|
||||||
fun addWidget(widget: Widget) {
|
fun addWidget(widget: Widget) {
|
||||||
|
|||||||
@ -2,16 +2,35 @@ package de.mm20.launcher2.ui.settings.widgets
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||||
import de.mm20.launcher2.widgets.WidgetRepository
|
import de.mm20.launcher2.widgets.WidgetRepository
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
class WidgetSettingsScreenVM: ViewModel(), KoinComponent {
|
class WidgetSettingsScreenVM : ViewModel(), KoinComponent {
|
||||||
|
|
||||||
|
|
||||||
private val widgetRepository: WidgetRepository by inject()
|
private val widgetRepository: WidgetRepository by inject()
|
||||||
|
private val dataStore: LauncherDataStore by inject()
|
||||||
|
|
||||||
val calendarWidget = widgetRepository.isCalendarWidgetEnabled().asLiveData()
|
val calendarWidget = widgetRepository.isCalendarWidgetEnabled().asLiveData()
|
||||||
val musicWidget = widgetRepository.isMusicWidgetEnabled().asLiveData()
|
val musicWidget = widgetRepository.isMusicWidgetEnabled().asLiveData()
|
||||||
val weatherWidget = widgetRepository.isWeatherWidgetEnabled().asLiveData()
|
val weatherWidget = widgetRepository.isWeatherWidgetEnabled().asLiveData()
|
||||||
val favoritesWidget = widgetRepository.isFavoritesWidgetEnabled().asLiveData()
|
val favoritesWidget = widgetRepository.isFavoritesWidgetEnabled().asLiveData()
|
||||||
|
val editButton = dataStore.data.map { it.widgets.editButton }.asLiveData()
|
||||||
|
fun setEditButton(editButton: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
dataStore.updateData {
|
||||||
|
it.toBuilder()
|
||||||
|
.setWidgets(
|
||||||
|
it.widgets.toBuilder()
|
||||||
|
.setEditButton(editButton)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -13,7 +13,9 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
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.PreferenceScreen
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -22,56 +24,71 @@ fun WidgetsSettingsScreen() {
|
|||||||
val viewModel: WidgetSettingsScreenVM = viewModel()
|
val viewModel: WidgetSettingsScreenVM = viewModel()
|
||||||
PreferenceScreen(title = stringResource(R.string.preference_screen_widgets)) {
|
PreferenceScreen(title = stringResource(R.string.preference_screen_widgets)) {
|
||||||
item {
|
item {
|
||||||
Preference(
|
PreferenceCategory {
|
||||||
title = stringResource(R.string.preference_screen_clockwidget),
|
Preference(
|
||||||
icon = Icons.Rounded.Schedule,
|
title = stringResource(R.string.preference_screen_clockwidget),
|
||||||
onClick = {
|
icon = Icons.Rounded.Schedule,
|
||||||
navController?.navigate("settings/widgets/clock")
|
onClick = {
|
||||||
|
navController?.navigate("settings/widgets/clock")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val weatherWidget by viewModel.weatherWidget.observeAsState()
|
||||||
|
if (weatherWidget == true) {
|
||||||
|
Preference(
|
||||||
|
title = stringResource(R.string.preference_screen_weatherwidget),
|
||||||
|
icon = Icons.Rounded.LightMode,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/widgets/weather")
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
val weatherWidget by viewModel.weatherWidget.observeAsState()
|
val musicWidget by viewModel.musicWidget.observeAsState()
|
||||||
if (weatherWidget == true) {
|
if (musicWidget == true) {
|
||||||
Preference(
|
Preference(
|
||||||
title = stringResource(R.string.preference_screen_weatherwidget),
|
title = stringResource(R.string.preference_screen_musicwidget),
|
||||||
icon = Icons.Rounded.LightMode,
|
icon = Icons.Rounded.Audiotrack,
|
||||||
onClick = {
|
onClick = {
|
||||||
navController?.navigate("settings/widgets/weather")
|
navController?.navigate("settings/widgets/music")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val calendarWidget by viewModel.calendarWidget.observeAsState()
|
||||||
|
if (calendarWidget == true) {
|
||||||
|
Preference(
|
||||||
|
title = stringResource(R.string.preference_screen_calendarwidget),
|
||||||
|
icon = Icons.Rounded.Today,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/widgets/calendar")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val favoritesWidget by viewModel.favoritesWidget.observeAsState()
|
||||||
|
if (favoritesWidget == true) {
|
||||||
|
Preference(
|
||||||
|
title = stringResource(R.string.favorites),
|
||||||
|
icon = Icons.Rounded.Star,
|
||||||
|
onClick = {
|
||||||
|
navController?.navigate("settings/favorites")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val musicWidget by viewModel.musicWidget.observeAsState()
|
item {
|
||||||
if (musicWidget == true) {
|
PreferenceCategory {
|
||||||
Preference(
|
val editButton by viewModel.editButton.observeAsState()
|
||||||
title = stringResource(R.string.preference_screen_musicwidget),
|
SwitchPreference(
|
||||||
icon = Icons.Rounded.Audiotrack,
|
title = stringResource(id = R.string.preference_edit_button),
|
||||||
onClick = {
|
summary = stringResource(id = R.string.preference_widgets_edit_button_summary),
|
||||||
navController?.navigate("settings/widgets/music")
|
value = editButton == true,
|
||||||
}
|
onValueChanged = {
|
||||||
)
|
viewModel.setEditButton(it)
|
||||||
}
|
})
|
||||||
|
|
||||||
val calendarWidget by viewModel.calendarWidget.observeAsState()
|
|
||||||
if (calendarWidget == true) {
|
|
||||||
Preference(
|
|
||||||
title = stringResource(R.string.preference_screen_calendarwidget),
|
|
||||||
icon = Icons.Rounded.Today,
|
|
||||||
onClick = {
|
|
||||||
navController?.navigate("settings/widgets/calendar")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val favoritesWidget by viewModel.favoritesWidget.observeAsState()
|
|
||||||
if (favoritesWidget == true) {
|
|
||||||
Preference(
|
|
||||||
title = stringResource(R.string.favorites),
|
|
||||||
icon = Icons.Rounded.Star,
|
|
||||||
onClick = {
|
|
||||||
navController?.navigate("settings/favorites")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user