Add preference to hide widget edit button

This commit is contained in:
MM20 2022-09-27 21:06:07 +02:00
parent 9309cd4891
commit fc6d1d382c
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
8 changed files with 181 additions and 103 deletions

View File

@ -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 -->

View File

@ -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()
} }

View File

@ -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)
)
} }
} }

View File

@ -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;
} }

View File

@ -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 =

View File

@ -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) {

View File

@ -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()
}
}
}
} }

View File

@ -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")
}
)
} }
} }
} }