mirror of https://github.com/Ashinch/ReadYou.git
feat(reading): support for specifying the composition of shared content (#660)
This commit is contained in:
parent
d88a542bf7
commit
2771989489
|
@ -82,6 +82,7 @@ fun Preferences.toSettings(): Settings {
|
|||
pullToSwitchArticle = PullToSwitchArticlePreference.fromPreference(this),
|
||||
openLink = OpenLinkPreference.fromPreferences(this),
|
||||
openLinkSpecificBrowser = OpenLinkSpecificBrowserPreference.fromPreferences(this),
|
||||
sharedContent = SharedContentPreference.fromPreferences(this),
|
||||
|
||||
// Languages
|
||||
languages = LanguagesPreference.fromPreferences(this),
|
||||
|
|
|
@ -81,6 +81,7 @@ data class Settings(
|
|||
val pullToSwitchArticle: PullToSwitchArticlePreference = PullToSwitchArticlePreference.default,
|
||||
val openLink: OpenLinkPreference = OpenLinkPreference.default,
|
||||
val openLinkSpecificBrowser: OpenLinkSpecificBrowserPreference = OpenLinkSpecificBrowserPreference.default,
|
||||
val sharedContent: SharedContentPreference = SharedContentPreference.default,
|
||||
|
||||
// Languages
|
||||
val languages: LanguagesPreference = LanguagesPreference.default,
|
||||
|
@ -195,10 +196,9 @@ val LocalInitialFilter =
|
|||
val LocalArticleListSwipeEndAction = compositionLocalOf { SwipeEndActionPreference.default }
|
||||
val LocalArticleListSwipeStartAction = compositionLocalOf { SwipeStartActionPreference.default }
|
||||
val LocalPullToSwitchArticle = compositionLocalOf { PullToSwitchArticlePreference.default }
|
||||
val LocalOpenLink =
|
||||
compositionLocalOf<OpenLinkPreference> { OpenLinkPreference.default }
|
||||
val LocalOpenLinkSpecificBrowser =
|
||||
compositionLocalOf { OpenLinkSpecificBrowserPreference.default }
|
||||
val LocalOpenLink = compositionLocalOf<OpenLinkPreference> { OpenLinkPreference.default }
|
||||
val LocalOpenLinkSpecificBrowser = compositionLocalOf { OpenLinkSpecificBrowserPreference.default }
|
||||
val LocalSharedContent = compositionLocalOf<SharedContentPreference> { SharedContentPreference.default }
|
||||
|
||||
// Languages
|
||||
val LocalLanguages =
|
||||
|
@ -287,6 +287,7 @@ fun SettingsProvider(
|
|||
LocalPullToSwitchArticle provides settings.pullToSwitchArticle,
|
||||
LocalOpenLink provides settings.openLink,
|
||||
LocalOpenLinkSpecificBrowser provides settings.openLinkSpecificBrowser,
|
||||
LocalSharedContent provides settings.sharedContent,
|
||||
|
||||
// Languages
|
||||
LocalLanguages provides settings.languages,
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package me.ash.reader.infrastructure.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.ext.DataStoreKeys
|
||||
import me.ash.reader.ui.ext.dataStore
|
||||
import me.ash.reader.ui.ext.orNotEmpty
|
||||
import me.ash.reader.ui.ext.put
|
||||
|
||||
sealed class SharedContentPreference(val value: Int) : Preference() {
|
||||
object OnlyLink : SharedContentPreference(0)
|
||||
object TitleAndLink : SharedContentPreference(1)
|
||||
|
||||
override fun put(context: Context, scope: CoroutineScope) {
|
||||
scope.launch {
|
||||
context.dataStore.put(
|
||||
DataStoreKeys.SharedContent,
|
||||
value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
fun toDesc(context: Context): String =
|
||||
when (this) {
|
||||
OnlyLink -> context.getString(R.string.only_link)
|
||||
TitleAndLink -> context.getString(R.string.title_and_link)
|
||||
}
|
||||
|
||||
fun share(context: Context, title: String?, link: String?) {
|
||||
when (this) {
|
||||
OnlyLink -> share(context, link.orEmpty())
|
||||
TitleAndLink -> share(context, title.orNotEmpty { it + "\n" } + link.orEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
private fun share(context: Context, content: String) {
|
||||
context.startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply {
|
||||
putExtra(Intent.EXTRA_TEXT, content)
|
||||
type = "text/plain"
|
||||
}, context.getString(R.string.share)))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val default = OnlyLink
|
||||
val values = listOf(OnlyLink, TitleAndLink)
|
||||
|
||||
fun fromPreferences(preferences: Preferences): SharedContentPreference =
|
||||
when (preferences[DataStoreKeys.SharedContent.key]) {
|
||||
0 -> OnlyLink
|
||||
1 -> TitleAndLink
|
||||
else -> default
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,16 +66,6 @@ fun Context.showToastLong(message: String?) {
|
|||
showToast(message, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
fun Context.share(content: String) {
|
||||
startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply {
|
||||
putExtra(
|
||||
Intent.EXTRA_TEXT,
|
||||
content,
|
||||
)
|
||||
type = "text/plain"
|
||||
}, getString(R.string.share)))
|
||||
}
|
||||
|
||||
fun Context.openURL(
|
||||
url: String?,
|
||||
openLink: OpenLinkPreference,
|
||||
|
|
|
@ -3,7 +3,14 @@ package me.ash.reader.ui.ext
|
|||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.*
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.doublePreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.emptyPreferences
|
||||
import androidx.datastore.preferences.core.floatPreferencesKey
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.catch
|
||||
|
@ -439,6 +446,12 @@ sealed class DataStoreKeys<T> {
|
|||
get() = stringPreferencesKey("openLppSpecificBrowser")
|
||||
}
|
||||
|
||||
object SharedContent : DataStoreKeys<Int>() {
|
||||
|
||||
override val key: Preferences.Key<Int>
|
||||
get() = intPreferencesKey("sharedContent")
|
||||
}
|
||||
|
||||
// Languages
|
||||
object Languages : DataStoreKeys<Int>() {
|
||||
|
||||
|
|
|
@ -35,3 +35,6 @@ fun String.md5(): String =
|
|||
.toString(16).padStart(32, '0')
|
||||
|
||||
fun String?.decodeHTML(): String? = this?.run { Html.fromHtml(this).toString() }
|
||||
|
||||
fun String?.orNotEmpty(l: (value: String) -> String): String =
|
||||
if (this.isNullOrBlank()) "" else l(this)
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
package me.ash.reader.ui.page.home.flow
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
|
@ -9,11 +15,18 @@ import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
|||
import androidx.compose.material.icons.rounded.DoneAll
|
||||
import androidx.compose.material.icons.rounded.Search
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
@ -28,11 +41,22 @@ import me.ash.reader.R
|
|||
import me.ash.reader.domain.model.article.ArticleWithFeed
|
||||
import me.ash.reader.domain.model.general.Filter
|
||||
import me.ash.reader.domain.model.general.MarkAsReadConditions
|
||||
import me.ash.reader.infrastructure.preference.*
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowArticleListDateStickyHeader
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowArticleListFeedIcon
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowArticleListTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowFilterBarFilled
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowFilterBarPadding
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowFilterBarStyle
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowFilterBarTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowTopBarTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalSharedContent
|
||||
import me.ash.reader.ui.component.FilterBar
|
||||
import me.ash.reader.ui.component.base.*
|
||||
import me.ash.reader.ui.component.base.DisplayText
|
||||
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||
import me.ash.reader.ui.component.base.RYExtensibleVisibility
|
||||
import me.ash.reader.ui.component.base.RYScaffold
|
||||
import me.ash.reader.ui.component.base.SwipeRefresh
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.share
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
|
||||
|
@ -55,6 +79,7 @@ fun FlowPage(
|
|||
val filterBarFilled = LocalFlowFilterBarFilled.current
|
||||
val filterBarPadding = LocalFlowFilterBarPadding.current
|
||||
val filterBarTonalElevation = LocalFlowFilterBarTonalElevation.current
|
||||
val sharedContent = LocalSharedContent.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val homeUiState = homeViewModel.homeUiState.collectAsStateValue()
|
||||
|
@ -126,9 +151,7 @@ fun FlowPage(
|
|||
val onShare: ((ArticleWithFeed) -> Unit)? = remember {
|
||||
{ articleWithFeed ->
|
||||
with(articleWithFeed.article) {
|
||||
context.share(
|
||||
arrayOf(title, link).filter { it.isNotBlank() }.joinToString(separator = "\n")
|
||||
)
|
||||
sharedContent.share(context, title, link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ import androidx.compose.ui.zIndex
|
|||
import androidx.navigation.NavHostController
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation
|
||||
import me.ash.reader.infrastructure.preference.LocalSharedContent
|
||||
import me.ash.reader.ui.component.base.FeedbackIconButton
|
||||
import me.ash.reader.ui.component.base.RYExtensibleVisibility
|
||||
import me.ash.reader.ui.ext.share
|
||||
import me.ash.reader.ui.ext.surfaceColorAtElevation
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
|
||||
|
@ -40,6 +40,7 @@ fun TopBar(
|
|||
) {
|
||||
val context = LocalContext.current
|
||||
val tonalElevation = LocalReadingPageTonalElevation.current
|
||||
val sharedContent = LocalSharedContent.current
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
@ -78,10 +79,7 @@ fun TopBar(
|
|||
contentDescription = stringResource(R.string.share),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
context.share(title
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.let { it + "\n" } + link
|
||||
)
|
||||
sharedContent.share(context, title, link)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.smallTopAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(tonalElevation.value.dp),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package me.ash.reader.ui.page.settings.accounts
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
@ -60,12 +59,8 @@ class AccountViewModel @Inject constructor(
|
|||
|
||||
fun exportAsOPML(accountId: Int, callback: (String) -> Unit = {}) {
|
||||
viewModelScope.launch(defaultDispatcher) {
|
||||
try {
|
||||
callback(opmlService.saveToString(accountId,
|
||||
_accountUiState.value.exportOPMLMode == ExportOPMLMode.ATTACH_INFO))
|
||||
} catch (e: Exception) {
|
||||
Log.e("FeedsViewModel", "exportAsOpml: ", e)
|
||||
}
|
||||
callback(opmlService.saveToString(accountId,
|
||||
_accountUiState.value.exportOPMLMode == ExportOPMLMode.ATTACH_INFO))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,9 @@ import me.ash.reader.infrastructure.preference.LocalInitialPage
|
|||
import me.ash.reader.infrastructure.preference.LocalOpenLink
|
||||
import me.ash.reader.infrastructure.preference.LocalOpenLinkSpecificBrowser
|
||||
import me.ash.reader.infrastructure.preference.LocalPullToSwitchArticle
|
||||
import me.ash.reader.infrastructure.preference.LocalSharedContent
|
||||
import me.ash.reader.infrastructure.preference.OpenLinkPreference
|
||||
import me.ash.reader.infrastructure.preference.SharedContentPreference
|
||||
import me.ash.reader.infrastructure.preference.SwipeEndActionPreference
|
||||
import me.ash.reader.infrastructure.preference.SwipeStartActionPreference
|
||||
import me.ash.reader.ui.component.base.DisplayText
|
||||
|
@ -57,6 +59,7 @@ fun InteractionPage(
|
|||
val pullToSwitchArticle = LocalPullToSwitchArticle.current
|
||||
val openLink = LocalOpenLink.current
|
||||
val openLinkSpecificBrowser = LocalOpenLinkSpecificBrowser.current
|
||||
val sharedContent = LocalSharedContent.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val isOpenLinkSpecificBrowserItemEnabled = remember(openLink) {
|
||||
openLink == OpenLinkPreference.SpecificBrowser
|
||||
|
@ -67,6 +70,7 @@ fun InteractionPage(
|
|||
var swipeEndDialogVisible by remember { mutableStateOf(false) }
|
||||
var openLinkDialogVisible by remember { mutableStateOf(false) }
|
||||
var openLinkSpecificBrowserDialogVisible by remember { mutableStateOf(false) }
|
||||
var sharedContentDialogVisible by remember { mutableStateOf(false) }
|
||||
|
||||
RYScaffold(
|
||||
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
|
||||
|
@ -158,6 +162,17 @@ fun InteractionPage(
|
|||
}
|
||||
},
|
||||
) {}
|
||||
Subtitle(
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
text = stringResource(R.string.share),
|
||||
)
|
||||
SettingItem(
|
||||
title = stringResource(R.string.shared_content),
|
||||
desc = sharedContent.toDesc(context),
|
||||
onClick = {
|
||||
sharedContentDialogVisible = true
|
||||
},
|
||||
) {}
|
||||
}
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
@ -264,4 +279,18 @@ fun InteractionPage(
|
|||
}
|
||||
)
|
||||
|
||||
RadioDialog(
|
||||
visible = sharedContentDialogVisible,
|
||||
title = stringResource(R.string.shared_content),
|
||||
options = SharedContentPreference.values.map {
|
||||
RadioDialogOption(
|
||||
text = it.toDesc(context),
|
||||
selected = it == sharedContent,
|
||||
) {
|
||||
it.put(context, scope)
|
||||
}
|
||||
},
|
||||
) {
|
||||
sharedContentDialogVisible = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -427,4 +427,7 @@
|
|||
<string name="save">Save</string>
|
||||
<string name="image_saved">Image saved</string>
|
||||
<string name="permission_denied">Permission denied</string>
|
||||
<string name="shared_content">Shared content</string>
|
||||
<string name="only_link">Only link</string>
|
||||
<string name="title_and_link">Title and link</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue