Skip to content

Commit

Permalink
feat(reading): support for specifying the composition of shared conte…
Browse files Browse the repository at this point in the history
…nt (#660)
  • Loading branch information
Ashinch authored Mar 26, 2024
1 parent d88a542 commit 2771989
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
}
10 changes: 0 additions & 10 deletions app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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>() {

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/me/ash/reader/ui/ext/StringExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
41 changes: 32 additions & 9 deletions app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
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
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
Expand All @@ -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

Expand All @@ -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()
Expand Down Expand Up @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -40,6 +40,7 @@ fun TopBar(
) {
val context = LocalContext.current
val tonalElevation = LocalReadingPageTonalElevation.current
val sharedContent = LocalSharedContent.current

Box(
modifier = Modifier
Expand Down Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

0 comments on commit 2771989

Please sign in to comment.