From 5fff554bbabe394741b1844afadd0f82a4b2091f Mon Sep 17 00:00:00 2001 From: Ash Date: Sun, 6 Mar 2022 05:29:23 +0800 Subject: [PATCH] Add a subscribe feed dialog --- app/build.gradle | 3 +- .../ui/page/home/article/ArticlePage.kt | 2 +- .../ash/reader/ui/page/home/feed/FeedPage.kt | 144 +++++++++++++++--- .../ui/widget/CanBeDisabledIconButton.kt | 2 +- .../java/me/ash/reader/ui/widget/Dialog.kt | 31 ++++ 5 files changed, 160 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/ui/widget/Dialog.kt diff --git a/app/build.gradle b/app/build.gradle index 00767e3..135ca7c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -81,7 +81,8 @@ dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.0-alpha01" - implementation "androidx.compose.material3:material3:1.0.0-alpha04" + implementation "androidx.compose.material:material:1.2.0-alpha03" + implementation "androidx.compose.material3:material3:1.0.0-alpha05" implementation "androidx.compose.material:material-icons-extended:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' diff --git a/app/src/main/java/me/ash/reader/ui/page/home/article/ArticlePage.kt b/app/src/main/java/me/ash/reader/ui/page/home/article/ArticlePage.kt index ec46682..aef640e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/article/ArticlePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/article/ArticlePage.kt @@ -214,7 +214,7 @@ private fun ArticleItem( if (isStarredFilter || articleWithFeed.article.isUnread) { 1f } else { - 0.75f + 0.7f } ) ) { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feed/FeedPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feed/FeedPage.kt index 91ea7e4..38395d2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feed/FeedPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feed/FeedPage.kt @@ -6,23 +6,30 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.* import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.material.TextField +import androidx.compose.material.TextFieldDefaults import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Settings -import androidx.compose.material.icons.rounded.Add -import androidx.compose.material.icons.rounded.ExpandMore -import androidx.compose.material.icons.rounded.Refresh +import androidx.compose.material.icons.rounded.* import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController @@ -40,8 +47,10 @@ import me.ash.reader.ui.page.home.HomeViewAction import me.ash.reader.ui.page.home.HomeViewModel import me.ash.reader.ui.util.collectAsStateValue import me.ash.reader.ui.widget.* +import java.io.InputStream +@ExperimentalComposeUiApi @ExperimentalAnimationApi @ExperimentalMaterial3Api @ExperimentalPagerApi @@ -55,17 +64,9 @@ fun FeedPage( filter: Filter, groupAndFeedOnClick: (currentGroup: Group?, currentFeed: Feed?) -> Unit = { _, _ -> }, ) { - val context = LocalContext.current val viewState = viewModel.viewState.collectAsStateValue() val syncState = RssRepository.syncState.collectAsStateValue() - val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { - Log.i("RLog", "launcher: ${it}") - it?.let { uri -> - context.contentResolver.openInputStream(uri)?.let { inputStream -> - viewModel.dispatch(FeedViewAction.AddFromFile(inputStream)) - } - } - } + var addFeedDialogVisible by remember { mutableStateOf(false) } LaunchedEffect(homeViewModel.filterState) { homeViewModel.filterState.collect { state -> @@ -88,6 +89,13 @@ fun FeedPage( Box( modifier.fillMaxSize() ) { + AddFeedDialog( + visible = addFeedDialogVisible, + hiddenFunction = { addFeedDialogVisible = false }, + openInputStreamCallback = { + viewModel.dispatch(FeedViewAction.AddFromFile(it)) + }, + ) TopTitleBox( title = viewState.account?.name ?: "未知账户", description = if (syncState.isSyncing) { @@ -132,7 +140,7 @@ fun FeedPage( ) } IconButton(onClick = { - launcher.launch("*/*") + addFeedDialogVisible = true }) { Icon( modifier = Modifier.size(26.dp), @@ -186,6 +194,107 @@ fun FeedPage( } } +@ExperimentalComposeUiApi +@Composable +private fun AddFeedDialog( + visible: Boolean, + hiddenFunction: () -> Unit, + openInputStreamCallback: (InputStream) -> Unit, +) { + val context = LocalContext.current + var inputString by remember { mutableStateOf("") } + val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { + it?.let { uri -> + context.contentResolver.openInputStream(uri)?.let { inputStream -> + openInputStreamCallback(inputStream) + } + } + } + val focusRequester = remember { FocusRequester() } + val localFocusManager = LocalFocusManager.current + val localSoftwareKeyboardController = LocalSoftwareKeyboardController.current + + Dialog( + visible = visible, + onDismissRequest = hiddenFunction, + icon = { + Icon( + imageVector = Icons.Rounded.RssFeed, + contentDescription = "Subscribe", + ) + }, + title = { Text("订阅") }, + text = { + Spacer(modifier = Modifier.height(10.dp)) + TextField( + modifier = Modifier + .focusRequester(focusRequester) + .onFocusChanged { if(it.isFocused) localSoftwareKeyboardController?.hide() } + .focusable(), + colors = TextFieldDefaults.textFieldColors( + backgroundColor = Color.Transparent, + cursorColor = MaterialTheme.colorScheme.onSurface, + textColor = MaterialTheme.colorScheme.onSurface, + focusedIndicatorColor = MaterialTheme.colorScheme.primary, + ), + value = inputString, + onValueChange = { + inputString = it + }, + placeholder = { + Text( + text = "订阅源或站点链接", + color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f) + ) + }, + singleLine = true, + trailingIcon = { + IconButton(onClick = {}) { + Icon( + imageVector = Icons.Rounded.ContentPaste, + contentDescription = "Paste", + tint = MaterialTheme.colorScheme.primary + ) + } + }, + keyboardActions = KeyboardActions( + onDone = { + hiddenFunction() + } + ) + ) + Spacer(modifier = Modifier.height(10.dp)) + }, + confirmButton = { + TextButton( + enabled = inputString.isNotEmpty(), + onClick = { + hiddenFunction() + } + ) { + Text( + text = "搜索", + color = if (inputString.isNotEmpty()) { + Color.Unspecified + } else { + MaterialTheme.colorScheme.outline.copy(alpha = 0.7f) + } + ) + } + }, + dismissButton = { + TextButton( + onClick = { + launcher.launch("*/*") + hiddenFunction() + } + ) { + Text("导入OPML文件") + } + }, + ) +} + @ExperimentalAnimationApi @Composable private fun ColumnScope.GroupList( @@ -230,9 +339,6 @@ private fun ColumnScope.FeedList( feeds: List, onClick: (currentFeed: Feed?) -> Unit = {}, ) { -// LaunchedEffect(feeds) { -// -// } AnimatedVisibility( visible = visible, enter = fadeIn() + expandVertically(), diff --git a/app/src/main/java/me/ash/reader/ui/widget/CanBeDisabledIconButton.kt b/app/src/main/java/me/ash/reader/ui/widget/CanBeDisabledIconButton.kt index 78a2853..6d81d77 100644 --- a/app/src/main/java/me/ash/reader/ui/widget/CanBeDisabledIconButton.kt +++ b/app/src/main/java/me/ash/reader/ui/widget/CanBeDisabledIconButton.kt @@ -22,7 +22,7 @@ fun CanBeDisabledIconButton( IconButton( modifier = Modifier.alpha( if (disabled) { - 0.75f + 0.7f } else { 1f } diff --git a/app/src/main/java/me/ash/reader/ui/widget/Dialog.kt b/app/src/main/java/me/ash/reader/ui/widget/Dialog.kt new file mode 100644 index 0000000..2939ac2 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/widget/Dialog.kt @@ -0,0 +1,31 @@ +package me.ash.reader.ui.widget + +import androidx.compose.animation.* +import androidx.compose.material3.AlertDialog +import androidx.compose.runtime.Composable + +@Composable +fun Dialog( + visible: Boolean, + onDismissRequest: () -> Unit = {}, + icon: @Composable (() -> Unit)? = null, + title: @Composable (() -> Unit)? = null, + text: @Composable (() -> Unit)? = null, + confirmButton: @Composable () -> Unit, + dismissButton: @Composable (() -> Unit)? = null, +) { + AnimatedVisibility( + visible = visible, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + AlertDialog( + onDismissRequest = onDismissRequest, + icon = icon, + title = title, + text = text, + confirmButton = confirmButton, + dismissButton = dismissButton, + ) + } +} \ No newline at end of file