Add export OPML file feature and fix ksoap2 XmlPullParser confusion
This commit is contained in:
parent
8263b12ae7
commit
2d5c9dbfc2
|
@ -53,6 +53,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'be.ceau:opml-parser:2.2.0'
|
||||||
implementation "androidx.profileinstaller:profileinstaller:1.2.0-alpha02"
|
implementation "androidx.profileinstaller:profileinstaller:1.2.0-alpha02"
|
||||||
implementation("io.coil-kt:coil-compose:2.0.0-rc02")
|
implementation("io.coil-kt:coil-compose:2.0.0-rc02")
|
||||||
implementation("androidx.compose.animation:animation-graphics:$compose_version")
|
implementation("androidx.compose.animation:animation-graphics:$compose_version")
|
||||||
|
|
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
|
@ -25,3 +25,8 @@
|
||||||
# Disable ServiceLoader reproducibility-breaking optimizations
|
# Disable ServiceLoader reproducibility-breaking optimizations
|
||||||
-keep class kotlinx.coroutines.CoroutineExceptionHandler
|
-keep class kotlinx.coroutines.CoroutineExceptionHandler
|
||||||
-keep class kotlinx.coroutines.internal.MainDispatcherFactory
|
-keep class kotlinx.coroutines.internal.MainDispatcherFactory
|
||||||
|
|
||||||
|
# ksoap2 XmlPullParser confusion
|
||||||
|
-dontwarn org.xmlpull.v1.XmlPullParser
|
||||||
|
-dontwarn org.xmlpull.v1.XmlSerializer
|
||||||
|
-keep class org.xmlpull.v1.* {*;}
|
|
@ -1,9 +1,11 @@
|
||||||
package me.ash.reader
|
package me.ash.reader
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.profileinstaller.ProfileInstallerInitializer
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import me.ash.reader.ui.page.common.HomeEntry
|
import me.ash.reader.ui.page.common.HomeEntry
|
||||||
|
|
||||||
|
@ -13,6 +15,7 @@ class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
Log.i("RLog", "onCreate: ${ProfileInstallerInitializer().create(this)}")
|
||||||
setContent {
|
setContent {
|
||||||
HomeEntry(intent.extras).also {
|
HomeEntry(intent.extras).also {
|
||||||
intent.replaceExtras(null)
|
intent.replaceExtras(null)
|
||||||
|
|
|
@ -20,7 +20,16 @@ interface GroupDao {
|
||||||
WHERE accountId = :accountId
|
WHERE accountId = :accountId
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
fun queryAllGroupWithFeed(accountId: Int): Flow<MutableList<GroupWithFeed>>
|
fun queryAllGroupWithFeedAsFlow(accountId: Int): Flow<MutableList<GroupWithFeed>>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query(
|
||||||
|
"""
|
||||||
|
SELECT * FROM `group`
|
||||||
|
WHERE accountId = :accountId
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
fun queryAllGroupWithFeed(accountId: Int): List<GroupWithFeed>
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -63,7 +63,7 @@ abstract class AbstractRssRepository constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pullFeeds(): Flow<MutableList<GroupWithFeed>> {
|
fun pullFeeds(): Flow<MutableList<GroupWithFeed>> {
|
||||||
return groupDao.queryAllGroupWithFeed(context.currentAccountId).flowOn(Dispatchers.IO)
|
return groupDao.queryAllGroupWithFeedAsFlow(context.currentAccountId).flowOn(Dispatchers.IO)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pullArticles(
|
fun pullArticles(
|
||||||
|
|
|
@ -1,16 +1,29 @@
|
||||||
package me.ash.reader.data.repository
|
package me.ash.reader.data.repository
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import be.ceau.opml.OpmlWriter
|
||||||
|
import be.ceau.opml.entity.Body
|
||||||
|
import be.ceau.opml.entity.Head
|
||||||
|
import be.ceau.opml.entity.Opml
|
||||||
|
import be.ceau.opml.entity.Outline
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import me.ash.reader.currentAccountId
|
||||||
|
import me.ash.reader.data.account.AccountDao
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.feed.Feed
|
||||||
import me.ash.reader.data.feed.FeedDao
|
import me.ash.reader.data.feed.FeedDao
|
||||||
import me.ash.reader.data.group.GroupDao
|
import me.ash.reader.data.group.GroupDao
|
||||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
import me.ash.reader.data.source.OpmlLocalDataSource
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class OpmlRepository @Inject constructor(
|
class OpmlRepository @Inject constructor(
|
||||||
|
@ApplicationContext
|
||||||
|
private val context: Context,
|
||||||
private val groupDao: GroupDao,
|
private val groupDao: GroupDao,
|
||||||
private val feedDao: FeedDao,
|
private val feedDao: FeedDao,
|
||||||
|
private val accountDao: AccountDao,
|
||||||
private val rssRepository: RssRepository,
|
private val rssRepository: RssRepository,
|
||||||
private val opmlLocalDataSource: OpmlLocalDataSource
|
private val opmlLocalDataSource: OpmlLocalDataSource
|
||||||
) {
|
) {
|
||||||
|
@ -36,4 +49,45 @@ class OpmlRepository @Inject constructor(
|
||||||
Log.e("saveToDatabase", "${e.message}")
|
Log.e("saveToDatabase", "${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun saveToString(): String? =
|
||||||
|
try {
|
||||||
|
val defaultGroup = groupDao.queryById(opmlLocalDataSource.getDefaultGroupId())!!
|
||||||
|
OpmlWriter().write(
|
||||||
|
Opml(
|
||||||
|
"2.0",
|
||||||
|
Head(
|
||||||
|
accountDao.queryById(context.currentAccountId).name,
|
||||||
|
Date().toString(), null, null, null,
|
||||||
|
null, null, null, null,
|
||||||
|
null, null, null, null,
|
||||||
|
),
|
||||||
|
Body(groupDao.queryAllGroupWithFeed(context.currentAccountId).map {
|
||||||
|
Outline(
|
||||||
|
mapOf(
|
||||||
|
"text" to it.group.name,
|
||||||
|
"title" to it.group.name,
|
||||||
|
"isDefault" to (it.group.id == defaultGroup.id).toString()
|
||||||
|
),
|
||||||
|
it.feeds.map { feed ->
|
||||||
|
Outline(
|
||||||
|
mapOf(
|
||||||
|
"text" to feed.name,
|
||||||
|
"title" to feed.name,
|
||||||
|
"xmlUrl" to feed.url,
|
||||||
|
"htmlUrl" to feed.url,
|
||||||
|
"isNotification" to feed.isNotification.toString(),
|
||||||
|
"isFullContent" to feed.isFullContent.toString(),
|
||||||
|
),
|
||||||
|
listOf()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("saveToString", "${e.message}")
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
package me.ash.reader.data.source
|
package me.ash.reader.data.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import be.ceau.opml.OpmlParser
|
||||||
import android.util.Xml
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import me.ash.reader.*
|
import me.ash.reader.*
|
||||||
import me.ash.reader.data.feed.Feed
|
import me.ash.reader.data.feed.Feed
|
||||||
import me.ash.reader.data.group.Group
|
import me.ash.reader.data.group.Group
|
||||||
import me.ash.reader.data.group.GroupWithFeed
|
import me.ash.reader.data.group.GroupWithFeed
|
||||||
import me.ash.reader.data.repository.StringsRepository
|
import me.ash.reader.data.repository.StringsRepository
|
||||||
import org.xmlpull.v1.XmlPullParser
|
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -29,59 +27,78 @@ class OpmlLocalDataSource @Inject constructor(
|
||||||
|
|
||||||
// @Throws(XmlPullParserException::class, IOException::class)
|
// @Throws(XmlPullParserException::class, IOException::class)
|
||||||
fun parseFileInputStream(inputStream: InputStream, defaultGroup: Group): List<GroupWithFeed> {
|
fun parseFileInputStream(inputStream: InputStream, defaultGroup: Group): List<GroupWithFeed> {
|
||||||
val groupWithFeedList = mutableListOf<GroupWithFeed>()
|
|
||||||
val accountId = context.currentAccountId
|
val accountId = context.currentAccountId
|
||||||
inputStream.use {
|
val opml = OpmlParser().parse(inputStream)
|
||||||
val parser: XmlPullParser = Xml.newPullParser()
|
val groupWithFeedList = mutableListOf<GroupWithFeed>().also {
|
||||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
it.addGroup(defaultGroup)
|
||||||
parser.setInput(it, null)
|
|
||||||
parser.nextTag()
|
|
||||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
|
||||||
if (parser.eventType != XmlPullParser.START_TAG) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if (parser.name != "outline") {
|
|
||||||
continue
|
opml.body.outlines.forEach {
|
||||||
}
|
// Only feeds
|
||||||
if ("rss" == parser.getAttributeValue(null, "type")) {
|
if (it.subElements.isEmpty()) {
|
||||||
val title = parser.getAttributeValue(null, "title")
|
// It's a empty group
|
||||||
val xmlUrl = parser.getAttributeValue(null, "xmlUrl")
|
if (it.attributes["xmlUrl"] == null) {
|
||||||
Log.i("RLog", "rss: ${title} , ${xmlUrl}")
|
if (!it.attributes["isDefault"].toBoolean()) {
|
||||||
if (groupWithFeedList.isEmpty()) {
|
groupWithFeedList.addGroup(
|
||||||
groupWithFeedList.add(
|
Group(
|
||||||
GroupWithFeed(
|
|
||||||
group = defaultGroup,
|
|
||||||
feeds = mutableListOf()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
groupWithFeedList.last().let { groupWithFeed ->
|
|
||||||
groupWithFeed.feeds.add(
|
|
||||||
Feed(
|
|
||||||
id = UUID.randomUUID().toString(),
|
id = UUID.randomUUID().toString(),
|
||||||
name = title,
|
name = it.attributes["title"] ?: it.text!!,
|
||||||
url = xmlUrl,
|
|
||||||
groupId = groupWithFeed.group.id,
|
|
||||||
accountId = accountId,
|
accountId = accountId,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val title = parser.getAttributeValue(null, "title")
|
groupWithFeedList.addFeedToDefault(
|
||||||
Log.i("RLog", "title: ${title}")
|
Feed(
|
||||||
groupWithFeedList.add(
|
|
||||||
GroupWithFeed(
|
|
||||||
group = Group(
|
|
||||||
id = UUID.randomUUID().toString(),
|
id = UUID.randomUUID().toString(),
|
||||||
name = title,
|
name = it.attributes["title"] ?: it.text!!,
|
||||||
|
url = it.attributes["xmlUrl"]!!,
|
||||||
|
groupId = defaultGroup.id,
|
||||||
accountId = accountId,
|
accountId = accountId,
|
||||||
),
|
isNotification = it.attributes["isNotification"].toBoolean(),
|
||||||
feeds = mutableListOf()
|
isFullContent = it.attributes["isFullContent"].toBoolean(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var groupId = defaultGroup.id
|
||||||
|
if (!it.attributes["isDefault"].toBoolean()) {
|
||||||
|
groupId = UUID.randomUUID().toString()
|
||||||
|
groupWithFeedList.addGroup(
|
||||||
|
Group(
|
||||||
|
id = groupId,
|
||||||
|
name = it.attributes["title"] ?: it.text!!,
|
||||||
|
accountId = accountId,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
it.subElements.forEach { outline ->
|
||||||
|
groupWithFeedList.addFeed(
|
||||||
|
Feed(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
name = outline.attributes["title"] ?: outline.text!!,
|
||||||
|
url = outline.attributes["xmlUrl"]!!,
|
||||||
|
groupId = groupId,
|
||||||
|
accountId = accountId,
|
||||||
|
isNotification = outline.attributes["isNotification"].toBoolean(),
|
||||||
|
isFullContent = outline.attributes["isFullContent"].toBoolean(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return groupWithFeedList
|
return groupWithFeedList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MutableList<GroupWithFeed>.addGroup(group: Group) {
|
||||||
|
add(GroupWithFeed(group = group, feeds = mutableListOf()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<GroupWithFeed>.addFeed(feed: Feed) {
|
||||||
|
last().feeds.add(feed)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<GroupWithFeed>.addFeedToDefault(feed: Feed) {
|
||||||
|
first().feeds.add(feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
package me.ash.reader.ui.page.home.feeds
|
package me.ash.reader.ui.page.home.feeds
|
||||||
|
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.animation.core.*
|
import androidx.compose.animation.core.*
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
@ -18,6 +21,8 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
@ -36,7 +41,10 @@ import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
|
||||||
import me.ash.reader.ui.widget.Banner
|
import me.ash.reader.ui.widget.Banner
|
||||||
import me.ash.reader.ui.widget.Subtitle
|
import me.ash.reader.ui.widget.Subtitle
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class)
|
@OptIn(
|
||||||
|
ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class,
|
||||||
|
androidx.compose.foundation.ExperimentalFoundationApi::class
|
||||||
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun FeedsPage(
|
fun FeedsPage(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
@ -45,6 +53,7 @@ fun FeedsPage(
|
||||||
homeViewModel: HomeViewModel = hiltViewModel(),
|
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||||
subscribeViewModel: SubscribeViewModel = hiltViewModel(),
|
subscribeViewModel: SubscribeViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val viewState = feedsViewModel.viewState.collectAsStateValue()
|
val viewState = feedsViewModel.viewState.collectAsStateValue()
|
||||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||||
|
@ -59,6 +68,18 @@ fun FeedsPage(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val launcher = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.CreateDocument()
|
||||||
|
) { result ->
|
||||||
|
feedsViewModel.dispatch(FeedsViewAction.ExportAsString { string ->
|
||||||
|
result?.let { uri ->
|
||||||
|
context.contentResolver.openOutputStream(uri)?.let { outputStream ->
|
||||||
|
outputStream.write(string.toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
feedsViewModel.dispatch(FeedsViewAction.FetchAccount)
|
feedsViewModel.dispatch(FeedsViewAction.FetchAccount)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +98,8 @@ fun FeedsPage(
|
||||||
SmallTopAppBar(
|
SmallTopAppBar(
|
||||||
title = {},
|
title = {},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = { }) {
|
IconButton(onClick = {
|
||||||
|
}) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Rounded.ArrowBack,
|
imageVector = Icons.Rounded.ArrowBack,
|
||||||
contentDescription = stringResource(R.string.back),
|
contentDescription = stringResource(R.string.back),
|
||||||
|
@ -113,18 +135,26 @@ fun FeedsPage(
|
||||||
content = {
|
content = {
|
||||||
SubscribeDialog(
|
SubscribeDialog(
|
||||||
openInputStreamCallback = {
|
openInputStreamCallback = {
|
||||||
feedsViewModel.dispatch(FeedsViewAction.AddFromFile(it))
|
feedsViewModel.dispatch(FeedsViewAction.ImportFromInputStream(it))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
item {
|
item {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
start = 24.dp,
|
start = 24.dp,
|
||||||
top = 48.dp,
|
top = 48.dp,
|
||||||
end = 24.dp,
|
end = 24.dp,
|
||||||
bottom = 24.dp
|
bottom = 24.dp
|
||||||
),
|
)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(
|
||||||
|
onLongPress = {
|
||||||
|
launcher.launch("ReadYou.opml")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
text = viewState.account?.name ?: stringResource(R.string.unknown),
|
text = viewState.account?.name ?: stringResource(R.string.unknown),
|
||||||
style = MaterialTheme.typography.displaySmall,
|
style = MaterialTheme.typography.displaySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
|
|
@ -31,7 +31,8 @@ class FeedsViewModel @Inject constructor(
|
||||||
when (action) {
|
when (action) {
|
||||||
is FeedsViewAction.FetchAccount -> fetchAccount()
|
is FeedsViewAction.FetchAccount -> fetchAccount()
|
||||||
is FeedsViewAction.FetchData -> fetchData(action.filterState)
|
is FeedsViewAction.FetchData -> fetchData(action.filterState)
|
||||||
is FeedsViewAction.AddFromFile -> addFromFile(action.inputStream)
|
is FeedsViewAction.ImportFromInputStream -> importFromInputStream(action.inputStream)
|
||||||
|
is FeedsViewAction.ExportAsString -> exportAsOpml(action.callback)
|
||||||
is FeedsViewAction.ScrollToItem -> scrollToItem(action.index)
|
is FeedsViewAction.ScrollToItem -> scrollToItem(action.index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,13 +47,19 @@ class FeedsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFromFile(inputStream: InputStream) {
|
private fun importFromInputStream(inputStream: InputStream) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
opmlRepository.saveToDatabase(inputStream)
|
opmlRepository.saveToDatabase(inputStream)
|
||||||
rssRepository.get().doSync()
|
rssRepository.get().doSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun exportAsOpml(callback: (String) -> Unit = {}) {
|
||||||
|
viewModelScope.launch(Dispatchers.Default) {
|
||||||
|
opmlRepository.saveToString()?.let { callback(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun fetchData(filterState: FilterState) {
|
private fun fetchData(filterState: FilterState) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
pullFeeds(
|
pullFeeds(
|
||||||
|
@ -145,10 +152,14 @@ sealed class FeedsViewAction {
|
||||||
|
|
||||||
object FetchAccount : FeedsViewAction()
|
object FetchAccount : FeedsViewAction()
|
||||||
|
|
||||||
data class AddFromFile(
|
data class ImportFromInputStream(
|
||||||
val inputStream: InputStream
|
val inputStream: InputStream
|
||||||
) : FeedsViewAction()
|
) : FeedsViewAction()
|
||||||
|
|
||||||
|
data class ExportAsString(
|
||||||
|
val callback: (String) -> Unit = {}
|
||||||
|
) : FeedsViewAction()
|
||||||
|
|
||||||
data class ScrollToItem(
|
data class ScrollToItem(
|
||||||
val index: Int
|
val index: Int
|
||||||
) : FeedsViewAction()
|
) : FeedsViewAction()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user