Get rid of LiveData

This commit is contained in:
MM20 2023-04-20 21:40:29 +02:00
parent 0e40355ae1
commit d1482ad112
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
77 changed files with 433 additions and 816 deletions

View File

@ -68,7 +68,6 @@ dependencies {
implementation(libs.bundles.kotlin)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.livedata)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.foundationlayout)
implementation(libs.androidx.compose.ui)

View File

@ -58,19 +58,6 @@
android:value="de.mm20.launcher2.ui.launcher.SharedLauncherActivity" />
</activity>
<activity
android:name=".launcher.widgets.picker.PickAppWidgetActivity"
android:exported="true"
android:launchMode="singleTask"
android:parentActivityName=".launcher.SharedLauncherActivity"
android:screenOrientation="portrait"
android:taskAffinity="de.mm20.launcher2.settings"
android:theme="@style/SettingsTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.mm20.launcher2.ui.launcher.SharedLauncherActivity" />
</activity>
<activity
android:name=".launcher.sheets.BindAndConfigureAppWidgetActivity"
/>

View File

@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
@ -178,8 +177,8 @@ fun AssistantScaffold(
val value by searchVM.searchQuery
val searchBarColor by viewModel.searchBarColor.observeAsState(Settings.SearchBarSettings.SearchBarColors.Auto)
val searchBarStyle by viewModel.searchBarStyle.observeAsState(Settings.SearchBarSettings.SearchBarStyle.Transparent)
val searchBarColor by viewModel.searchBarColor.collectAsState()
val searchBarStyle by viewModel.searchBarStyle.collectAsState()
val launchOnEnter by searchVM.launchOnEnter.collectAsState(false)

View File

@ -1,27 +0,0 @@
package de.mm20.launcher2.ui.base
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class BaseActivityVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val theme = dataStore.data.map { it.appearance.theme }.asLiveData()
fun getTheme(): Settings.AppearanceSettings.Theme = runBlocking {
dataStore.data.map { it.appearance.theme }.first()
}
val colorScheme = dataStore.data.map { it.appearance.colorScheme }.asLiveData()
fun getColorScheme(): Settings.AppearanceSettings.ColorScheme = runBlocking {
dataStore.data.map { it.appearance.colorScheme }.first()
}
}

View File

@ -12,7 +12,6 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
@ -38,9 +37,9 @@ fun RestoreBackupSheet(
viewModel.setInputUri(uri)
}
val state by viewModel.state.observeAsState(RestoreBackupState.Parsing)
val selectedComponents by viewModel.selectedComponents.observeAsState(emptySet())
val compatibility by viewModel.compatibility.observeAsState(null)
val state by viewModel.state
val selectedComponents by viewModel.selectedComponents
val compatibility by viewModel.compatibility
BottomSheetDialog(
onDismissRequest = onDismissRequest,
@ -103,7 +102,7 @@ fun RestoreBackupSheet(
)
}
RestoreBackupState.Ready -> {
val metadata by viewModel.metadata.observeAsState(null)
val metadata by viewModel.metadata
if (metadata != null) {
Column {
@ -153,9 +152,7 @@ fun RestoreBackupSheet(
style = MaterialTheme.typography.titleSmall,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
)
val components by viewModel.availableComponents.observeAsState(
emptyList()
)
val components by viewModel.availableComponents
for (component in components) {
Row(
modifier = Modifier

View File

@ -1,7 +1,7 @@
package de.mm20.launcher2.ui.common
import android.net.Uri
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.backup.BackupCompatibility
@ -18,12 +18,12 @@ class RestoreBackupSheetVM : ViewModel(), KoinComponent {
private var restoreUri: Uri? = null
val state = MutableLiveData(RestoreBackupState.Parsing)
val metadata = MutableLiveData<BackupMetadata?>(null)
val compatibility = MutableLiveData<BackupCompatibility?>(null)
val selectedComponents = MutableLiveData(setOf<BackupComponent>())
val state = mutableStateOf(RestoreBackupState.Parsing)
val metadata = mutableStateOf<BackupMetadata?>(null)
val compatibility = mutableStateOf<BackupCompatibility?>(null)
val selectedComponents = mutableStateOf(setOf<BackupComponent>())
val availableComponents = MutableLiveData(emptyList<BackupComponent>())
val availableComponents = mutableStateOf(emptyList<BackupComponent>())
fun setInputUri(uri: Uri) {
restoreUri = uri

View File

@ -12,7 +12,6 @@ import androidx.compose.material.icons.rounded.Error
import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@ -29,8 +28,8 @@ fun WeatherLocationSearchDialog(
) {
val scope = rememberCoroutineScope()
val viewModel: WeatherLocationSearchDialogVM = viewModel()
val isSearching by viewModel.isSearchingLocation.observeAsState(initial = false)
val locations by viewModel.locationResults.observeAsState(emptyList())
val isSearching by viewModel.isSearchingLocation
val locations by viewModel.locationResults
BottomSheetDialog(
onDismissRequest = onDismissRequest,

View File

@ -1,6 +1,6 @@
package de.mm20.launcher2.ui.common
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import de.mm20.launcher2.weather.WeatherLocation
import de.mm20.launcher2.weather.WeatherRepository
@ -12,8 +12,8 @@ import kotlin.coroutines.coroutineContext
class WeatherLocationSearchDialogVM: ViewModel(), KoinComponent {
private val repository: WeatherRepository by inject()
val isSearchingLocation = MutableLiveData(false)
val locationResults = MutableLiveData<List<WeatherLocation>>(emptyList())
val isSearchingLocation = mutableStateOf(false)
val locationResults = mutableStateOf<List<WeatherLocation>>(emptyList())
private var debounceSearchJob: Job? = null
suspend fun searchLocation(query: String) {
@ -34,7 +34,7 @@ class WeatherLocationSearchDialogVM: ViewModel(), KoinComponent {
}
fun setLocation(location: WeatherLocation) {
locationResults.postValue(emptyList())
locationResults.value = emptyList()
repository.setAutoLocation(false)
repository.setLocation(location)
}

View File

@ -6,9 +6,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.app.ActivityOptionsCompat
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.globalactions.GlobalActionsService
@ -48,13 +46,17 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
) { dim, theme, systemDarkMode ->
dim && (theme == Settings.AppearanceSettings.Theme.Dark || theme == Settings.AppearanceSettings.Theme.System && systemDarkMode)
}
val dimBackground = dimBackgroundState.asLiveData()
val dimBackground = dimBackgroundState.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val statusBarColor = dataStore.data.map { it.systemBars.statusBarColor }.asLiveData()
val navBarColor = dataStore.data.map { it.systemBars.statusBarColor }.asLiveData()
val statusBarColor = dataStore.data.map { it.systemBars.statusBarColor }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val navBarColor = dataStore.data.map { it.systemBars.statusBarColor }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar }.asLiveData()
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar }.asLiveData()
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setSystemInDarkMode(darkMode: Boolean) {
isSystemInDarkMode.value = darkMode
@ -62,15 +64,19 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
val baseLayout = dataStore.data.map { it.layout.baseLayout }
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }.asLiveData()
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }.asLiveData()
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar }.asLiveData()
val fixedRotation = dataStore.data.map { it.layout.fixedRotation }.asLiveData()
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val fixedRotation = dataStore.data.map { it.layout.fixedRotation }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val isSearchOpen = MutableLiveData(false)
val isWidgetEditMode = MutableLiveData(false)
val isSearchOpen = mutableStateOf(false)
val isWidgetEditMode = mutableStateOf(false)
val searchBarFocused = MutableLiveData(false)
val searchBarFocused = mutableStateOf(false)
val autoFocusSearch = dataStore.data.map { it.searchBar.autoFocus }
@ -103,10 +109,14 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
isWidgetEditMode.value = editMode
}
val wallpaperBlur = dataStore.data.map { it.appearance.blurWallpaper }.asLiveData()
val fillClockHeight = dataStore.data.map { it.clockWidget.fillHeight }.asLiveData()
val searchBarColor = dataStore.data.map { it.searchBar.color }.asLiveData()
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }.asLiveData()
val wallpaperBlur = dataStore.data.map { it.appearance.blurWallpaper }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true)
val fillClockHeight = dataStore.data.map { it.clockWidget.fillHeight }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true)
val searchBarColor = dataStore.data.map { it.searchBar.color }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarColors.Auto)
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarStyle.Transparent)
val gestureState: StateFlow<GestureState> = dataStore
.data.map { it.gestures }

View File

@ -46,7 +46,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@ -101,8 +100,8 @@ fun PagerScaffold(
val context = LocalContext.current
val isSearchOpen by viewModel.isSearchOpen.observeAsState(false)
val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false)
val isSearchOpen by viewModel.isSearchOpen
val isWidgetEditMode by viewModel.isWidgetEditMode
val actions by searchVM.searchActionResults
@ -147,7 +146,7 @@ fun PagerScaffold(
}
}
val fillClockHeight by viewModel.fillClockHeight.observeAsState(true)
val fillClockHeight by viewModel.fillClockHeight.collectAsState()
val showNavBarScrim by remember {
derivedStateOf {
@ -199,7 +198,7 @@ fun PagerScaffold(
}
}
val blurEnabled by viewModel.wallpaperBlur.observeAsState(false)
val blurEnabled by viewModel.wallpaperBlur.collectAsState()
val blurWallpaper by remember {
derivedStateOf {
@ -369,11 +368,17 @@ fun PagerScaffold(
.fillMaxHeight()
.pointerInput(gestureManager.shouldDetectDoubleTaps) {
detectTapGestures(
onDoubleTap = if (gestureManager.shouldDetectDoubleTaps) {{
if (!isWidgetEditMode) gestureManager.dispatchDoubleTap(it)
}} else null,
onDoubleTap = if (gestureManager.shouldDetectDoubleTaps) {
{
if (!isWidgetEditMode) gestureManager.dispatchDoubleTap(
it
)
}
} else null,
onLongPress = {
if (!isWidgetEditMode) gestureManager.dispatchLongPress(it)
if (!isWidgetEditMode) gestureManager.dispatchLongPress(
it
)
},
onTap = {
if (!isWidgetEditMode) gestureManager.dispatchTap(it)
@ -491,7 +496,7 @@ fun PagerScaffold(
}
}
val focusSearchBar by viewModel.searchBarFocused.observeAsState(false)
val focusSearchBar by viewModel.searchBarFocused
val widgetEditModeOffset by animateDpAsState(
(if (isWidgetEditMode) 128.dp else 0.dp) * (if (bottomSearchBar) 1 else -1)
@ -499,8 +504,8 @@ fun PagerScaffold(
val value by searchVM.searchQuery
val searchBarColor by viewModel.searchBarColor.observeAsState(SearchBarColors.Auto)
val searchBarStyle by viewModel.searchBarStyle.observeAsState(SearchBarStyle.Transparent)
val searchBarColor by viewModel.searchBarColor.collectAsState()
val searchBarStyle by viewModel.searchBarStyle.collectAsState()
val launchOnEnter by searchVM.launchOnEnter.collectAsState(false)

View File

@ -43,7 +43,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@ -100,8 +99,8 @@ fun PullDownScaffold(
val actions by searchVM.searchActionResults
val isSearchOpen by viewModel.isSearchOpen.observeAsState(false)
val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false)
val isSearchOpen by viewModel.isSearchOpen
val isWidgetEditMode by viewModel.isWidgetEditMode
val widgetsScrollState = rememberScrollState()
val searchState = rememberLazyListState()
@ -148,7 +147,7 @@ fun PullDownScaffold(
}
}
val fillClockHeight by viewModel.fillClockHeight.observeAsState(true)
val fillClockHeight by viewModel.fillClockHeight.collectAsState()
val showStatusBarScrim by remember {
derivedStateOf {
@ -210,7 +209,7 @@ fun PullDownScaffold(
val maxSearchBarOffset = with(density) { 128.dp.toPx() }
val blurEnabled by viewModel.wallpaperBlur.observeAsState(false)
val blurEnabled by viewModel.wallpaperBlur.collectAsState()
val blurWallpaper by remember {
derivedStateOf {
@ -516,15 +515,15 @@ fun PullDownScaffold(
}
}
}
val searchBarFocused by viewModel.searchBarFocused.observeAsState(false)
val searchBarFocused by viewModel.searchBarFocused
val editModeSearchBarOffset by animateDpAsState(
(if (isWidgetEditMode) 128.dp else 0.dp) * (if (bottomSearchBar) 1 else -1)
)
val value by searchVM.searchQuery
val searchBarColor by viewModel.searchBarColor.observeAsState(Settings.SearchBarSettings.SearchBarColors.Auto)
val searchBarStyle by viewModel.searchBarStyle.observeAsState(Settings.SearchBarSettings.SearchBarStyle.Transparent)
val searchBarColor by viewModel.searchBarColor.collectAsState()
val searchBarStyle by viewModel.searchBarStyle.collectAsState()
val context = LocalContext.current

View File

@ -19,7 +19,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@ -90,7 +89,7 @@ abstract class SharedLauncherActivity(
setContent {
val snackbarHostState = remember { SnackbarHostState() }
val wallpaperColors by wallpaperColorsAsState()
val dimBackground by viewModel.dimBackground.observeAsState(false)
val dimBackground by viewModel.dimBackground.collectAsState()
CompositionLocalProvider(
LocalEnterHomeTransitionManager provides enterHomeTransitionManager,
LocalWindowSize provides windowSize,
@ -103,24 +102,24 @@ abstract class SharedLauncherActivity(
LauncherTheme {
ProvideCurrentTime {
ProvideSettings {
val statusBarColor by viewModel.statusBarColor.observeAsState(
SystemBarColors.Auto
)
val navBarColor by viewModel.navBarColor.observeAsState(SystemBarColors.Auto)
val statusBarColor by viewModel.statusBarColor.collectAsState()
val navBarColor by viewModel.navBarColor.collectAsState()
val lightStatus =
!dimBackground && (statusBarColor == SystemBarColors.Dark || statusBarColor == SystemBarColors.Auto && wallpaperColors.supportsDarkText)
val lightNav =
!dimBackground && (navBarColor == SystemBarColors.Dark || navBarColor == SystemBarColors.Auto && wallpaperColors.supportsDarkText)
val hideStatus by viewModel.hideStatusBar.observeAsState(false)
val hideNav by viewModel.hideNavBar.observeAsState(false)
val hideStatus by viewModel.hideStatusBar.collectAsState()
val hideNav by viewModel.hideNavBar.collectAsState()
val layout by viewModel.baseLayout.collectAsState(null)
val bottomSearchBar by viewModel.bottomSearchBar.observeAsState(false)
val reverseSearchResults by viewModel.reverseSearchResults.observeAsState(false)
val fixedSearchBar by viewModel.fixedSearchBar.observeAsState(false)
val bottomSearchBar by viewModel.bottomSearchBar.collectAsState()
val reverseSearchResults by viewModel.reverseSearchResults.collectAsState()
val fixedSearchBar by viewModel.fixedSearchBar.collectAsState()
viewModel.fixedRotation.observe(this) { fixedRotation ->
val fixedRotation by viewModel.fixedRotation.collectAsState()
LaunchedEffect(fixedRotation) {
requestedOrientation = if (fixedRotation) {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
} else {

View File

@ -13,7 +13,6 @@ import androidx.compose.material3.IconButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester

View File

@ -31,7 +31,6 @@ import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@ -180,11 +179,11 @@ fun CustomizeSearchableSheet(
val scope = rememberCoroutineScope()
val suggestions by remember { viewModel.getIconSuggestions(iconSizePx.toInt()) }
.observeAsState(emptyList())
.collectAsState(emptyList())
val defaultIcon by remember {
viewModel.getDefaultIcon(iconSizePx.toInt())
}.observeAsState()
}.collectAsState(null)
var query by remember { mutableStateOf("") }
var filterIconPack by remember { mutableStateOf<IconPack?>(null) }

View File

@ -1,7 +1,6 @@
package de.mm20.launcher2.ui.launcher.sheets
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.liveData
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
import de.mm20.launcher2.data.customattrs.CustomIcon
import de.mm20.launcher2.icons.CustomIconWithPreview
@ -14,6 +13,7 @@ import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
@ -32,7 +32,7 @@ class CustomizeSearchableSheetVM(
return iconService.getIcon(searchable, size)
}
fun getIconSuggestions(size: Int) = liveData {
fun getIconSuggestions(size: Int) = flow {
emit(iconService.getCustomIconSuggestions(searchable, size))
}
@ -49,7 +49,7 @@ class CustomizeSearchableSheetVM(
closeIconPicker()
}
fun getDefaultIcon(size: Int) = liveData {
fun getDefaultIcon(size: Int) = flow {
emit(iconService.getUncustomizedDefaultIcon(searchable, size))
}

View File

@ -51,7 +51,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@ -102,8 +101,8 @@ fun EditFavoritesSheet(
viewModel.reload()
}
val loading by viewModel.loading.observeAsState(true)
val createShortcutTarget by viewModel.createShortcutTarget.observeAsState(null)
val loading by viewModel.loading
val createShortcutTarget by viewModel.createShortcutTarget
BottomSheetDialog(
onDismissRequest = onDismiss,
@ -157,7 +156,7 @@ fun EditFavoritesSheet(
@Composable
fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM, paddingValues: PaddingValues) {
val items by viewModel.gridItems.observeAsState(emptyList())
val items by viewModel.gridItems
val columns = LocalGridSettings.current.columnCount
val availableTags by viewModel.availableTags
@ -336,10 +335,8 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM, paddingValues: Padding
}
}
}
val enableFrequentlyUsed by viewModel.enableFrequentlyUsed.observeAsState(
null
)
val frequentlyUsedRows by viewModel.frequentlyUsedRows.observeAsState(1)
val enableFrequentlyUsed by viewModel.enableFrequentlyUsed.collectAsState()
val frequentlyUsedRows by viewModel.frequentlyUsedRows.collectAsState()
AnimatedVisibility(showSettings) {
Surface(
modifier = Modifier

View File

@ -6,9 +6,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.appshortcuts.AppShortcutRepository
import de.mm20.launcher2.badges.Badge
@ -26,9 +24,11 @@ import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.data.Tag
import de.mm20.launcher2.services.favorites.FavoritesService
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -43,11 +43,11 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
private val permissionsManager: PermissionsManager by inject()
private val dataStore: LauncherDataStore by inject()
val gridItems = MutableLiveData<List<FavoritesSheetGridItem>>(emptyList())
val gridItems = mutableStateOf<List<FavoritesSheetGridItem>>(emptyList())
val loading = MutableLiveData(false)
val loading = mutableStateOf(false)
val createShortcutTarget = MutableLiveData<FavoritesSheetSection?>(null)
val createShortcutTarget = mutableStateOf<FavoritesSheetSection?>(null)
private var manuallySorted: MutableList<SavableSearchable> = mutableListOf()
private var automaticallySorted: MutableList<SavableSearchable> = mutableListOf()
@ -245,7 +245,8 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
}
}
val enableFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }.asLiveData()
val enableFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFrequentlyUsed(frequentlyUsed: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -260,7 +261,8 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
}
}
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }.asLiveData()
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -59,7 +59,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.launcher.widgets.picker.PickAppWidgetActivity
import de.mm20.launcher2.widgets.CalendarWidget
import de.mm20.launcher2.widgets.AppWidget
import de.mm20.launcher2.widgets.AppWidgetConfig
@ -111,7 +110,7 @@ class BindAndConfigureAppWidgetActivity : Activity() {
AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE,
appWidgetProviderInfo.profile
)
}, PickAppWidgetActivity.RequestCodeBind
}, RequestCodeBind
)
}
}
@ -122,7 +121,7 @@ class BindAndConfigureAppWidgetActivity : Activity() {
this,
appWidgetId,
0,
PickAppWidgetActivity.RequestCodeConfigure,
RequestCodeConfigure,
null
)
} else {

View File

@ -18,9 +18,9 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@ -77,7 +77,7 @@ fun WidgetColumn(
) {
val scope = rememberCoroutineScope()
Column {
val widgets by viewModel.widgets.observeAsState(emptyList())
val widgets by viewModel.widgets.collectAsState()
val swapThresholds = remember(widgets) {
Array(widgets.size) { floatArrayOf(0f, 0f) }
}
@ -144,7 +144,7 @@ fun WidgetColumn(
}
}
val editButton by viewModel.editButton.observeAsState()
val editButton by viewModel.editButton.collectAsState()
if (editButton == true) {
val icon =
AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_edit_add)

View File

@ -2,11 +2,13 @@ package de.mm20.launcher2.ui.launcher.widgets
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.widgets.Widget
import de.mm20.launcher2.widgets.WidgetRepository
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
@ -15,12 +17,14 @@ class WidgetsVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val editButton = dataStore.data.map { it.widgets.editButton }.asLiveData()
val editButton = dataStore.data.map { it.widgets.editButton }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val widgets = widgetRepository.get().asLiveData()
val widgets = widgetRepository.get()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
fun addWidget(widget: Widget) {
val widgets = widgets.value?.toMutableList() ?: return
val widgets = widgets.value.toMutableList()
widgets.add(widget)
widgetRepository.set(widgets)
}
@ -34,14 +38,14 @@ class WidgetsVM : ViewModel(), KoinComponent {
}
fun moveUp(index: Int) {
val widgets = widgets.value?.toMutableList() ?: return
val widgets = widgets.value.toMutableList()
val widget = widgets.removeAt(index)
widgets.add(index - 1, widget)
widgetRepository.set(widgets)
}
fun moveDown(index: Int) {
val widgets = widgets.value?.toMutableList() ?: return
val widgets = widgets.value.toMutableList()
val widget = widgets.removeAt(index)
widgets.add(index + 1, widget)
widgetRepository.set(widgets)

View File

@ -10,9 +10,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
@ -61,7 +59,7 @@ fun CalendarWidget(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.Center
) {
val selectedDate by viewModel.selectedDate.observeAsState(LocalDate.now())
val selectedDate by viewModel.selectedDate
var showDropdown by remember { mutableStateOf(false) }
TextButton(onClick = { showDropdown = true }) {
Text(
@ -99,8 +97,8 @@ fun CalendarWidget(
Icon(imageVector = Icons.Rounded.OpenInNew, contentDescription = null)
}
}
val events by viewModel.calendarEvents.observeAsState(emptyList())
val hasPermission by viewModel.hasPermission.observeAsState()
val events by viewModel.calendarEvents
val hasPermission by viewModel.hasPermission.collectAsState()
Column(
modifier = Modifier
.animateContentSize()
@ -124,7 +122,7 @@ fun CalendarWidget(
modifier = Modifier
.fillMaxWidth()
)
val runningEvents by viewModel.hiddenPastEvents.observeAsState(0)
val runningEvents by viewModel.hiddenPastEvents
if (runningEvents > 0) {
Info(
text = pluralStringResource(
@ -137,7 +135,7 @@ fun CalendarWidget(
}
)
}
val nextEvents by viewModel.nextEvents.observeAsState(emptyList())
val nextEvents by viewModel.nextEvents
if (nextEvents.isNotEmpty()) {
Text(
stringResource(R.string.calendar_widget_next_events),
@ -150,7 +148,7 @@ fun CalendarWidget(
.fillMaxWidth()
)
}
val pinnedEvents by viewModel.pinnedCalendarEvents.observeAsState(emptyList())
val pinnedEvents by viewModel.pinnedCalendarEvents.collectAsState()
if (pinnedEvents.isNotEmpty()) {
Text(
stringResource(R.string.calendar_widget_pinned_events),

View File

@ -5,23 +5,22 @@ import android.content.Context
import android.content.Intent
import android.provider.CalendarContract
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.calendar.CalendarRepository
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.CalendarEvent
import de.mm20.launcher2.services.favorites.FavoritesService
import de.mm20.launcher2.widgets.CalendarWidget
import de.mm20.launcher2.widgets.CalendarWidgetConfig
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.lang.Integer.min
@ -39,23 +38,24 @@ class CalendarWidgetVM : ViewModel(), KoinComponent {
private val widgetConfig = MutableStateFlow(CalendarWidgetConfig())
val calendarEvents = MutableLiveData<List<CalendarEvent>>(emptyList())
val calendarEvents = mutableStateOf<List<CalendarEvent>>(emptyList())
val pinnedCalendarEvents =
favoritesService.getFavorites(
includeTypes = listOf(CalendarEvent.Domain),
automaticallySorted = true,
manuallySorted = true,
).asLiveData(viewModelScope.coroutineContext)
val nextEvents = MutableLiveData<List<CalendarEvent>>(emptyList())
).stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
val nextEvents = mutableStateOf<List<CalendarEvent>>(emptyList())
var availableDates = listOf(LocalDate.now())
private val permissionsManager: PermissionsManager by inject()
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar).asLiveData()
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
private var showRunningPastDayEvents = false
val hiddenPastEvents = MutableLiveData(0)
val hiddenPastEvents = mutableStateOf(0)
val selectedDate = MutableLiveData(LocalDate.now())
val selectedDate = mutableStateOf(LocalDate.now())
fun updateWidget(widget: CalendarWidget) {
widgetConfig.value = widget.config
@ -147,17 +147,17 @@ class CalendarWidgetVM : ViewModel(), KoinComponent {
}
val hiddenCount = totalCount - events.size
hiddenPastEvents.postValue(hiddenCount)
hiddenPastEvents.value = hiddenCount
} else {
hiddenPastEvents.postValue(0)
hiddenPastEvents.value = 0
}
calendarEvents.postValue(events)
calendarEvents.value = events
val e = this.upcomingEvents
if (events.isEmpty() && e.isNotEmpty()) {
nextEvents.postValue(listOf(e[0]))
nextEvents.value = listOf(e[0])
} else {
nextEvents.postValue(emptyList())
nextEvents.value = emptyList()
}
}

View File

@ -16,8 +16,8 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -44,9 +44,9 @@ fun ClockWidget(
) {
val viewModel: ClockWidgetVM = viewModel()
val context = LocalContext.current
val layout by viewModel.layout.observeAsState()
val clockStyle by viewModel.clockStyle.observeAsState()
val color by viewModel.color.observeAsState()
val layout by viewModel.layout.collectAsState()
val clockStyle by viewModel.clockStyle.collectAsState()
val color by viewModel.color.collectAsState()
val time = LocalTime.current
LaunchedEffect(time) {

View File

@ -4,7 +4,6 @@ import android.content.Context
import android.content.Intent
import android.provider.AlarmClock
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.preferences.LauncherDataStore
@ -55,10 +54,13 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
}
}
val layout = dataStore.data.map { it.clockWidget.layout }.asLiveData()
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }.asLiveData()
val layout = dataStore.data.map { it.clockWidget.layout }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val color = dataStore.data.map { it.clockWidget.color }.asLiveData()
val color = dataStore.data.map { it.clockWidget.color }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun updateTime(time: Long) {
partProviders.value.forEach { it.setTime(time) }

View File

@ -15,12 +15,11 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService
import androidx.lifecycle.MutableLiveData
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import kotlinx.coroutines.channels.awaitClose
@ -29,7 +28,7 @@ import kotlinx.coroutines.flow.*
class AlarmPartProvider : PartProvider {
private val nextAlarmTime = MutableLiveData<Long?>(null)
private val nextAlarmTime = mutableStateOf<Long?>(null)
private val time = MutableStateFlow(System.currentTimeMillis())
@ -75,7 +74,7 @@ class AlarmPartProvider : PartProvider {
override fun Component(layout: ClockWidgetLayout) {
val context = LocalContext.current
val alarmTime by nextAlarmTime.observeAsState(null)
val alarmTime by nextAlarmTime
val time by this.time.collectAsState(System.currentTimeMillis())
alarmTime?.let {

View File

@ -5,10 +5,7 @@ import android.content.Context
import android.graphics.Bitmap
import android.media.session.PlaybackState.CustomAction
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.music.MusicService
import de.mm20.launcher2.music.PlaybackState
@ -16,7 +13,6 @@ import de.mm20.launcher2.music.SupportedActions
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

View File

@ -1,118 +0,0 @@
package de.mm20.launcher2.ui.launcher.widgets.picker
import android.appwidget.AppWidgetProviderInfo
import android.graphics.drawable.Drawable
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.ktx.toDp
@Composable
fun AppWidgetList(
modifier: Modifier = Modifier,
widgets: List<AppWidgetGroup>,
onWidgetSelected: (AppWidgetProviderInfo) -> Unit = {}
) {
val context = LocalContext.current
val density = (LocalDensity.current.density * 160).toInt()
LazyColumn(
modifier = modifier
) {
for (group in widgets) {
item {
Text(
modifier = Modifier.padding(
top = 16.dp,
start = 8.dp,
end = 8.dp,
bottom = 8.dp
),
text = group.appName,
style = MaterialTheme.typography.titleLarge
)
}
items(group.widgets) {
LauncherCard(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier
.clickable {
onWidgetSelected(it)
}
.padding(16.dp),
) {
val label = remember { it.loadLabel(context.packageManager) }
Text(text = label, style = MaterialTheme.typography.titleMedium)
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
) {
val image: Drawable? = remember {
it.loadPreviewImage(context, density) ?: it.loadIcon(
context,
density
)
}
if (image != null) {
val mod =
if (image.intrinsicWidth > 0 && image.intrinsicHeight > 0) {
Modifier
.heightIn(max = image.intrinsicHeight.toDp())
.widthIn(max = image.intrinsicWidth.toDp())
.aspectRatio(
image.intrinsicWidth.toFloat() / image.intrinsicHeight.toFloat(),
matchHeightConstraintsFirst = true
)
} else {
Modifier.size(64.dp)
}
Canvas(
modifier = mod
) {
drawIntoCanvas {
image.setBounds(
0,
0,
size.width.toInt(),
size.height.toInt(),
)
image.draw(it.nativeCanvas)
}
}
}
}
if (isAtLeastApiLevel(31)) {
val description = remember { it.loadDescription(context)?.toString() }
if (description != null) {
Text(text = description, style = MaterialTheme.typography.bodySmall)
}
}
}
}
}
}
}
}

View File

@ -1,168 +0,0 @@
package de.mm20.launcher2.ui.launcher.widgets.picker
import android.appwidget.AppWidgetHost
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.base.ProvideSettings
import de.mm20.launcher2.ui.theme.LauncherTheme
class PickAppWidgetActivity : BaseActivity() {
private val viewModel by viewModels<PickAppWidgetVM>()
private lateinit var widgetHost: AppWidgetHost
private lateinit var appWidgetManager: AppWidgetManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
widgetHost = AppWidgetHost(this, 44203)
appWidgetManager = AppWidgetManager.getInstance(this)
val availableWidgets = viewModel.getAvailableWidgets(this)
setContent {
LauncherTheme {
ProvideSettings {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = {
Text(stringResource(R.string.widget_add_widget))
},
navigationIcon = {
IconButton(onClick = { finish() }) {
Icon(
imageVector = Icons.Rounded.ArrowBack,
contentDescription = stringResource(
id = R.string.menu_back
)
)
}
}
)
}
) {
val available by availableWidgets.observeAsState()
val widgets = available
if (widgets != null) {
AppWidgetList(
modifier = Modifier
.fillMaxSize()
.padding(it),
widgets = widgets,
onWidgetSelected = {
selectAppWidget(it)
}
)
} else {
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
}
}
}
}
}
private fun selectAppWidget(widget: AppWidgetProviderInfo) {
val appWidgetId = widgetHost.allocateAppWidgetId()
bindAppWidget(widget, appWidgetId)
}
private fun bindAppWidget(widget: AppWidgetProviderInfo, appWidgetId: Int) {
val canBind = appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, widget.provider)
Log.d("MM20", "Can bind: $canBind")
if (canBind) {
configureAppWidget(widget, appWidgetId)
} else {
startActivityForResult(
Intent(AppWidgetManager.ACTION_APPWIDGET_BIND).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, widget.provider)
}, RequestCodeBind
)
}
}
private fun configureAppWidget(widget: AppWidgetProviderInfo, appWidgetId: Int) {
if (widget.configure != null) {
widgetHost.startAppWidgetConfigureActivityForResult(
this,
appWidgetId,
0,
RequestCodeConfigure,
null
)
} else {
finishWithResult(appWidgetId)
}
}
@Deprecated("Deprecated in super class")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
RequestCodeBind -> {
val appWidgetId =
data?.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
if (resultCode == RESULT_OK) {
val widget = appWidgetManager.getAppWidgetInfo(appWidgetId)
configureAppWidget(widget, appWidgetId)
} else {
widgetHost.deleteAppWidgetId(appWidgetId)
cancel()
}
}
RequestCodeConfigure -> {
val appWidgetId =
data?.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return cancel()
if (resultCode == RESULT_OK) {
finishWithResult(appWidgetId)
} else {
widgetHost.deleteAppWidgetId(appWidgetId)
cancel()
}
}
}
}
private fun finishWithResult(widgetId: Int) {
val data = Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
setResult(RESULT_OK, data)
finish()
}
private fun cancel() {
setResult(RESULT_CANCELED)
finish()
}
companion object {
const val RequestCodeConfigure = 1
const val RequestCodeBind = 2
}
}

View File

@ -1,45 +0,0 @@
package de.mm20.launcher2.ui.launcher.widgets.picker
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.content.Context
import android.content.pm.PackageManager
import androidx.compose.ui.text.toLowerCase
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import de.mm20.launcher2.crashreporter.CrashReporter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
typealias AppWidgetGroup = Pair<String, List<AppWidgetProviderInfo>>
inline val AppWidgetGroup.appName: String
get() = this.first
inline val AppWidgetGroup.widgets: List<AppWidgetProviderInfo>
get() = this.second
class PickAppWidgetVM : ViewModel() {
fun getAvailableWidgets(context: Context): LiveData<List<AppWidgetGroup>?> = liveData {
emit(null)
val appWidgetManager = AppWidgetManager.getInstance(context)
val widgets = withContext(Dispatchers.IO) {
appWidgetManager.installedProviders
.sortedBy { it.loadLabel(context.packageManager).lowercase() }
.groupBy {
val pkg = it.provider.packageName
val appInfo = try {
context.packageManager.getApplicationInfo(pkg, 0)
} catch (e: PackageManager.NameNotFoundException) {
CrashReporter.logException(e)
return@groupBy ""
}
appInfo.loadLabel(context.packageManager).toString()
}
.toList()
.sortedBy { it.first.lowercase() }
}
emit(widgets)
}
}

View File

@ -34,7 +34,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@ -81,7 +80,7 @@ fun WeatherWidget(widget: WeatherWidget) {
}
}
val selectedForecast by viewModel.currentForecast.observeAsState()
val selectedForecast by viewModel.currentForecast
val imperialUnits by viewModel.imperialUnits.collectAsState(false)
val compactMode = !widget.config.showForecast
@ -93,8 +92,8 @@ fun WeatherWidget(widget: WeatherWidget) {
}
val forecast = selectedForecast ?: run {
val hasPermission by viewModel.hasLocationPermission.observeAsState()
val autoLocation by viewModel.autoLocation.observeAsState()
val hasPermission by viewModel.hasLocationPermission.collectAsState()
val autoLocation by viewModel.autoLocation.collectAsState()
Column {
AnimatedVisibility(hasPermission == false && autoLocation == true) {
MissingPermissionBanner(
@ -126,9 +125,9 @@ fun WeatherWidget(widget: WeatherWidget) {
if (!compactMode) {
val dailyForecasts by viewModel.dailyForecasts.observeAsState(emptyList())
val selectedDayForecast by viewModel.currentDailyForecast.observeAsState()
val currentDayForecasts by viewModel.currentDayForecasts.observeAsState(emptyList())
val dailyForecasts by viewModel.dailyForecasts
val selectedDayForecast by viewModel.currentDailyForecast
val currentDayForecasts by viewModel.currentDayForecasts
Surface(
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = LocalCardStyle.current.opacity),

View File

@ -1,6 +1,7 @@
package de.mm20.launcher2.ui.launcher.widgets.weather
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.*
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
@ -8,8 +9,10 @@ import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.weather.DailyForecast
import de.mm20.launcher2.weather.Forecast
import de.mm20.launcher2.weather.WeatherRepository
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -30,16 +33,16 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
set(value) {
field = min(value, forecasts.lastIndex)
if (field < 0) {
currentForecast.postValue(null)
currentForecast.value = null
return
}
selectedForecastIndex = min(
selectedForecastIndex,
forecasts[value].hourlyForecasts.lastIndex
)
currentDayForecasts.postValue(forecasts[value].hourlyForecasts)
currentDailyForecast.postValue(forecasts[value])
currentForecast.postValue(getCurrentlySelectedForecast())
currentDayForecasts.value = forecasts[value].hourlyForecasts
currentDailyForecast.value = forecasts[value]
currentForecast.value = getCurrentlySelectedForecast()
}
/**
@ -48,11 +51,11 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
private var selectedForecastIndex = 0
set(value) {
if (selectedDayIndex < 0) {
currentForecast.postValue(null)
currentForecast.value = null
return
}
field = min(value, forecasts[selectedDayIndex].hourlyForecasts.lastIndex)
currentForecast.postValue(getCurrentlySelectedForecast())
currentForecast.value = getCurrentlySelectedForecast()
}
private val forecastsFlow = weatherRepository.forecasts
@ -65,29 +68,29 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
field = value
selectedDayIndex = 0
selectedForecastIndex = 0
dailyForecasts.postValue(value)
dailyForecasts.value = value
}
/**
* Currently selected forecast, one of [currentDayForecasts]
*/
val currentForecast = MutableLiveData<Forecast?>(getCurrentlySelectedForecast())
val currentForecast = mutableStateOf<Forecast?>(getCurrentlySelectedForecast())
/**
* List of forecast summaries for each day
*/
val dailyForecasts = MutableLiveData<List<DailyForecast>>(emptyList())
val dailyForecasts = mutableStateOf<List<DailyForecast>>(emptyList())
/**
* Forecasts of the currently selected day (hourly in most cases).
* This is [DailyForecast.hourlyForecasts] of [currentDailyForecast]
*/
val currentDayForecasts = MutableLiveData<List<Forecast>>(emptyList())
val currentDayForecasts = mutableStateOf<List<Forecast>>(emptyList())
/**
* Daily forecast summary for the currently selected day, one of [dailyForecasts] or null
*/
val currentDailyForecast = MutableLiveData<DailyForecast>(null)
val currentDailyForecast = mutableStateOf<DailyForecast?>(null)
init {
viewModelScope.launch {
@ -98,11 +101,13 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
}
}
val hasLocationPermission = permissionsManager.hasPermission(PermissionGroup.Location).asLiveData()
val hasLocationPermission = permissionsManager.hasPermission(PermissionGroup.Location)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun requestLocationPermission(context: AppCompatActivity) {
permissionsManager.requestPermission(context, PermissionGroup.Location)
}
val autoLocation = weatherRepository.autoLocation.asLiveData()
val autoLocation = weatherRepository.autoLocation
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val imperialUnits = dataStore.data.map { it.weather.imperialUnits }

View File

@ -1,82 +1,24 @@
package de.mm20.launcher2.ui.settings.appearance
import android.graphics.drawable.ColorDrawable
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
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.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.FormatPaint
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PlainTooltipBox
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.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.core.content.ContextCompat
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.HorizontalPagerIndicator
import com.google.accompanist.pager.rememberPagerState
import de.mm20.launcher2.icons.StaticIconLayer
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import de.mm20.launcher2.preferences.Settings.IconSettings
import de.mm20.launcher2.preferences.Settings.SearchBarSettings
import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors
import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarStyle
import de.mm20.launcher2.preferences.Settings.SystemBarsSettings.SystemBarColors
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.SearchBar
import de.mm20.launcher2.ui.component.SearchBarLevel
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
import de.mm20.launcher2.ui.component.getShape
import de.mm20.launcher2.ui.component.preferences.ListPreference
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.SliderPreference
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
import de.mm20.launcher2.ui.component.preferences.label
import de.mm20.launcher2.ui.component.preferences.value
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.theme.getTypography
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@Composable
fun AppearanceSettingsScreen() {
@ -86,7 +28,7 @@ fun AppearanceSettingsScreen() {
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item {
PreferenceCategory {
val theme by viewModel.theme.observeAsState()
val theme by viewModel.theme.collectAsState()
ListPreference(
title = stringResource(id = R.string.preference_theme),
items = listOf(
@ -100,7 +42,7 @@ fun AppearanceSettingsScreen() {
viewModel.setTheme(newValue)
}
)
val colorScheme by viewModel.colorScheme.observeAsState()
val colorScheme by viewModel.colorScheme.collectAsState()
Preference(
title = stringResource(id = R.string.preference_screen_colors),
summary = when (colorScheme) {
@ -113,7 +55,7 @@ fun AppearanceSettingsScreen() {
navController?.navigate("settings/appearance/colorscheme")
}
)
val font by viewModel.font.observeAsState()
val font by viewModel.font.collectAsState()
ListPreference(
title = stringResource(R.string.preference_font),
items = listOf(

View File

@ -1,26 +1,16 @@
package de.mm20.launcher2.ui.settings.appearance
import android.content.Context
import android.content.Intent
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.icons.IconPack
import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Font
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import de.mm20.launcher2.preferences.Settings.SearchBarSettings
import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors
import de.mm20.launcher2.preferences.Settings.SystemBarsSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -30,7 +20,8 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
private val iconService: IconService by inject()
val theme = dataStore.data.map { it.appearance.theme }.asLiveData()
val theme = dataStore.data.map { it.appearance.theme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setTheme(theme: Theme) {
viewModelScope.launch {
dataStore.updateData {
@ -41,7 +32,8 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val colorScheme = dataStore.data.map { it.appearance.colorScheme }.asLiveData()
val colorScheme = dataStore.data.map { it.appearance.colorScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setColorScheme(colorScheme: ColorScheme) {
viewModelScope.launch {
dataStore.updateData {
@ -52,7 +44,8 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val font = dataStore.data.map { it.appearance.font }.asLiveData()
val font = dataStore.data.map { it.appearance.font }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFont(font: Font) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings.backup
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
@ -19,11 +18,9 @@ import java.time.format.DateTimeFormatter
fun BackupSettingsScreen() {
val viewModel: BackupSettingsScreenVM = viewModel()
val restoreUri by viewModel.restoreUri.observeAsState()
val restoreUri by viewModel.restoreUri
val showBackupSheet by viewModel.showBackupSheet.observeAsState(false)
val context = LocalContext.current
val showBackupSheet by viewModel.showBackupSheet
val restoreLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),

View File

@ -1,15 +1,15 @@
package de.mm20.launcher2.ui.settings.backup
import android.net.Uri
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import org.koin.core.component.KoinComponent
class BackupSettingsScreenVM : ViewModel(), KoinComponent {
val showBackupSheet = MutableLiveData(false)
val showBackupSheet = mutableStateOf(false)
val restoreUri = MutableLiveData<Uri?>(null)
val restoreUri = mutableStateOf<Uri?>(null)
fun setShowBackupSheet(show: Boolean) {
showBackupSheet.value = show

View File

@ -12,7 +12,6 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
@ -38,8 +37,8 @@ fun CreateBackupSheet(
viewModel.reset()
}
val components by viewModel.selectedComponents.observeAsState(emptySet())
val state by viewModel.state.observeAsState(CreateBackupState.Ready)
val components by viewModel.selectedComponents
val state by viewModel.state
val backupLauncher = rememberLauncherForActivityResult(

View File

@ -1,7 +1,7 @@
package de.mm20.launcher2.ui.settings.backup
import android.net.Uri
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.backup.BackupComponent
@ -14,9 +14,9 @@ class CreateBackupSheetVM : ViewModel(), KoinComponent {
private val backupManager: BackupManager by inject()
val state = MutableLiveData(CreateBackupState.Ready)
val state = mutableStateOf(CreateBackupState.Ready)
val selectedComponents = MutableLiveData(BackupComponent.values().toSet())
val selectedComponents = mutableStateOf(BackupComponent.values().toSet())
fun reset() {
state.value = CreateBackupState.Ready

View File

@ -9,8 +9,8 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -43,7 +43,7 @@ fun CardsSettingsScreen() {
}
item {
PreferenceCategory {
val shape by viewModel.shape.observeAsState()
val shape by viewModel.shape.collectAsState()
ListPreference(
icon = Icons.Rounded.Rectangle,
title = stringResource(R.string.preference_cards_shape),
@ -55,7 +55,7 @@ fun CardsSettingsScreen() {
onValueChanged = {
if (it != null) viewModel.setShape(it)
})
val radius by viewModel.radius.observeAsState(0)
val radius by viewModel.radius.collectAsState()
SliderPreference(
title = stringResource(R.string.preference_cards_corner_radius),
icon = Icons.Rounded.RoundedCorner,
@ -67,7 +67,7 @@ fun CardsSettingsScreen() {
viewModel.setRadius(it)
}
)
val opacity by viewModel.opacity.observeAsState(0f)
val opacity by viewModel.opacity.collectAsState()
SliderPreference(
title = stringResource(R.string.preference_cards_opacity),
icon = Icons.Rounded.Opacity,
@ -78,7 +78,7 @@ fun CardsSettingsScreen() {
viewModel.setOpacity(it)
}
)
val borderWidth by viewModel.borderWidth.observeAsState(0)
val borderWidth by viewModel.borderWidth.collectAsState()
SliderPreference(
title = stringResource(R.string.preference_cards_stroke_width),
icon = Icons.Rounded.LineWeight,

View File

@ -1,11 +1,12 @@
package de.mm20.launcher2.ui.settings.cards
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -13,7 +14,8 @@ import org.koin.core.component.inject
class CardsSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val opacity = dataStore.data.map { it.cards.opacity }.asLiveData()
val opacity = dataStore.data.map { it.cards.opacity }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0f)
fun setOpacity(opacity: Float) {
viewModelScope.launch {
dataStore.updateData {
@ -25,7 +27,9 @@ class CardsSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val radius = dataStore.data.map { it.cards.radius }.asLiveData()
val radius = dataStore.data.map { it.cards.radius }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
fun setRadius(radius: Int) {
viewModelScope.launch {
dataStore.updateData {
@ -37,7 +41,8 @@ class CardsSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val borderWidth = dataStore.data.map { it.cards.borderWidth }.asLiveData()
val borderWidth = dataStore.data.map { it.cards.borderWidth }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
fun setBorderWidth(borderWidth: Int) {
viewModelScope.launch {
dataStore.updateData {
@ -49,7 +54,9 @@ class CardsSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val shape = dataStore.data.map { it.cards.shape }.asLiveData()
val shape = dataStore.data.map { it.cards.shape }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setShape(shape: Settings.CardSettings.Shape) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -8,17 +8,14 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.HorizontalPagerIndicator
import com.google.accompanist.pager.rememberPagerState
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
@ -35,7 +32,7 @@ fun ClockWidgetSettingsScreen() {
) {
item {
PreferenceCategory {
val layout by viewModel.layout.observeAsState()
val layout by viewModel.layout.collectAsState()
ListPreference(
title = stringResource(R.string.preference_clockwidget_layout),
value = layout,
@ -47,7 +44,7 @@ fun ClockWidgetSettingsScreen() {
if (it != null) viewModel.setLayout(it)
}
)
val clockStyle by viewModel.clockStyle.observeAsState()
val clockStyle by viewModel.clockStyle.collectAsState()
ClockStylePreference(
layout = layout ?: ClockWidgetLayout.Vertical,
value = clockStyle,
@ -55,7 +52,7 @@ fun ClockWidgetSettingsScreen() {
viewModel.setClockStyle(it)
}
)
val color by viewModel.color.observeAsState()
val color by viewModel.color.collectAsState()
ListPreference(
title = stringResource(R.string.preference_clock_widget_color),
value = color,
@ -68,7 +65,7 @@ fun ClockWidgetSettingsScreen() {
if (it != null) viewModel.setColor(it)
}
)
val fillHeight by viewModel.fillHeight.observeAsState()
val fillHeight by viewModel.fillHeight.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_clock_widget_fill_height),
summary = stringResource(R.string.preference_clock_widget_fill_height_summary),
@ -79,7 +76,7 @@ fun ClockWidgetSettingsScreen() {
}
item {
PreferenceCategory {
val datePart by viewModel.datePart.observeAsState()
val datePart by viewModel.datePart.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_clockwidget_date_part),
summary = stringResource(R.string.preference_clockwidget_date_part_summary),
@ -89,7 +86,7 @@ fun ClockWidgetSettingsScreen() {
viewModel.setDatePart(it)
},
)
val favoritesPart by viewModel.favoritesPart.observeAsState()
val favoritesPart by viewModel.favoritesPart.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_clockwidget_favorites_part),
summary = stringResource(R.string.preference_clockwidget_favorites_part_summary),
@ -103,7 +100,7 @@ fun ClockWidgetSettingsScreen() {
}
item {
PreferenceCategory {
val musicPart by viewModel.musicPart.observeAsState()
val musicPart by viewModel.musicPart.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_clockwidget_music_part),
summary = stringResource(R.string.preference_clockwidget_music_part_summary),
@ -113,7 +110,7 @@ fun ClockWidgetSettingsScreen() {
viewModel.setMusicPart(it)
}
)
val alarmPart by viewModel.alarmPart.observeAsState()
val alarmPart by viewModel.alarmPart.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_clockwidget_alarm_part),
summary = stringResource(R.string.preference_clockwidget_alarm_part_summary),
@ -123,7 +120,7 @@ fun ClockWidgetSettingsScreen() {
viewModel.setAlarmPart(it)
}
)
val batteryPart by viewModel.batteryPart.observeAsState()
val batteryPart by viewModel.batteryPart.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_clockwidget_battery_part),
summary = stringResource(R.string.preference_clockwidget_battery_part_summary),

View File

@ -1,20 +1,21 @@
package de.mm20.launcher2.ui.settings.clockwidget
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val layout = dataStore.data.map { it.clockWidget.layout }.asLiveData()
val layout = dataStore.data.map { it.clockWidget.layout }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLayout(layout: ClockWidgetSettings.ClockWidgetLayout) {
viewModelScope.launch {
dataStore.updateData {
@ -27,7 +28,9 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }.asLiveData()
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setClockStyle(clockStyle: ClockWidgetSettings.ClockStyle) {
viewModelScope.launch {
dataStore.updateData {
@ -40,7 +43,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val color = dataStore.data.map { it.clockWidget.color }.asLiveData()
val color = dataStore.data.map { it.clockWidget.color }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setColor(color: ClockWidgetColors) {
viewModelScope.launch {
dataStore.updateData {
@ -53,7 +57,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val fillHeight = dataStore.data.map { it.clockWidget.fillHeight }.asLiveData()
val fillHeight = dataStore.data.map { it.clockWidget.fillHeight }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFillHeight(fillHeight: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -66,7 +71,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val datePart = dataStore.data.map { it.clockWidget.datePart }.asLiveData()
val datePart = dataStore.data.map { it.clockWidget.datePart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setDatePart(datePart: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -79,7 +85,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val favoritesPart = dataStore.data.map { it.clockWidget.favoritesPart }.asLiveData()
val favoritesPart = dataStore.data.map { it.clockWidget.favoritesPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFavoritesPart(favoritesPart: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -92,7 +99,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val batteryPart = dataStore.data.map { it.clockWidget.batteryPart }.asLiveData()
val batteryPart = dataStore.data.map { it.clockWidget.batteryPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBatteryPart(batteryPart: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -105,7 +113,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val musicPart = dataStore.data.map { it.clockWidget.musicPart }.asLiveData()
val musicPart = dataStore.data.map { it.clockWidget.musicPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setMusicPart(musicPart: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -118,7 +127,8 @@ class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val alarmPart = dataStore.data.map { it.clockWidget.alarmPart }.asLiveData()
val alarmPart = dataStore.data.map { it.clockWidget.alarmPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAlarmPart(alarmPart: Boolean) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -8,8 +8,8 @@ import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@ -34,7 +34,7 @@ fun ColorSchemeSettingsScreen() {
PreferenceScreen(title = stringResource(R.string.preference_screen_colors)) {
item {
PreferenceCategory {
val colorScheme by viewModel.colorScheme.observeAsState()
val colorScheme by viewModel.colorScheme.collectAsState()
val items = mutableListOf(
AppearanceSettings.ColorScheme.Default to stringResource(R.string.preference_colors_default),

View File

@ -1,11 +1,12 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -13,9 +14,11 @@ import org.koin.core.component.inject
class ColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val theme = dataStore.data.map { it.appearance.theme }.asLiveData()
val theme = dataStore.data.map { it.appearance.theme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val colorScheme = dataStore.data.map { it.appearance.colorScheme }.asLiveData()
val colorScheme = dataStore.data.map { it.appearance.colorScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setColorScheme(colorScheme: AppearanceSettings.ColorScheme) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -4,7 +4,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.stringResource
@ -18,7 +17,7 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
fun CustomColorSchemeSettingsScreen() {
val viewModel: CustomColorSchemeSettingsScreenVM = viewModel()
val advancedMode by viewModel.advancedMode.observeAsState()
val advancedMode by viewModel.advancedMode.collectAsState()
PreferenceScreen(
title = stringResource(R.string.preference_screen_colors),
@ -64,7 +63,7 @@ fun CustomColorSchemeSettingsScreen() {
if (advancedMode == false) {
item {
PreferenceCategory {
val baseColors by viewModel.baseColors.observeAsState()
val baseColors by viewModel.baseColors.collectAsState()
ColorPreference(
title = stringResource(R.string.preference_custom_colors_a1),
value = baseColors?.let { Color(it.accent1) },
@ -149,7 +148,7 @@ fun CustomColorSchemeSettingsScreen() {
if (advancedMode == true) {
item {
PreferenceCategory(stringResource(R.string.preference_category_custom_colors_light)) {
val lightScheme by viewModel.lightScheme.observeAsState()
val lightScheme by viewModel.lightScheme.collectAsState()
ColorPreference(
title = "Primary",
value = lightScheme?.let { Color(it.primary) },
@ -513,7 +512,7 @@ fun CustomColorSchemeSettingsScreen() {
}
PreferenceCategory(stringResource(R.string.preference_category_custom_colors_dark)) {
val darkScheme by viewModel.darkScheme.observeAsState()
val darkScheme by viewModel.darkScheme.collectAsState()
ColorPreference(
title = "Primary",
value = darkScheme?.let { Color(it.primary) },

View File

@ -1,13 +1,13 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.CustomColors
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -17,7 +17,8 @@ import scheme.Scheme
class CustomColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val advancedMode = dataStore.data.map { it.appearance.customColors.advancedMode }.asLiveData()
val advancedMode = dataStore.data.map { it.appearance.customColors.advancedMode }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAdvancedMode(advancedMode: Boolean) {
viewModelScope.launch {
val lightScheme = dataStore.updateData {
@ -63,7 +64,8 @@ class CustomColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val baseColors = dataStore.data.map { it.appearance.customColors.baseColors }.asLiveData()
val baseColors = dataStore.data.map { it.appearance.customColors.baseColors }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBaseColors(baseColors: CustomColors.BaseColors) {
viewModelScope.launch {
dataStore.updateData {
@ -82,7 +84,8 @@ class CustomColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val darkScheme = dataStore.data.map { it.appearance.customColors.darkScheme }.asLiveData()
val darkScheme = dataStore.data.map { it.appearance.customColors.darkScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setDarkScheme(darkScheme: CustomColors.Scheme) {
viewModelScope.launch {
dataStore.updateData {
@ -99,7 +102,8 @@ class CustomColorSchemeSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val lightScheme = dataStore.data.map { it.appearance.customColors.lightScheme }.asLiveData()
val lightScheme = dataStore.data.map { it.appearance.customColors.lightScheme }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLightScheme(lightScheme: CustomColors.Scheme) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -6,14 +6,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.BugReport
import androidx.compose.material.icons.rounded.Share
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -26,7 +25,7 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
fun CrashReportScreen(fileName: String) {
val viewModel: CrashReportScreenVM = viewModel()
val context = LocalContext.current
val crashReport by remember(fileName) { viewModel.getCrashReport(fileName) }.observeAsState()
val crashReport by remember(fileName) { viewModel.getCrashReport(fileName) }.collectAsState(null)
PreferenceScreen(
title = when (crashReport?.type) {
CrashReportType.Exception -> "Exception"

View File

@ -5,14 +5,14 @@ import android.content.Intent
import android.net.Uri
import androidx.core.content.FileProvider
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import de.mm20.launcher2.crashreporter.CrashReport
import de.mm20.launcher2.crashreporter.CrashReporter
import kotlinx.coroutines.flow.flow
import java.io.File
import java.net.URLEncoder
class CrashReportScreenVM : ViewModel() {
fun getCrashReport(fileName: String) = liveData<CrashReport?> {
fun getCrashReport(fileName: String) = flow<CrashReport?> {
emit(CrashReporter.getCrashReport(fileName))
}

View File

@ -13,7 +13,6 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@ -32,9 +31,9 @@ import java.net.URLEncoder
fun CrashReporterScreen() {
val viewModel: CrashReporterScreenVM = viewModel()
val navController = LocalNavController.current
val reports by viewModel.reports.observeAsState()
val showExceptions by viewModel.showExceptions.observeAsState(true)
val showCrashes by viewModel.showCrashes.observeAsState(true)
val reports by viewModel.reports
val showExceptions by viewModel.showExceptions
val showCrashes by viewModel.showCrashes
PreferenceScreen(
title = stringResource(R.string.preference_crash_reporter),
helpUrl = "https://kvaesitso.mm20.de/docs/user-guide/troubleshooting/crashreporter"

View File

@ -1,6 +1,6 @@
package de.mm20.launcher2.ui.settings.crashreporter
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.crashreporter.BuildConfig
@ -29,10 +29,10 @@ class CrashReporterScreenVM: ViewModel() {
}
}
val showExceptions = MutableLiveData(false)
val showCrashes = MutableLiveData(true)
val showExceptions = mutableStateOf(false)
val showCrashes = mutableStateOf(true)
val reports = MutableLiveData<List<CrashReport>?>(null)
val reports = mutableStateOf<List<CrashReport>?>(null)
private var _reports: List<CrashReport>? = null
init {

View File

@ -12,8 +12,8 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -30,7 +30,7 @@ fun EasterEggSettingsScreen() {
val viewModel: EasterEggSettingsScreenVM = viewModel()
PreferenceScreen(title = stringResource(R.string.preference_screen_about)) {
item {
val easterEgg by viewModel.easterEgg.observeAsState(false)
val easterEgg by viewModel.easterEgg.collectAsState()
val bgAlpha by animateFloatAsState(if (easterEgg) 1f else 0f)
val textColor by animateColorAsState(if (easterEgg) MaterialTheme.colorScheme.onSecondaryContainer else MaterialTheme.colorScheme.onBackground)
Column(

View File

@ -1,10 +1,11 @@
package de.mm20.launcher2.ui.settings.easteregg
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -12,7 +13,8 @@ import org.koin.core.component.inject
class EasterEggSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val easterEgg = dataStore.data.map { it.easterEgg }.asLiveData()
val easterEgg = dataStore.data.map { it.easterEgg }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setEasterEgg(easterEgg: Boolean) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -7,8 +7,8 @@ import androidx.compose.material.icons.rounded.Sort
import androidx.compose.material.icons.rounded.SwapVert
import androidx.compose.material.icons.rounded.TableRows
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@ -45,7 +45,7 @@ fun FavoritesSettingsScreen() {
}
item {
PreferenceCategory(stringResource(R.string.preference_category_favorites_frequently_used)) {
val frequentlyUsed by viewModel.frequentlyUsed.observeAsState()
val frequentlyUsed by viewModel.frequentlyUsed.collectAsState()
SwitchPreference(
title = stringResource(R.string.frequently_used_show_in_favorites),
summary = stringResource(R.string.preference_favorites_frequently_used_summary),
@ -55,7 +55,7 @@ fun FavoritesSettingsScreen() {
},
icon = Icons.Rounded.Insights
)
val frequentlyUsedRows by viewModel.frequentlyUsedRows.observeAsState(1)
val frequentlyUsedRows by viewModel.frequentlyUsedRows.collectAsState()
SliderPreference(
title = stringResource(R.string.frequently_used_rows),
value = frequentlyUsedRows,
@ -67,7 +67,7 @@ fun FavoritesSettingsScreen() {
},
icon = Icons.Rounded.TableRows
)
val searchResultWeightFactor by viewModel.searchResultWeightFactor.observeAsState(WeightFactor.Default)
val searchResultWeightFactor by viewModel.searchResultWeightFactor.collectAsState()
ListPreference(
title = stringResource(R.string.preference_search_result_ordering_weight_factor),
icon = Icons.Rounded.SwapVert,
@ -82,7 +82,7 @@ fun FavoritesSettingsScreen() {
}
}
item {
val editButton by viewModel.editButton.observeAsState()
val editButton by viewModel.editButton.collectAsState()
PreferenceCategory {
SwitchPreference(
title = stringResource(R.string.preference_edit_button),

View File

@ -1,11 +1,12 @@
package de.mm20.launcher2.ui.settings.favorites
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -13,7 +14,8 @@ import org.koin.core.component.inject
class FavoritesSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val frequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }.asLiveData()
val frequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFrequentlyUsed(frequentlyUsed: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -27,7 +29,8 @@ class FavoritesSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }.asLiveData()
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
viewModelScope.launch {
dataStore.updateData {
@ -41,7 +44,8 @@ class FavoritesSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val editButton = dataStore.data.map { it.favorites.editButton }.asLiveData()
val editButton = dataStore.data.map { it.favorites.editButton }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setEditButton(editButton: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -55,7 +59,8 @@ class FavoritesSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val searchResultWeightFactor = dataStore.data.map { it.resultOrdering.weightFactor }.asLiveData()
val searchResultWeightFactor = dataStore.data.map { it.resultOrdering.weightFactor }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), WeightFactor.Default)
fun setSearchResultWeightFactor(searchResultWeightFactor: WeightFactor) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -11,8 +11,8 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
@ -40,7 +40,7 @@ fun FileSearchSettingsScreen() {
viewModel.onResume()
}
}
val loading by viewModel.loading.observeAsState()
val loading by viewModel.loading
PreferenceScreen(title = stringResource(R.string.preference_search_files)) {
if (loading == true) {
item {
@ -52,8 +52,8 @@ fun FileSearchSettingsScreen() {
}
item {
PreferenceCategory {
val localFiles by viewModel.localFiles.observeAsState()
val hasFilePermission by viewModel.hasFilePermission.observeAsState()
val localFiles by viewModel.localFiles.collectAsState()
val hasFilePermission by viewModel.hasFilePermission.collectAsState()
AnimatedVisibility(hasFilePermission == false) {
MissingPermissionBanner(
text = stringResource(
@ -74,8 +74,8 @@ fun FileSearchSettingsScreen() {
enabled = hasFilePermission == true
)
val nextcloud by viewModel.nextcloud.observeAsState()
val nextcloudAccount by viewModel.nextcloudAccount.observeAsState()
val nextcloud by viewModel.nextcloud.collectAsState()
val nextcloudAccount by viewModel.nextcloudAccount
AnimatedVisibility(nextcloudAccount == null) {
Banner(
text = stringResource(R.string.no_account_nextcloud),
@ -107,8 +107,8 @@ fun FileSearchSettingsScreen() {
enabled = nextcloudAccount != null
)
val owncloud by viewModel.owncloud.observeAsState()
val owncloudAccount by viewModel.owncloudAccount.observeAsState()
val owncloud by viewModel.owncloud.collectAsState()
val owncloudAccount by viewModel.owncloudAccount
AnimatedVisibility(owncloudAccount == null) {
Banner(
text = stringResource(R.string.no_account_owncloud),
@ -141,8 +141,8 @@ fun FileSearchSettingsScreen() {
)
if (viewModel.microsoftAvailable) {
val onedrive by viewModel.onedrive.observeAsState()
val microsoftAccount by viewModel.microsoftAccount.observeAsState()
val onedrive by viewModel.onedrive.collectAsState()
val microsoftAccount by viewModel.microsoftAccount
AnimatedVisibility(microsoftAccount == null) {
Banner(
text = stringResource(R.string.no_account_microsoft),
@ -176,8 +176,8 @@ fun FileSearchSettingsScreen() {
}
if (viewModel.googleAvailable) {
val gdrive by viewModel.gdrive.observeAsState()
val googleAccount by viewModel.googleAccount.observeAsState()
val gdrive by viewModel.gdrive.collectAsState()
val googleAccount by viewModel.googleAccount
AnimatedVisibility(googleAccount == null) {
Banner(
text = stringResource(R.string.no_account_google),

View File

@ -1,9 +1,8 @@
package de.mm20.launcher2.ui.settings.filesearch
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.accounts.Account
import de.mm20.launcher2.accounts.AccountType
@ -11,7 +10,9 @@ import de.mm20.launcher2.accounts.AccountsRepository
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -21,14 +22,14 @@ class FileSearchSettingsScreenVM : ViewModel(), KoinComponent {
private val accountsRepository: AccountsRepository by inject()
private val permissionsManager: PermissionsManager by inject()
val hasFilePermission =
permissionsManager.hasPermission(PermissionGroup.ExternalStorage).asLiveData()
val hasFilePermission = permissionsManager.hasPermission(PermissionGroup.ExternalStorage)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val loading = MutableLiveData(true)
val nextcloudAccount = MutableLiveData<Account?>(null)
val owncloudAccount = MutableLiveData<Account?>(null)
val microsoftAccount = MutableLiveData<Account?>(null)
val googleAccount = MutableLiveData<Account?>(null)
val loading = mutableStateOf(true)
val nextcloudAccount = mutableStateOf<Account?>(null)
val owncloudAccount = mutableStateOf<Account?>(null)
val microsoftAccount = mutableStateOf<Account?>(null)
val googleAccount = mutableStateOf<Account?>(null)
val microsoftAvailable = accountsRepository.isSupported(AccountType.Microsoft)
val googleAvailable = accountsRepository.isSupported(AccountType.Google)
@ -46,7 +47,8 @@ class FileSearchSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val localFiles = dataStore.data.map { it.fileSearch.localFiles }.asLiveData()
val localFiles = dataStore.data.map { it.fileSearch.localFiles }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLocalFiles(localFiles: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -61,7 +63,8 @@ class FileSearchSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val nextcloud = dataStore.data.map { it.fileSearch.nextcloud }.asLiveData()
val nextcloud = dataStore.data.map { it.fileSearch.nextcloud }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setNextcloud(nextcloud: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -76,7 +79,8 @@ class FileSearchSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val gdrive = dataStore.data.map { it.fileSearch.gdrive }.asLiveData()
val gdrive = dataStore.data.map { it.fileSearch.gdrive }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setGdrive(gdrive: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -91,7 +95,8 @@ class FileSearchSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val onedrive = dataStore.data.map { it.fileSearch.onedrive }.asLiveData()
val onedrive = dataStore.data.map { it.fileSearch.onedrive }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setOneDrive(onedrive: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -106,7 +111,8 @@ class FileSearchSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val owncloud = dataStore.data.map { it.fileSearch.owncloud }.asLiveData()
val owncloud = dataStore.data.map { it.fileSearch.owncloud }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setOwncloud(owncloud: Boolean) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -14,7 +14,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue

View File

@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.settings.gestures
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.icons.IconService
@ -29,8 +28,10 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent {
private val iconService: IconService by inject()
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val baseLayout = dataStore.data.map { it.layout.baseLayout }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBaseLayout(baseLayout: Settings.LayoutSettings.Layout) {
viewModelScope.launch {
@ -43,10 +44,15 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent {
}
val swipeDown = dataStore.data.map { it.gestures.swipeDown }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val swipeLeft = dataStore.data.map { it.gestures.swipeLeft }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val swipeRight = dataStore.data.map { it.gestures.swipeRight }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val doubleTap = dataStore.data.map { it.gestures.doubleTap }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val longPress = dataStore.data.map { it.gestures.longPress }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSwipeDown(action: GestureAction) {
viewModelScope.launch {

View File

@ -9,7 +9,6 @@ import androidx.compose.material.icons.rounded.Visibility
import androidx.compose.material.icons.rounded.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@ -31,8 +30,8 @@ fun HiddenItemsSettingsScreen() {
val context = LocalContext.current
val density = LocalDensity.current
val apps by viewModel.allApps.observeAsState(emptyList())
val other by viewModel.hiddenItems.observeAsState(emptyList())
val apps by viewModel.allApps.collectAsState()
val other by viewModel.hiddenItems.collectAsState()
PreferenceScreen(title = stringResource(R.string.preference_hidden_items)) {
items(apps, key = { it.key }) { searchable ->
val icon by remember(searchable.key) {

View File

@ -5,10 +5,8 @@ import android.content.Context
import android.content.pm.LauncherApps
import android.os.Bundle
import androidx.core.content.getSystemService
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.applications.AppRepository
import de.mm20.launcher2.searchable.SearchableRepository
import de.mm20.launcher2.icons.IconService
@ -18,8 +16,12 @@ import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.data.LauncherApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -31,11 +33,11 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
val allApps = appRepository.getAllInstalledApps().map {
withContext(Dispatchers.Default) { it.sorted() }
}.asLiveData()
val hiddenItems: LiveData<List<SavableSearchable>> = liveData {
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
val hiddenItems: StateFlow<List<SavableSearchable>> = flow {
val hidden = searchableRepository.get(hidden = true).first().filter { it !is LauncherApp }.sorted()
emit(hidden)
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
fun isHidden(searchable: SavableSearchable): Flow<Boolean> {
return searchableRepository.isHidden(searchable)

View File

@ -55,8 +55,8 @@ fun HomescreenSettingsScreen() {
val bottomSearchBar by viewModel.bottomSearchBar.collectAsStateWithLifecycle(null)
val fixedSearchBar by viewModel.fixedSearchBar.collectAsStateWithLifecycle(null)
val lightStatusBar by viewModel.statusBarIcons.collectAsStateWithLifecycle(null)
val dimWallpaper by viewModel.dimWallpaper.collectAsStateWithLifecycle(false)
val blurWallpaper by viewModel.blurWallpaper.collectAsStateWithLifecycle(false)
val dimWallpaper by viewModel.dimWallpaper.collectAsStateWithLifecycle()
val blurWallpaper by viewModel.blurWallpaper.collectAsStateWithLifecycle()
val lightNavBar by viewModel.navBarIcons.collectAsStateWithLifecycle(null)
val hideStatusBar by viewModel.hideStatusBar.collectAsStateWithLifecycle(null)
val hideNavBar by viewModel.hideNavBar.collectAsStateWithLifecycle(null)

View File

@ -6,14 +6,15 @@ import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@ -24,6 +25,7 @@ class HomescreenSettingsScreenVM(
val dimWallpaper = dataStore.data.map { it.appearance.dimWallpaper }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setDimWallpaper(dimWallpaper: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -37,6 +39,7 @@ class HomescreenSettingsScreenVM(
}
val blurWallpaper = dataStore.data.map { it.appearance.blurWallpaper }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setBlurWallpaper(blurWallpaper: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -59,6 +62,7 @@ class HomescreenSettingsScreenVM(
}
val statusBarIcons = dataStore.data.map { it.systemBars.statusBarColor }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLightStatusBar(statusBarColor: Settings.SystemBarsSettings.SystemBarColors) {
viewModelScope.launch {
dataStore.updateData {
@ -73,6 +77,7 @@ class HomescreenSettingsScreenVM(
}
val navBarIcons = dataStore.data.map { it.systemBars.navBarColor }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLightNavBar(navBarColors: Settings.SystemBarsSettings.SystemBarColors) {
viewModelScope.launch {
dataStore.updateData {
@ -87,6 +92,7 @@ class HomescreenSettingsScreenVM(
}
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setHideStatusBar(hideStatusBar: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -101,6 +107,7 @@ class HomescreenSettingsScreenVM(
}
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setHideNavBar(hideNavBar: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -115,6 +122,7 @@ class HomescreenSettingsScreenVM(
}
val searchBarColor = dataStore.data.map { it.searchBar.color }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSearchBarColor(color: Settings.SearchBarSettings.SearchBarColors) {
viewModelScope.launch {
dataStore.updateData {
@ -128,9 +136,8 @@ class HomescreenSettingsScreenVM(
}
}
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSearchBarStyle(searchBarStyle: Settings.SearchBarSettings.SearchBarStyle) {
viewModelScope.launch {
dataStore.updateData {
@ -145,6 +152,7 @@ class HomescreenSettingsScreenVM(
}
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFixedSearchBar(fixedSearchBar: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -156,6 +164,7 @@ class HomescreenSettingsScreenVM(
}
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBottomSearchBar(bottomSearchBar: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -167,6 +176,7 @@ class HomescreenSettingsScreenVM(
}
val fixedRotation = dataStore.data.map { it.layout.fixedRotation }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFixedRotation(fixedRotation: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -178,6 +188,7 @@ class HomescreenSettingsScreenVM(
}
val widgetEditButton = dataStore.data.map { it.widgets.editButton }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWidgetEditButton(editButton: Boolean) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.settings.integrations
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.accounts.Account

View File

@ -11,8 +11,8 @@ import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.OpenInBrowser
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -102,7 +102,7 @@ fun LicenseScreen(library: OpenSourceLibrary) {
style = MaterialTheme.typography.bodySmall
)
}
val licenseText by viewModel.getLicenseText(library).observeAsState()
val licenseText by viewModel.getLicenseText(library).collectAsState(null)
licenseText?.let {
Text(
text = it,

View File

@ -2,13 +2,13 @@ package de.mm20.launcher2.ui.settings.license
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.liveData
import de.mm20.launcher2.licenses.OpenSourceLibrary
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
class LicenseScreenVM(private val context: Application) : AndroidViewModel(context) {
fun getLicenseText(library: OpenSourceLibrary) = liveData<String?> {
fun getLicenseText(library: OpenSourceLibrary) = flow<String?> {
val text = withContext(Dispatchers.IO) {
context.resources.openRawResource(library.licenseText).reader()
.readText()

View File

@ -9,7 +9,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity

View File

@ -7,7 +7,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource

View File

@ -2,14 +2,15 @@ package de.mm20.launcher2.ui.settings.search
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -19,6 +20,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
private val permissionsManager: PermissionsManager by inject()
val favorites = dataStore.data.map { it.favorites.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFavorites(favorites: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -31,7 +33,10 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val contacts = dataStore.data.map { it.contactsSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setContacts(contacts: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -47,7 +52,9 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val calendar = dataStore.data.map { it.calendarSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCalendar(calendar: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -63,6 +70,8 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val calculator = dataStore.data.map { it.calculatorSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCalculator(calculator: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -74,6 +83,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setUnitConverter(unitConverter: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -85,6 +95,8 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWikipedia(wikipedia: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -96,6 +108,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val websites = dataStore.data.map { it.websiteSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWebsites(websites: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -106,18 +119,8 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val webSearch = dataStore.data.map { it.webSearch.enabled }
fun setWebSearch(webSearch: Boolean) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder().setWebSearch(
it.webSearch.toBuilder().setEnabled(webSearch)
).build()
}
}
}
val autoFocus = dataStore.data.map { it.searchBar.autoFocus }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAutoFocus(autoFocus: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -129,6 +132,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val launchOnEnter = dataStore.data.map { it.searchBar.launchOnEnter }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLaunchOnEnter(launchOnEnter: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -140,7 +144,9 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val hasAppShortcutPermission = permissionsManager.hasPermission(PermissionGroup.AppShortcuts)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val appShortcuts = dataStore.data.map { it.appShortcutSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAppShortcuts(appShortcuts: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -152,6 +158,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
}
val searchResultOrdering = dataStore.data.map { it.resultOrdering.ordering }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSearchResultOrdering(searchResultOrdering: Settings.SearchResultOrderingSettings.Ordering) {
viewModelScope.launch {
dataStore.updateData {
@ -164,6 +171,7 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setReverseSearchResults(reverseSearchResults: Boolean) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -25,8 +25,8 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
@ -69,8 +69,8 @@ fun SearchActionsSettingsScreen() {
onItemMove = { from, to -> viewModel.moveItem(from.index, to.index) }
)
val searchActions by viewModel.searchActions.observeAsState(emptyList())
val disabledActions by viewModel.disabledActions.observeAsState(emptyList())
val searchActions by viewModel.searchActions.collectAsState()
val disabledActions by viewModel.disabledActions.collectAsState()
Scaffold(
floatingActionButton = {
@ -187,8 +187,8 @@ fun SearchActionsSettingsScreen() {
}
}
val editAction by viewModel.showEditDialogFor.observeAsState(null)
val createAction by viewModel.showCreateDialog.observeAsState(false)
val editAction by viewModel.showEditDialogFor
val createAction by viewModel.showCreateDialog
if (createAction) {
EditSearchActionSheet(

View File

@ -1,13 +1,14 @@
package de.mm20.launcher2.ui.settings.searchactions
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.searchactions.SearchActionService
import de.mm20.launcher2.searchactions.builders.CustomizableSearchActionBuilder
import de.mm20.launcher2.searchactions.builders.SearchActionBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -18,11 +19,11 @@ class SearchActionsSettingsScreenVM : ViewModel(), KoinComponent {
val searchActions = searchActionService
.getSearchActionBuilders()
.asLiveData()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
val disabledActions = searchActionService
.getDisabledActionBuilders()
.asLiveData()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
fun addAction(searchAction: SearchActionBuilder) {
val actions =
@ -60,8 +61,8 @@ class SearchActionsSettingsScreenVM : ViewModel(), KoinComponent {
searchActionService.saveSearchActionBuilders(actions)
}
val showEditDialogFor = MutableLiveData<CustomizableSearchActionBuilder?>(null)
val showCreateDialog = MutableLiveData(false)
val showEditDialogFor = mutableStateOf<CustomizableSearchActionBuilder?>(null)
val showCreateDialog = mutableStateOf(false)
fun editAction(action: CustomizableSearchActionBuilder) {
showEditDialogFor.value = action

View File

@ -1,19 +1,14 @@
package de.mm20.launcher2.ui.settings.unitconverter
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Loop
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
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.component.preferences.SwitchPreference
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreenVM
@Composable
fun UnitConverterSettingsScreen() {
@ -21,7 +16,7 @@ fun UnitConverterSettingsScreen() {
PreferenceScreen(title = stringResource(R.string.preference_search_unitconverter)) {
item {
PreferenceCategory {
val unitConverter by viewModel.unitConverter.observeAsState()
val unitConverter by viewModel.unitConverter.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_search_unitconverter),
summary = stringResource(R.string.preference_search_unitconverter_summary),
@ -30,7 +25,7 @@ fun UnitConverterSettingsScreen() {
viewModel.setUnitConverter(it)
}
)
val currencyConverter by viewModel.currencyConverter.observeAsState()
val currencyConverter by viewModel.currencyConverter.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_search_currencyconverter),
summary = stringResource(R.string.preference_search_currencyconverter_summary),

View File

@ -1,10 +1,11 @@
package de.mm20.launcher2.ui.settings.unitconverter
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -13,7 +14,8 @@ class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled }.asLiveData()
val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setUnitConverter(unitConverter: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -27,7 +29,8 @@ class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val currencyConverter = dataStore.data.map { it.unitConverterSearch.currencies }.asLiveData()
val currencyConverter = dataStore.data.map { it.unitConverterSearch.currencies }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCurrencyConverter(currencyConverter: Boolean) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -4,7 +4,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@ -29,7 +28,7 @@ fun WeatherIntegrationSettingsScreen() {
) {
item {
PreferenceCategory {
val weatherProvider by viewModel.weatherProvider.observeAsState()
val weatherProvider by viewModel.weatherProvider.collectAsState()
ListPreference(
title = stringResource(R.string.preference_weather_provider),
items = viewModel.availableProviders.map {
@ -60,7 +59,7 @@ fun WeatherIntegrationSettingsScreen() {
}
item {
PreferenceCategory(title = stringResource(R.string.preference_category_location)) {
val hasPermission by viewModel.hasLocationPermission.observeAsState()
val hasPermission by viewModel.hasLocationPermission.collectAsState()
AnimatedVisibility(hasPermission == false) {
MissingPermissionBanner(
text = stringResource(R.string.missing_permission_auto_location),
@ -70,7 +69,7 @@ fun WeatherIntegrationSettingsScreen() {
modifier = Modifier.padding(16.dp)
)
}
val autoLocation by viewModel.autoLocation.observeAsState(false)
val autoLocation by viewModel.autoLocation.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_automatic_location),
summary = stringResource(R.string.preference_automatic_location_summary),
@ -79,7 +78,7 @@ fun WeatherIntegrationSettingsScreen() {
viewModel.setAutoLocation(it)
}
)
val location by viewModel.location.observeAsState()
val location by viewModel.location
LocationPreference(
title = stringResource(R.string.preference_location),
value = location,

View File

@ -1,9 +1,8 @@
package de.mm20.launcher2.ui.settings.weather
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.MutableLiveData
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
@ -12,9 +11,11 @@ import de.mm20.launcher2.preferences.Settings.WeatherSettings
import de.mm20.launcher2.weather.WeatherLocation
import de.mm20.launcher2.weather.WeatherRepository
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -25,7 +26,8 @@ class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
val availableProviders = repository.getAvailableProviders()
val weatherProvider = repository.selectedProvider.asLiveData()
val weatherProvider = repository.selectedProvider
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWeatherProvider(provider: WeatherSettings.WeatherProvider) {
repository.selectProvider(provider)
}
@ -41,14 +43,16 @@ class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
}
}
val autoLocation = repository.autoLocation.asLiveData()
val autoLocation = repository.autoLocation
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setAutoLocation(autoLocation: Boolean) {
repository.setAutoLocation(autoLocation)
}
val location = MutableLiveData<WeatherLocation?>(null)
val location = mutableStateOf<WeatherLocation?>(null)
val hasLocationPermission = permissionsManager.hasPermission(PermissionGroup.Location).asLiveData()
val hasLocationPermission = permissionsManager.hasPermission(PermissionGroup.Location)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun requestLocationPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Location)
@ -64,7 +68,7 @@ class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
if (autoLoc) lastLoc
else loc
}.collectLatest {
this@WeatherIntegrationSettingsScreenVM.location.postValue(it)
this@WeatherIntegrationSettingsScreenVM.location.value = it
}
}
}

View File

@ -1,8 +1,8 @@
package de.mm20.launcher2.ui.settings.wikipedia
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
@ -15,7 +15,7 @@ fun WikipediaSettingsScreen() {
val viewModel: WikipediaSettingsScreenVM = viewModel()
PreferenceScreen(title = stringResource(R.string.preference_search_wikipedia)) {
item {
val wikipedia by viewModel.wikipedia.observeAsState()
val wikipedia by viewModel.wikipedia.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_search_wikipedia),
summary = stringResource(R.string.preference_search_wikipedia_summary),
@ -24,7 +24,7 @@ fun WikipediaSettingsScreen() {
viewModel.setWikipedia(it)
}
)
val images by viewModel.images.observeAsState()
val images by viewModel.images.collectAsState()
SwitchPreference(
title = stringResource(R.string.preference_search_wikipedia_pictures),
summary = stringResource(R.string.preference_search_wikipedia_pictures_summary),
@ -34,7 +34,7 @@ fun WikipediaSettingsScreen() {
viewModel.setImages(it)
}
)
val customUrl by viewModel.customUrl.observeAsState("")
val customUrl by viewModel.customUrl.collectAsState()
TextPreference(
title = stringResource(R.string.preference_wikipedia_customurl),
value = customUrl,

View File

@ -1,10 +1,11 @@
package de.mm20.launcher2.ui.settings.wikipedia
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@ -12,7 +13,8 @@ import org.koin.core.component.inject
class WikipediaSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled }.asLiveData()
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWikipedia(wikipedia: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -26,7 +28,8 @@ class WikipediaSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val images = dataStore.data.map { it.wikipediaSearch.images }.asLiveData()
val images = dataStore.data.map { it.wikipediaSearch.images }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setImages(images: Boolean) {
viewModelScope.launch {
dataStore.updateData {
@ -40,7 +43,8 @@ class WikipediaSettingsScreenVM: ViewModel(), KoinComponent {
}
}
val customUrl = dataStore.data.map { it.wikipediaSearch.customUrl }.asLiveData()
val customUrl = dataStore.data.map { it.wikipediaSearch.customUrl }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
fun setCustomUrl(customUrl: String) {
viewModelScope.launch {
dataStore.updateData {

View File

@ -1,13 +1,10 @@
package de.mm20.launcher2.database
import androidx.lifecycle.LiveData
import androidx.room.*
import de.mm20.launcher2.database.entities.IconEntity
import de.mm20.launcher2.database.entities.IconPackEntity
import kotlinx.coroutines.flow.Flow
internal val AppTypes = listOf("app", "calendar", "clock")
@Dao
interface IconDao {
@Insert

View File

@ -37,8 +37,6 @@ dependencyResolutionManagement {
version("androidx.compose.compiler", "1.4.5")
library("androidx.compose.runtime", "androidx.compose.runtime", "runtime")
.version("1.5.0-alpha03")
library("androidx.compose.livedata", "androidx.compose.runtime", "runtime-livedata")
.version("1.5.0-alpha03")
library("androidx.compose.foundation", "androidx.compose.foundation", "foundation")
.version("1.5.0-alpha03")
library("androidx.compose.foundationlayout", "androidx.compose.foundation", "foundation-layout")
@ -61,8 +59,6 @@ dependencyResolutionManagement {
version("androidx.lifecycle", "2.6.1")
library("androidx.lifecycle.viewmodel", "androidx.lifecycle", "lifecycle-viewmodel-ktx")
.versionRef("androidx.lifecycle")
library("androidx.lifecycle.livedata", "androidx.lifecycle", "lifecycle-livedata-ktx")
.versionRef("androidx.lifecycle")
library("androidx.lifecycle.common", "androidx.lifecycle", "lifecycle-common-java8")
.versionRef("androidx.lifecycle")
library("androidx.lifecycle.runtime", "androidx.lifecycle", "lifecycle-runtime-ktx")
@ -75,7 +71,6 @@ dependencyResolutionManagement {
"androidx.lifecycle",
listOf(
"androidx.lifecycle.viewmodel",
"androidx.lifecycle.livedata",
"androidx.lifecycle.common",
"androidx.lifecycle.runtime"
)