diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6ad173f3..77d80e8a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,7 +27,7 @@ android { applicationId = "com.anod.appwatcher" minSdk = 31 targetSdk = 36 - versionCode = 17000 + versionCode = 17001 versionName = "1.7.0" } diff --git a/app/src/main/java/com/anod/appwatcher/AppWatcherActivity.kt b/app/src/main/java/com/anod/appwatcher/AppWatcherActivity.kt index c99bf065..275ec47c 100644 --- a/app/src/main/java/com/anod/appwatcher/AppWatcherActivity.kt +++ b/app/src/main/java/com/anod/appwatcher/AppWatcherActivity.kt @@ -196,7 +196,8 @@ class AppWatcherActivity : BaseComposeActivity(), KoinComponent { val wideLayout by foldableDevice.layout.collectAsState() HistoryListScreenScene( wideLayout = wideLayout, - navigateBack = { backStack.removeLastOrNull() } + navigateBack = { backStack.removeLastOrNull() }, + navigateTo = { backStack.add(it) } ) } entry( @@ -240,6 +241,7 @@ class AppWatcherActivity : BaseComposeActivity(), KoinComponent { InstalledListScreenScene( showAction = key.importMode, navigateBack = { backStack.removeLastOrNull() }, + navigateTo = { backStack.add(it) } ) } entry( diff --git a/app/src/main/java/com/anod/appwatcher/accounts/AccountSelectionRequest.kt b/app/src/main/java/com/anod/appwatcher/accounts/AccountSelectionRequest.kt index b8cff7f5..24f12e42 100644 --- a/app/src/main/java/com/anod/appwatcher/accounts/AccountSelectionRequest.kt +++ b/app/src/main/java/com/anod/appwatcher/accounts/AccountSelectionRequest.kt @@ -6,6 +6,8 @@ import android.app.Activity import android.content.Context import android.content.Intent import androidx.activity.result.contract.ActivityResultContract +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.ShowDialogData sealed interface AccountSelectionResult { class Success(val account: Account) : AccountSelectionResult @@ -13,6 +15,16 @@ sealed interface AccountSelectionResult { class Error(val errorMessage: String) : AccountSelectionResult } +data class AccountSelectionDialogData( + val currentAccount: Account? +) : ShowDialogData + +fun showAccountSelectionAction( + currentAccount: Account? +): ScreenCommonAction = ScreenCommonAction.ShowDialog( + AccountSelectionDialogData(currentAccount) +) + class AccountSelectionRequest : ActivityResultContract() { override fun createIntent(context: Context, input: Account?): Intent { diff --git a/app/src/main/java/com/anod/appwatcher/details/DetailsPanel.kt b/app/src/main/java/com/anod/appwatcher/details/DetailsPanel.kt index 4b92b660..7dbffc98 100644 --- a/app/src/main/java/com/anod/appwatcher/details/DetailsPanel.kt +++ b/app/src/main/java/com/anod/appwatcher/details/DetailsPanel.kt @@ -364,7 +364,7 @@ private fun DetailsScreenContent( val result = snackBarHostState.showSnackbar(TagSnackbar.Visuals(action.appInfo, context)) if (result == SnackbarResult.ActionPerformed) { - showTagList = action.appInfo + showTagList = action.appInfo.app } } diff --git a/app/src/main/java/com/anod/appwatcher/details/DetailsViewModel.kt b/app/src/main/java/com/anod/appwatcher/details/DetailsViewModel.kt index f51079fd..956a812f 100644 --- a/app/src/main/java/com/anod/appwatcher/details/DetailsViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/details/DetailsViewModel.kt @@ -9,6 +9,7 @@ import androidx.annotation.StringRes import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import androidx.core.net.toUri import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope @@ -26,6 +27,7 @@ import com.anod.appwatcher.database.AppsDatabase import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.database.entities.AppChange import com.anod.appwatcher.database.entities.Tag +import com.anod.appwatcher.tags.TagSnackbarAppInfo import com.anod.appwatcher.utils.AppIconLoader import com.anod.appwatcher.utils.BaseFlowViewModel import com.anod.appwatcher.utils.androidVersions @@ -36,8 +38,9 @@ import finsky.api.DfeApi import finsky.api.Document import finsky.api.toDocument import info.anodsplace.applog.AppLog -import info.anodsplace.framework.content.ShowToastActionDefaults import info.anodsplace.framework.content.InstalledApps +import info.anodsplace.framework.content.ShowToastActionDefaults +import info.anodsplace.framework.content.StartActivityAction import info.anodsplace.framework.content.forAppInfo import info.anodsplace.framework.content.forUninstall import info.anodsplace.framework.text.Html @@ -52,8 +55,6 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.net.URLEncoder import java.util.Locale -import androidx.core.net.toUri -import info.anodsplace.framework.content.StartActivityAction typealias TagMenuItem = Pair @@ -115,7 +116,7 @@ data class AppVersionInfo( sealed interface DetailsAction { class StartActivity(override val intent: Intent) : DetailsAction, StartActivityAction - class ShowTagSnackbar(val appInfo: App) : DetailsAction + class ShowTagSnackbar(val appInfo: TagSnackbarAppInfo) : DetailsAction object Dismiss : DetailsAction class Share(val app: App, val recentChange: AppChange) : DetailsAction class ShowToast(@param:StringRes override val resId: Int) : ShowToastActionDefaults(resId), DetailsAction @@ -405,9 +406,11 @@ class DetailsViewModel( AppListTable.ERROR_ALREADY_ADDED -> emitAction(action = showToastAction(resId = R.string.app_already_added)) else -> emitAction( DetailsAction.ShowTagSnackbar( - appInfo = App( - document!!, - uploadDateParserCache + appInfo = TagSnackbarAppInfo( + app = App( + document!!, + uploadDateParserCache + ) ) ) ) diff --git a/app/src/main/java/com/anod/appwatcher/history/HistoryListScreen.kt b/app/src/main/java/com/anod/appwatcher/history/HistoryListScreen.kt index 65438ae9..b158bf88 100644 --- a/app/src/main/java/com/anod/appwatcher/history/HistoryListScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/history/HistoryListScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -32,6 +31,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation3.runtime.NavKey import androidx.paging.LoadState import androidx.paging.PagingData import androidx.paging.compose.LazyPagingItems @@ -39,20 +39,20 @@ import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemKey import com.anod.appwatcher.R import com.anod.appwatcher.compose.SearchTopBar -import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.navigation.SceneNavKey +import com.anod.appwatcher.navigation.asNavKey import com.anod.appwatcher.search.ListItem import com.anod.appwatcher.search.MarketAppItem import com.anod.appwatcher.search.RetryButton -import com.anod.appwatcher.tags.TagSelectionDialog -import com.anod.appwatcher.tags.TagSnackbar import com.anod.appwatcher.utils.AppIconLoader import info.anodsplace.framework.app.FoldableDeviceLayout +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.onScreenCommonAction import kotlinx.coroutines.flow.Flow import org.koin.java.KoinJavaComponent @Composable -fun HistoryListScreenScene(wideLayout: FoldableDeviceLayout, navigateBack: () -> Unit) { +fun HistoryListScreenScene(wideLayout: FoldableDeviceLayout, navigateBack: () -> Unit, navigateTo: (NavKey) -> Unit) { val viewModel: HistoryListViewModel = viewModel( factory = HistoryListViewModel.Factory( wideLayout = wideLayout, @@ -65,7 +65,8 @@ fun HistoryListScreenScene(wideLayout: FoldableDeviceLayout, navigateBack: () -> onEvent = viewModel::handleEvent, pagingDataFlow = viewModel.pagingData, viewActions = viewModel.viewActions, - navigateBack = navigateBack + navigateBack = navigateBack, + navigateTo = navigateTo, ) } @@ -74,8 +75,9 @@ fun HistoryListScreen( screenState: HistoryListState, pagingDataFlow: Flow>, onEvent: (HistoryListEvent) -> Unit, - viewActions: Flow, + viewActions: Flow, navigateBack: () -> Unit = {}, + navigateTo: (NavKey) -> Unit = {}, appIconLoader: AppIconLoader = KoinJavaComponent.getKoin().get(), ) { val context = LocalContext.current @@ -134,31 +136,15 @@ fun HistoryListScreen( } } - var showTagList: App? by remember { mutableStateOf(null) } LaunchedEffect(key1 = viewActions) { viewActions.collect { action -> - when (action) { - is HistoryListAction.ShowTagSnackbar -> { - val result = snackbarHostState.showSnackbar(TagSnackbar.Visuals(action.info, context)) - if (result == SnackbarResult.ActionPerformed) { - showTagList = action.info - } - } - - HistoryListAction.OnBackPress -> navigateBack() - } + context.onScreenCommonAction( + action = action, + navigateBack = navigateBack, + navigateTo = { navigateTo(it.asNavKey) }, + ) } } - - if (showTagList != null) { - TagSelectionDialog( - appId = showTagList!!.appId, - appTitle = showTagList!!.title, - onDismissRequest = { - showTagList = null - } - ) - } } @Composable diff --git a/app/src/main/java/com/anod/appwatcher/history/HistoryListViewModel.kt b/app/src/main/java/com/anod/appwatcher/history/HistoryListViewModel.kt index 4094758d..1eff07bd 100644 --- a/app/src/main/java/com/anod/appwatcher/history/HistoryListViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/history/HistoryListViewModel.kt @@ -17,6 +17,7 @@ import com.anod.appwatcher.accounts.toAndroidAccount import com.anod.appwatcher.database.AppsDatabase import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.database.observePackages +import com.anod.appwatcher.navigation.SceneNavKey import com.anod.appwatcher.search.ListItem import com.anod.appwatcher.search.updateRowId import com.anod.appwatcher.utils.BaseFlowViewModel @@ -26,6 +27,7 @@ import finsky.api.FilterComposite import finsky.api.FilterPredicate import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps +import info.anodsplace.framework.content.ScreenCommonAction import info.anodsplace.playstore.AppNameFilter import info.anodsplace.playstore.PaidHistoryFilter import kotlinx.coroutines.flow.Flow @@ -42,22 +44,16 @@ data class HistoryListState( val authToken: String = "", val nameFilter: String = "", val wideLayout: FoldableDeviceLayout = FoldableDeviceLayout(), - val selectedApp: App? = null, ) -sealed interface HistoryListAction { - data object OnBackPress : HistoryListAction - class ShowTagSnackbar(val info: App) : HistoryListAction -} - sealed interface HistoryListEvent { data object OnBackPress : HistoryListEvent class OnNameFilter(val query: String) : HistoryListEvent - class SelectApp(val app: App?) : HistoryListEvent + class SelectApp(val app: App) : HistoryListEvent class SetWideLayout(val wideLayout: FoldableDeviceLayout) : HistoryListEvent } -class HistoryListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel(), KoinComponent { +class HistoryListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel(), KoinComponent { class Factory( private val wideLayout: FoldableDeviceLayout @@ -117,11 +113,9 @@ class HistoryListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel override fun handleEvent(event: HistoryListEvent) { when (event) { - HistoryListEvent.OnBackPress -> emitAction(HistoryListAction.OnBackPress) + HistoryListEvent.OnBackPress -> emitAction(ScreenCommonAction.NavigateBack) is HistoryListEvent.OnNameFilter -> viewState = viewState.copy(nameFilter = event.query) - is HistoryListEvent.SelectApp -> { - viewState = viewState.copy(selectedApp = event.app) - } + is HistoryListEvent.SelectApp -> emitAction(ScreenCommonAction.NavigateTo(SceneNavKey.AppDetails(event.app))) is HistoryListEvent.SetWideLayout -> { viewState = viewState.copy(wideLayout = event.wideLayout) } diff --git a/app/src/main/java/com/anod/appwatcher/installed/InstalledListScreen.kt b/app/src/main/java/com/anod/appwatcher/installed/InstalledListScreen.kt index 9a1b4257..5d822235 100644 --- a/app/src/main/java/com/anod/appwatcher/installed/InstalledListScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/installed/InstalledListScreen.kt @@ -20,11 +20,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation3.runtime.NavKey import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.anod.appwatcher.R import com.anod.appwatcher.model.Filters import com.anod.appwatcher.navigation.SceneNavKey +import com.anod.appwatcher.navigation.asNavKey import com.anod.appwatcher.preferences.Preferences import com.anod.appwatcher.watchlist.WatchListPage import com.anod.appwatcher.watchlist.WatchListPagingSource @@ -35,7 +37,8 @@ import info.anodsplace.framework.content.onScreenCommonAction @Composable fun InstalledListScreenScene( showAction: Boolean, - navigateBack: () -> Unit + navigateBack: () -> Unit, + navigateTo: (NavKey) -> Unit ) { val context = LocalContext.current val viewModel: InstalledListViewModel = viewModel( @@ -64,7 +67,7 @@ fun InstalledListScreenScene( LaunchedEffect(true) { viewModel.viewActions.collect { action -> - context.onScreenCommonAction(action, navigateBack) + context.onScreenCommonAction(action, navigateBack, navigateTo = { navigateTo(it.asNavKey) }) } } } diff --git a/app/src/main/java/com/anod/appwatcher/installed/InstalledListViewModel.kt b/app/src/main/java/com/anod/appwatcher/installed/InstalledListViewModel.kt index 31eea1cd..8176abca 100644 --- a/app/src/main/java/com/anod/appwatcher/installed/InstalledListViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/installed/InstalledListViewModel.kt @@ -15,7 +15,7 @@ import com.anod.appwatcher.accounts.AuthTokenBlocking import com.anod.appwatcher.accounts.CheckTokenError import com.anod.appwatcher.accounts.CheckTokenResult import com.anod.appwatcher.accounts.toAndroidAccount -import com.anod.appwatcher.database.entities.App +import com.anod.appwatcher.navigation.SceneNavKey import com.anod.appwatcher.utils.BaseFlowViewModel import com.anod.appwatcher.utils.PackageChangedReceiver import com.anod.appwatcher.utils.SelectionState @@ -41,7 +41,6 @@ data class InstalledListState( val selectionMode: Boolean = false, val titleFilter: String = "", val wideLayout: FoldableDeviceLayout = FoldableDeviceLayout(isWideLayout = false, hinge = Rect()), - val selectedApp: App? = null, val importStatus: ImportStatus = ImportStatus.NotStarted, val selection: SelectionState = SelectionState(), val packageChanged: String = "", @@ -57,7 +56,6 @@ sealed interface InstalledListEvent { class ChangeSort(val sortId: Int) : InstalledListEvent class SwitchImportMode(val selectionMode: Boolean) : InstalledListEvent class SetSelection(val all: Boolean) : InstalledListEvent - class SelectApp(val app: App?) : InstalledListEvent class AuthTokenError(val error: CheckTokenError) : InstalledListEvent object Import : InstalledListEvent } @@ -114,7 +112,7 @@ class InstalledListViewModel( viewState = viewState.copy(sortId = event.sortId) } is InstalledListEvent.FilterByTitle -> viewState = viewState.copy(titleFilter = event.query) - InstalledListEvent.OnBackPressed -> onBackPressed() + InstalledListEvent.OnBackPressed -> emitAction(ScreenCommonAction.NavigateBack) is InstalledListEvent.SwitchImportMode -> { switchImportMode(event.selectionMode) } @@ -123,10 +121,6 @@ class InstalledListViewModel( viewState = viewState.copy(selection = viewState.selection.selectAll(event.all)) } - is InstalledListEvent.SelectApp -> { - viewState = viewState.copy(selectedApp = event.app) - } - InstalledListEvent.Import -> import() is InstalledListEvent.AuthTokenError -> { if (event.error is CheckTokenError.RequiresInteraction) { @@ -156,18 +150,6 @@ class InstalledListViewModel( } } - private fun onBackPressed() { - if (viewState.wideLayout.isWideLayout) { - if (viewState.selectedApp != null) { - handleEvent(InstalledListEvent.SelectApp(app = null)) - } else { - emitAction(ScreenCommonAction.NavigateBack) - } - } else { - emitAction(ScreenCommonAction.NavigateBack) - } - } - private fun handleListEvent(listEvent: WatchListEvent) { when (listEvent) { is WatchListEvent.AppClick -> { @@ -176,7 +158,7 @@ class InstalledListViewModel( togglePackage(listEvent.app.packageName) } } else { - viewState = viewState.copy(selectedApp = listEvent.app) + emitAction(ScreenCommonAction.NavigateTo(SceneNavKey.AppDetails(selectedApp = listEvent.app))) } } is WatchListEvent.AppLongClick -> { diff --git a/app/src/main/java/com/anod/appwatcher/navigation/SceneNavKey.kt b/app/src/main/java/com/anod/appwatcher/navigation/SceneNavKey.kt index d896242a..00df805f 100644 --- a/app/src/main/java/com/anod/appwatcher/navigation/SceneNavKey.kt +++ b/app/src/main/java/com/anod/appwatcher/navigation/SceneNavKey.kt @@ -3,9 +3,17 @@ package com.anod.appwatcher.navigation import androidx.navigation3.runtime.NavKey import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.database.entities.Tag +import info.anodsplace.framework.content.ScreenCommonNavKey import kotlinx.serialization.Serializable -sealed interface SceneNavKey : NavKey { + +val ScreenCommonNavKey.asNavKey: NavKey + get() = when (this) { + is SceneNavKey -> this + else -> throw IllegalArgumentException("Unknown ScreenCommonNavKey: $this") + } + +sealed interface SceneNavKey : NavKey, ScreenCommonNavKey { @Serializable data object Main : SceneNavKey diff --git a/app/src/main/java/com/anod/appwatcher/search/SearchResultsScreen.kt b/app/src/main/java/com/anod/appwatcher/search/SearchResultsScreen.kt index 43a3999a..15d8b8fe 100644 --- a/app/src/main/java/com/anod/appwatcher/search/SearchResultsScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/search/SearchResultsScreen.kt @@ -1,5 +1,7 @@ package com.anod.appwatcher.search +import android.accounts.Account +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -18,15 +20,13 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarResult 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.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -42,13 +42,14 @@ import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemKey import coil3.ImageLoader import com.anod.appwatcher.R +import com.anod.appwatcher.accounts.AccountSelectionDialogData +import com.anod.appwatcher.accounts.AccountSelectionRequest import com.anod.appwatcher.compose.AppTheme import com.anod.appwatcher.compose.SearchTopBar import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.navigation.SceneNavKey -import com.anod.appwatcher.tags.TagSelectionDialog -import com.anod.appwatcher.tags.TagSnackbar import com.anod.appwatcher.utils.AppIconLoader +import com.anod.appwatcher.utils.PlainShowSnackbarData import com.anod.appwatcher.utils.date.UploadDateParserCache import finsky.api.Document import finsky.protos.AppDetails @@ -56,9 +57,11 @@ import finsky.protos.DocDetails import finsky.protos.DocV2 import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps -import info.anodsplace.framework.content.startActivity +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.onScreenCommonAction import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch import org.koin.java.KoinJavaComponent fun SceneNavKey.Search.toViewState(wideLayout: FoldableDeviceLayout) = SearchViewState( @@ -74,12 +77,15 @@ fun SceneNavKey.Search.toViewState(wideLayout: FoldableDeviceLayout) = SearchVie fun SearchResultsScreenScene(initialState: SearchViewState, navigateBack: () -> Unit = {}) { val viewModel: SearchViewModel = viewModel(factory = SearchViewModel.Factory(initialState)) val screenState by viewModel.viewStates.collectAsState(initial = viewModel.viewState) + val accountSelectionRequest = rememberLauncherForActivityResult(AccountSelectionRequest()) { + viewModel.handleEvent(SearchViewEvent.SetAccount(it)) + } SearchResultsScreen( screenState = screenState, pagingDataFlow = { viewModel.pagingData }, onEvent = viewModel::handleEvent, viewActions = viewModel.viewActions, - onShowAccountDialog = { /* accountSelectionDialog.show() */ }, + onShowAccountDialog = { accountSelectionRequest.launch(it) }, navigateBack = navigateBack ) } @@ -89,8 +95,8 @@ fun SearchResultsScreen( screenState: SearchViewState, pagingDataFlow: () -> Flow>, onEvent: (SearchViewEvent) -> Unit, - viewActions: Flow, - onShowAccountDialog: () -> Unit = { }, + viewActions: Flow, + onShowAccountDialog: (account: Account?) -> Unit = { }, navigateBack: () -> Unit = {}, appIconLoader: AppIconLoader = KoinJavaComponent.getKoin().get(), ) { @@ -152,52 +158,34 @@ fun SearchResultsScreen( } } - var showTagList: Pair? by remember { mutableStateOf(null) } - var deleteNoticeDocument: Document? by remember { mutableStateOf(null) } + val scope = rememberCoroutineScope() LaunchedEffect(key1 = true, key2 = onShowAccountDialog) { viewActions.collect { action -> - when (action) { - SearchViewAction.ShowAccountDialog -> onShowAccountDialog() - is SearchViewAction.ShowSnackbar -> { - snackbarHostState.showSnackbar( - message = action.message, - duration = action.duration - ) - if (action.exitScreen) { - navigateBack() + context.onScreenCommonAction( + action, + navigateBack = navigateBack, + navigateTo = { }, + showSnackbar = { + if (it is PlainShowSnackbarData) { + scope.launch { + snackbarHostState.showSnackbar( + message = it.message, + duration = it.duration + ) + if (it.exitScreen) { + navigateBack() + } + } } - } - is SearchViewAction.ShowTagSnackbar -> { - val exitScreen = action.isShareSource - val result = snackbarHostState.showSnackbar(TagSnackbar.Visuals(action.info, context)) - if (result == SnackbarResult.ActionPerformed) { - showTagList = Pair(action.info, exitScreen) - } else if (exitScreen) { - navigateBack() + }, + showDialog = { dialogData -> + if (dialogData is AccountSelectionDialogData) { + onShowAccountDialog(dialogData.currentAccount) } } - is SearchViewAction.AlreadyWatchedNotice -> { - deleteNoticeDocument = action.document - } - SearchViewAction.NavigateBack -> navigateBack() - is SearchViewAction.StartActivity -> context.startActivity(action) - } + ) } } - - if (showTagList != null) { - val (appInfo, exitScreen) = showTagList!! - TagSelectionDialog( - appId = appInfo.appId, - appTitle = appInfo.title, - onDismissRequest = { - showTagList = null - if (exitScreen) { - navigateBack() - } - } - ) - } } @Composable diff --git a/app/src/main/java/com/anod/appwatcher/search/SearchViewModel.kt b/app/src/main/java/com/anod/appwatcher/search/SearchViewModel.kt index bf50055e..4ade6e2b 100644 --- a/app/src/main/java/com/anod/appwatcher/search/SearchViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/search/SearchViewModel.kt @@ -2,8 +2,8 @@ package com.anod.appwatcher.search import android.accounts.Account import android.content.Context -import android.content.Intent import android.content.pm.PackageManager +import android.widget.Toast import androidx.compose.material3.SnackbarDuration import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel @@ -17,11 +17,13 @@ import androidx.paging.cachedIn import androidx.paging.filter import androidx.paging.map import com.anod.appwatcher.R +import com.anod.appwatcher.accounts.AccountSelectionResult import com.anod.appwatcher.accounts.AuthAccountInitializer import com.anod.appwatcher.accounts.AuthTokenBlocking import com.anod.appwatcher.accounts.AuthTokenStartIntent import com.anod.appwatcher.accounts.CheckTokenError import com.anod.appwatcher.accounts.CheckTokenResult +import com.anod.appwatcher.accounts.showAccountSelectionAction import com.anod.appwatcher.accounts.toAndroidAccount import com.anod.appwatcher.database.AppsDatabase import com.anod.appwatcher.database.entities.App @@ -30,12 +32,14 @@ import com.anod.appwatcher.preferences.Preferences import com.anod.appwatcher.utils.BaseFlowViewModel import com.anod.appwatcher.utils.date.UploadDateParserCache import com.anod.appwatcher.utils.networkConnection +import com.anod.appwatcher.utils.showSnackbarAction import finsky.api.DfeApi -import finsky.api.Document import finsky.api.toDocument import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps -import info.anodsplace.framework.content.StartActivityAction +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.showToastAction +import info.anodsplace.framework.content.startActivityAction import info.anodsplace.playstore.AppDetailsFilter import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow @@ -56,29 +60,13 @@ sealed interface SearchStatus { data class SearchList(val query: String) : SearchStatus } -sealed interface SearchViewAction { - class StartActivity(override val intent: Intent) : SearchViewAction, StartActivityAction - class ShowSnackbar(val message: String, val duration: SnackbarDuration = SnackbarDuration.Short, val exitScreen: Boolean = false) : SearchViewAction - data object ShowAccountDialog : SearchViewAction - class ShowTagSnackbar(val info: App, val isShareSource: Boolean) : SearchViewAction - class AlreadyWatchedNotice(val document: Document) : SearchViewAction - data object NavigateBack: SearchViewAction -} - -private fun startActivityAction(intent: Intent): SearchViewAction { - return SearchViewAction.StartActivity( - intent = intent, - ) -} - sealed interface SearchViewEvent { data object NoAccount : SearchViewEvent data object OnBackPressed : SearchViewEvent class SetWideLayout(val wideLayout: FoldableDeviceLayout) : SearchViewEvent class SearchQueryChange(val query: String) : SearchViewEvent class OnSearchEnter(val query: String) : SearchViewEvent - class AccountSelectError(val errorMessage: String) : SearchViewEvent - class AccountSelected(val account: Account) : SearchViewEvent + class SetAccount(val result: AccountSelectionResult) : SearchViewEvent class SelectApp(val app: App?) : SearchViewEvent } @@ -97,7 +85,7 @@ data class SearchViewState( class SearchViewModel( initialState: SearchViewState -) : BaseFlowViewModel(), KoinComponent { +) : BaseFlowViewModel(), KoinComponent { class Factory(private val initialState: SearchViewState) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") @@ -138,13 +126,19 @@ class SearchViewModel( override fun handleEvent(event: SearchViewEvent) { when (event) { - SearchViewEvent.NoAccount -> emitAction(SearchViewAction.ShowAccountDialog) + SearchViewEvent.NoAccount -> emitAction(showAccountSelectionAction(prefs.account?.toAndroidAccount())) is SearchViewEvent.SetWideLayout -> viewState = viewState.copy(wideLayout = event.wideLayout) is SearchViewEvent.SearchQueryChange -> viewState = viewState.copy(searchQuery = event.query) is SearchViewEvent.OnSearchEnter -> onSearchRequest(event.query) - is SearchViewEvent.AccountSelectError -> onAccountSelectError(event.errorMessage) - is SearchViewEvent.AccountSelected -> onAccountSelected(event.account) - SearchViewEvent.OnBackPressed -> emitAction(SearchViewAction.NavigateBack) + is SearchViewEvent.SetAccount -> { + if (event.result is AccountSelectionResult.Error) { + onAccountSelectError(event.result.errorMessage) + } else if (event.result is AccountSelectionResult.Success) { + onAccountSelected(event.result.account) + } + } + + SearchViewEvent.OnBackPressed -> emitAction(ScreenCommonAction.NavigateBack) is SearchViewEvent.SelectApp -> { viewState = viewState.copy(selectedApp = event.app) } @@ -154,12 +148,15 @@ class SearchViewModel( private fun onAccountSelectError(errorMessage: String) { if (networkConnection.isNetworkAvailable) { if (errorMessage.isNotBlank()) { - emitAction(SearchViewAction.ShowSnackbar(message = errorMessage, duration = SnackbarDuration.Short, exitScreen = true)) + emitAction(showToastAction(text = errorMessage, length = Toast.LENGTH_LONG)) + emitAction(ScreenCommonAction.NavigateBack) } else { - emitAction(SearchViewAction.ShowSnackbar(message = context.getString(R.string.failed_gain_access), duration = SnackbarDuration.Long, exitScreen = true)) + emitAction(showToastAction(text = context.getString(R.string.failed_gain_access), length = Toast.LENGTH_LONG)) + emitAction(ScreenCommonAction.NavigateBack) } } else { - emitAction(SearchViewAction.ShowSnackbar(message = context.getString(R.string.check_connection), duration = SnackbarDuration.Short, exitScreen = true)) + emitAction(showToastAction(text = context.getString(R.string.check_connection), length = Toast.LENGTH_LONG)) + emitAction(ScreenCommonAction.NavigateBack) } } @@ -171,7 +168,7 @@ class SearchViewModel( viewState = viewState.copy(searchStatus = searchStatus) if (searchStatus is SearchStatus.NoNetwork) { emitAction( - SearchViewAction.ShowSnackbar( + showSnackbarAction( message = context.getString(R.string.check_connection), duration = SnackbarDuration.Short, exitScreen = false @@ -179,7 +176,7 @@ class SearchViewModel( ) } else if (searchStatus is SearchStatus.Error) { emitAction( - SearchViewAction.ShowSnackbar( + showSnackbarAction( message = context.getString(R.string.error_fetching_info), duration = SnackbarDuration.Short, exitScreen = false @@ -227,7 +224,7 @@ class SearchViewModel( resetPager() emit(SearchStatus.SearchList(query = query)) } - } catch (e: Exception) { + } catch (_: Exception) { if (!networkConnection.isNetworkAvailable) { emit(SearchStatus.NoNetwork(query = query)) } else { @@ -286,9 +283,11 @@ class SearchViewModel( emitAction(startActivityAction(intent = e.intent)) // TODO: finish = true } catch (e: Exception) { if (networkConnection.isNetworkAvailable) { - emitAction(SearchViewAction.ShowSnackbar(message = context.getString(R.string.failed_gain_access), duration = SnackbarDuration.Long, exitScreen = true)) + emitAction(showToastAction(text = context.getString(R.string.failed_gain_access), length = Toast.LENGTH_LONG)) + emitAction(ScreenCommonAction.NavigateBack) } else { - emitAction(SearchViewAction.ShowSnackbar(message = context.getString(R.string.check_connection), duration = SnackbarDuration.Short, exitScreen = true)) + emitAction(showToastAction(text = context.getString(R.string.check_connection), length = Toast.LENGTH_LONG)) + emitAction(ScreenCommonAction.NavigateBack) } } } diff --git a/app/src/main/java/com/anod/appwatcher/sync/SchedulesHistoryScreen.kt b/app/src/main/java/com/anod/appwatcher/sync/SchedulesHistoryScreen.kt index 6a27943a..0c5c6845 100644 --- a/app/src/main/java/com/anod/appwatcher/sync/SchedulesHistoryScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/sync/SchedulesHistoryScreen.kt @@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.CenterAlignedTopAppBar @@ -51,7 +51,7 @@ fun SchedulesHistoryScreenScene(navigateBack: () -> Unit) { val context = LocalContext.current LaunchedEffect(true) { viewModel.viewActions.collect { action -> - context.onScreenCommonAction(action, navigateBack) + context.onScreenCommonAction(action, navigateBack, navigateTo = { /* no-op */ }) } } } @@ -61,7 +61,7 @@ fun SchedulesHistoryScreenScene(navigateBack: () -> Unit) { fun SchedulesHistoryScreen(schedules: ImmutableList, dateFormat: DateFormat, navigateBack: () -> Unit) { AppTheme { Surface { - Column(modifier = Modifier.fillMaxWidth()) { + Column(modifier = Modifier.fillMaxSize()) { CenterAlignedTopAppBar( title = { Text(text = stringResource(id = R.string.refresh_history)) }, navigationIcon = { BackArrowIconButton(onClick = { navigateBack() }) }, diff --git a/app/src/main/java/com/anod/appwatcher/tags/TagSnackbar.kt b/app/src/main/java/com/anod/appwatcher/tags/TagSnackbar.kt index 9f1c2d11..969f812f 100644 --- a/app/src/main/java/com/anod/appwatcher/tags/TagSnackbar.kt +++ b/app/src/main/java/com/anod/appwatcher/tags/TagSnackbar.kt @@ -6,6 +6,7 @@ import androidx.compose.material3.SnackbarVisuals import com.anod.appwatcher.R import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.model.AppListFilter +import info.anodsplace.framework.content.ShowSnackbarData import info.anodsplace.ktx.hashCodeOf /** @@ -14,6 +15,8 @@ import info.anodsplace.ktx.hashCodeOf * @date 02/05/2017. */ +data class TagSnackbarAppInfo(val app: App) : ShowSnackbarData + object TagSnackbar { private const val GREEN_BOOK = "📗" @@ -26,8 +29,8 @@ object TagSnackbar { override fun hashCode(): Int = hashCodeOf(message, actionLabel, duration, withDismissAction) override fun equals(other: Any?): Boolean = (other as? AppListFilter.Installed)?.hashCode() == hashCode() - constructor(info: App, context: Context) : this( - message = context.getString(R.string.app_stored, info.title), + constructor(info: TagSnackbarAppInfo, context: Context) : this( + message = context.getString(R.string.app_stored, info.app.title), actionLabel = context.getString(R.string.action_tag, GREEN_BOOK) ) } diff --git a/app/src/main/java/com/anod/appwatcher/tags/TagWatchListScreen.kt b/app/src/main/java/com/anod/appwatcher/tags/TagWatchListScreen.kt index 458d6971..be05578e 100644 --- a/app/src/main/java/com/anod/appwatcher/tags/TagWatchListScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/tags/TagWatchListScreen.kt @@ -24,8 +24,8 @@ import com.anod.appwatcher.compose.TagAppIconButton import com.anod.appwatcher.database.entities.Tag import com.anod.appwatcher.model.Filters import com.anod.appwatcher.navigation.SceneNavKey +import com.anod.appwatcher.navigation.asNavKey import com.anod.appwatcher.utils.prefs -import com.anod.appwatcher.watchlist.WatchListAction import com.anod.appwatcher.watchlist.WatchListEvent import com.anod.appwatcher.watchlist.WatchListPagingSource import com.anod.appwatcher.watchlist.WatchListScreen @@ -34,8 +34,7 @@ import com.anod.appwatcher.watchlist.WatchListStateViewModel import com.anod.appwatcher.watchlist.WatchListTopBar import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps -import info.anodsplace.framework.content.showToast -import info.anodsplace.framework.content.startActivity +import info.anodsplace.framework.content.onScreenCommonAction @Composable fun TagWatchListScreenScene(wideLayout: FoldableDeviceLayout, tag: Tag, navigateBack: () -> Unit, navigateTo: (NavKey) -> Unit) { @@ -88,14 +87,8 @@ fun TagWatchListScreenScene(wideLayout: FoldableDeviceLayout, tag: Tag, navigate } LaunchedEffect(true) { - viewModel.viewActions.collect { - when (it) { - is WatchListAction.SelectApp -> {} - is WatchListAction.ShowToast -> context.showToast(it) - is WatchListAction.StartActivity -> context.startActivity(it) - WatchListAction.NavigateBack -> navigateBack() - is WatchListAction.NavigateTo -> navigateTo(it.navKey) - } + viewModel.viewActions.collect { action -> + context.onScreenCommonAction(action = action, navigateBack = navigateBack, navigateTo = { navigateTo(it.asNavKey) }) } } } diff --git a/app/src/main/java/com/anod/appwatcher/userLog/UserLogScreen.kt b/app/src/main/java/com/anod/appwatcher/userLog/UserLogScreen.kt index 396786f1..d41f3d08 100644 --- a/app/src/main/java/com/anod/appwatcher/userLog/UserLogScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/userLog/UserLogScreen.kt @@ -47,7 +47,7 @@ fun UserLogScreenScene(navigateBack: () -> Unit) { val context = LocalContext.current LaunchedEffect(true) { viewModel.viewActions.collect { action -> - context.onScreenCommonAction(action, navigateBack) + context.onScreenCommonAction(action, navigateBack, navigateTo = { /* no-op */ }) } } } diff --git a/app/src/main/java/com/anod/appwatcher/utils/ShowSnackbarAction.kt b/app/src/main/java/com/anod/appwatcher/utils/ShowSnackbarAction.kt new file mode 100644 index 00000000..9b3df5d6 --- /dev/null +++ b/app/src/main/java/com/anod/appwatcher/utils/ShowSnackbarAction.kt @@ -0,0 +1,15 @@ +package com.anod.appwatcher.utils + +import androidx.compose.material3.SnackbarDuration +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.ShowSnackbarData + +data class PlainShowSnackbarData( + val message: String, + val duration: SnackbarDuration = SnackbarDuration.Short, + val exitScreen: Boolean = false +) : ShowSnackbarData + +fun showSnackbarAction(message: String, duration: SnackbarDuration = SnackbarDuration.Short, exitScreen: Boolean = false): ScreenCommonAction = ScreenCommonAction.ShowSnackbar( + data = PlainShowSnackbarData(message, duration, exitScreen) +) \ No newline at end of file diff --git a/app/src/main/java/com/anod/appwatcher/watchlist/MainScreen.kt b/app/src/main/java/com/anod/appwatcher/watchlist/MainScreen.kt index e651d185..a4748d44 100644 --- a/app/src/main/java/com/anod/appwatcher/watchlist/MainScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/watchlist/MainScreen.kt @@ -32,10 +32,12 @@ import com.anod.appwatcher.compose.RefreshIcon import com.anod.appwatcher.compose.SortMenuItem import com.anod.appwatcher.database.entities.Tag import com.anod.appwatcher.navigation.SceneNavKey +import com.anod.appwatcher.navigation.asNavKey import com.anod.appwatcher.preferences.Preferences import com.anod.appwatcher.tags.EditTagDialog import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps +import info.anodsplace.framework.content.onScreenCommonAction import info.anodsplace.framework.content.showToast import info.anodsplace.framework.content.startActivity import info.anodsplace.permissions.AppPermission @@ -91,13 +93,7 @@ fun MainScreenScene(prefs: Preferences, wideLayout: FoldableDeviceLayout, naviga } LaunchedEffect(true) { listViewModel.viewActions.collect { action -> - when (action) { - is WatchListAction.StartActivity -> context.startActivity(action) - is WatchListAction.ShowToast -> context.showToast(action) - is WatchListAction.SelectApp -> navigateTo(SceneNavKey.AppDetails(action.app)) - WatchListAction.NavigateBack -> navigateBack() - is WatchListAction.NavigateTo -> navigateTo(action.navKey) - } + context.onScreenCommonAction(action, navigateBack = navigateBack, navigateTo = { navigateTo(it.asNavKey) }) } } val pagingSourceConfig = WatchListPagingSource.Config( diff --git a/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt b/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt index 6515d0be..c222fc5e 100644 --- a/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/watchlist/WatchListStateViewModel.kt @@ -18,7 +18,6 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.CreationExtras -import androidx.navigation3.runtime.NavKey import com.anod.appwatcher.AppWatcherActivity import com.anod.appwatcher.R import com.anod.appwatcher.accounts.AuthTokenBlocking @@ -44,8 +43,9 @@ import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps import info.anodsplace.framework.content.PinShortcut import info.anodsplace.framework.content.PinShortcutManager -import info.anodsplace.framework.content.ShowToastActionDefaults -import info.anodsplace.framework.content.StartActivityAction +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.showToastAction +import info.anodsplace.framework.content.startActivityAction import info.anodsplace.graphics.toIcon import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList @@ -100,7 +100,6 @@ sealed interface WatchListEvent { class FilterById(val filterId: Int) : WatchListEvent class AddAppToTag(val show: Boolean) : WatchListEvent class EditTag(val show: Boolean) : WatchListEvent - class SelectApp(val app: App) : WatchListEvent class UpdateSyncProgress(val syncProgress: SyncProgress) : WatchListEvent data object PinTagShortcut : WatchListEvent @@ -110,31 +109,13 @@ sealed interface WatchListEvent { class SectionHeaderClick(val type: SectionHeader) : WatchListEvent } -sealed interface WatchListAction { - data class StartActivity(override val intent: Intent) : WatchListAction, StartActivityAction - class ShowToast(resId: Int, text: String, length: Int) : ShowToastActionDefaults(resId, text, length), WatchListAction - data class SelectApp(val app: App) : WatchListAction - data class NavigateTo(val navKey: NavKey): WatchListAction - data object NavigateBack : WatchListAction -} - -private fun startActivityAction(intent: Intent): WatchListAction - = WatchListAction.StartActivity(intent) - -private fun showToastAction(resId: Int = 0, text: String = "", length: Int = Toast.LENGTH_SHORT): WatchListAction - = WatchListAction.ShowToast( - resId = resId, - text = text, - length = length - ) - class WatchListStateViewModel( state: SavedStateHandle, tag: Tag, defaultFilterId: Int, collectRecentlyInstalledApps: Boolean, wideLayout: FoldableDeviceLayout -) : BaseFlowViewModel(), KoinComponent { +) : BaseFlowViewModel(), KoinComponent { private val authToken: AuthTokenBlocking by inject() private val application: Application by inject() private val db: AppsDatabase by inject() @@ -257,13 +238,13 @@ class WatchListStateViewModel( is WatchListEvent.FilterById -> viewState = viewState.copy(filterId = event.filterId) is WatchListEvent.EditTag -> viewState = viewState.copy(showEditTagDialog = event.show) is WatchListEvent.ShowSearch -> viewState = viewState.copy(showSearch = event.show) - is WatchListEvent.SelectApp -> emitAction(WatchListAction.SelectApp(event.app)) - WatchListEvent.OnBackPressed -> emitAction(WatchListAction.NavigateBack) + WatchListEvent.OnBackPressed -> emitAction(ScreenCommonAction.NavigateBack) is WatchListEvent.SearchSubmit -> { val query = viewState.titleFilter viewState = viewState.copy(showSearch = false, titleFilter = "") - emitAction(WatchListAction.NavigateTo( + emitAction( + ScreenCommonAction.NavigateTo( SceneNavKey.Search(keyword = query, focus = true, initiateSearch = true) )) } @@ -283,15 +264,17 @@ class WatchListStateViewModel( )) WatchListEvent.Refresh -> refresh() is WatchListEvent.AppClick -> { - emitAction(WatchListAction.SelectApp(event.app)) + emitAction(ScreenCommonAction.NavigateTo(SceneNavKey.AppDetails(event.app))) } is WatchListEvent.AppLongClick -> {} is WatchListEvent.EmptyButton -> { when (event.idx) { - 1 -> emitAction(WatchListAction.NavigateTo( + 1 -> emitAction( + ScreenCommonAction.NavigateTo( SceneNavKey.Search(focus = true,) )) - 2 -> emitAction(WatchListAction.NavigateTo(SceneNavKey.Installed(importMode = true))) + + 2 -> emitAction(ScreenCommonAction.NavigateTo(SceneNavKey.Installed(importMode = true))) 3 -> emitAction(startActivityAction( intent = Intent.makeMainActivity(ComponentName("com.android.vending", "com.android.vending.AssetBrowserActivity")) )) @@ -299,7 +282,8 @@ class WatchListStateViewModel( } is WatchListEvent.SectionHeaderClick -> { when (event.type) { - SectionHeader.RecentlyInstalled -> emitAction(WatchListAction.NavigateTo( + SectionHeader.RecentlyInstalled -> emitAction( + ScreenCommonAction.NavigateTo( SceneNavKey.Installed(importMode = false) )) else -> { } diff --git a/app/src/main/java/com/anod/appwatcher/wishlist/WishListScreen.kt b/app/src/main/java/com/anod/appwatcher/wishlist/WishListScreen.kt index c64aacdb..5108f8bf 100644 --- a/app/src/main/java/com/anod/appwatcher/wishlist/WishListScreen.kt +++ b/app/src/main/java/com/anod/appwatcher/wishlist/WishListScreen.kt @@ -16,7 +16,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -39,16 +38,14 @@ import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.itemKey import com.anod.appwatcher.R import com.anod.appwatcher.compose.SearchTopBar -import com.anod.appwatcher.database.entities.App import com.anod.appwatcher.navigation.SceneNavKey import com.anod.appwatcher.search.ListItem import com.anod.appwatcher.search.MarketAppItem import com.anod.appwatcher.search.RetryButton -import com.anod.appwatcher.tags.TagSelectionDialog -import com.anod.appwatcher.tags.TagSnackbar.Visuals import com.anod.appwatcher.utils.AppIconLoader import info.anodsplace.framework.app.FoldableDeviceLayout -import info.anodsplace.framework.content.startActivity +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.onScreenCommonAction import kotlinx.coroutines.flow.Flow import org.koin.java.KoinJavaComponent @@ -75,7 +72,7 @@ fun WishListScreen( screenState: WishListState, pagingDataFlow: Flow>, onEvent: (WishListEvent) -> Unit, - viewActions: Flow, + viewActions: Flow, navigateBack: () -> Unit = {}, appIconLoader: AppIconLoader = KoinJavaComponent.getKoin().get(), ) { @@ -143,32 +140,17 @@ fun WishListScreen( } } - var showTagList: App? by remember { mutableStateOf(null) } LaunchedEffect(key1 = viewActions) { viewActions.collect { action -> - when (action) { - is WishListAction.ShowTagSnackbar -> { - val result = snackbarHostState.showSnackbar(Visuals(action.info, context)) - if (result == SnackbarResult.ActionPerformed) { - showTagList = action.info - } - } - - is WishListAction.NavigateBack -> navigateBack() - is WishListAction.StartActivity -> context.startActivity(action) - } + context.onScreenCommonAction( + action, + navigateBack = navigateBack, + navigateTo = { /* No navigation keys yet */ }, + showSnackbar = { }, + showDialog = { /* No dialogs yet */ } + ) } } - - if (showTagList != null) { - TagSelectionDialog( - appId = showTagList!!.appId, - appTitle = showTagList!!.title, - onDismissRequest = { - showTagList = null - } - ) - } } @Composable diff --git a/app/src/main/java/com/anod/appwatcher/wishlist/WishListViewModel.kt b/app/src/main/java/com/anod/appwatcher/wishlist/WishListViewModel.kt index b57fb1af..0356089a 100644 --- a/app/src/main/java/com/anod/appwatcher/wishlist/WishListViewModel.kt +++ b/app/src/main/java/com/anod/appwatcher/wishlist/WishListViewModel.kt @@ -1,6 +1,5 @@ package com.anod.appwatcher.wishlist -import android.content.Intent import android.content.pm.PackageManager import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel @@ -24,13 +23,13 @@ import com.anod.appwatcher.search.updateRowId import com.anod.appwatcher.utils.BaseFlowViewModel import com.anod.appwatcher.utils.date.UploadDateParserCache import com.anod.appwatcher.utils.prefs -import com.anod.appwatcher.wishlist.WishListAction.StartActivity import finsky.api.DfeApi import finsky.api.FilterComposite import finsky.api.FilterPredicate import info.anodsplace.framework.app.FoldableDeviceLayout import info.anodsplace.framework.content.InstalledApps -import info.anodsplace.framework.content.StartActivityAction +import info.anodsplace.framework.content.ScreenCommonAction +import info.anodsplace.framework.content.startActivityAction import info.anodsplace.playstore.AppDetailsFilter import info.anodsplace.playstore.AppNameFilter import kotlinx.coroutines.flow.Flow @@ -49,12 +48,6 @@ data class WishListState( val isError: Boolean = false ) -sealed interface WishListAction { - class ShowTagSnackbar(val info: App) : WishListAction - class StartActivity(override val intent: Intent) : WishListAction, StartActivityAction - data object NavigateBack : WishListAction -} - sealed interface WishListEvent { data object OnBackPress : WishListEvent data object NoAccount : WishListEvent @@ -65,7 +58,7 @@ sealed interface WishListEvent { class AuthTokenError(val error: CheckTokenError) : WishListEvent } -class WishListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel(), KoinComponent { +class WishListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel(), KoinComponent { class Factory( private val wideLayout: FoldableDeviceLayout @@ -150,7 +143,7 @@ class WishListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel emitAction(WishListAction.NavigateBack) + WishListEvent.OnBackPress -> emitAction(ScreenCommonAction.NavigateBack) is WishListEvent.OnNameFilter -> viewState = viewState.copy(nameFilter = event.query) is WishListEvent.SelectApp -> { viewState = viewState.copy(selectedApp = event.app) @@ -163,7 +156,7 @@ class WishListViewModel(wideLayout: FoldableDeviceLayout) : BaseFlowViewModel { viewState = viewState.copy(isError = true) if (event.error is CheckTokenError.RequiresInteraction) { - emitAction(StartActivity(event.error.intent)) + emitAction(startActivityAction(event.error.intent)) } } WishListEvent.NoAccount -> { diff --git a/lib b/lib index 3da0a8f5..5f2cd8c8 160000 --- a/lib +++ b/lib @@ -1 +1 @@ -Subproject commit 3da0a8f5aa63d3dd3b1739b35c2ac1a627a6e46b +Subproject commit 5f2cd8c8bde0822dee6dd9d135fe524830c9b439