Fixed article loading and LazyColumn problems
All checks were successful
continuous-integration/drone/push Build is passing

Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
Balazs Toldi 2021-10-24 15:03:58 +02:00
parent a4b3e7e80a
commit 103a253ac0
Signed by: Bazsalanszky
GPG key ID: 933820884952BE27
6 changed files with 135 additions and 56 deletions

View file

@ -64,7 +64,8 @@ class MainActivity : ComponentActivity() {
val scope = rememberCoroutineScope()
val drawerState = rememberDrawerState(DrawerValue.Closed)
val articleList = viewModel.articles.observeAsState(emptyList())
val articleList = viewModel.articles
val feedList = viewModel.feeds.observeAsState()
Scaffold(
floatingActionButton = {
FloatingActionButton(
@ -96,12 +97,17 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth()
) {
items(articleList.value.size) { index ->
val articleAndFeed = articleList.value[index]
articleAndFeed.articles.sortedByDescending { it.pubDate }
.forEach {
showArticle(articleAndFeed.feed, article = it)
}
items(
count = articleList.value.size,
key = { index ->
articleList.value[index].articleId as Long
}
) { index ->
Log.e(null, articleList.value.size.toString())
val article = articleList.value[index]
val feed =
feedList.value?.first() { it.feedId == article.feedId }
feed?.let { it1 -> showArticle(it1, article = article) }
}
}
}
@ -146,7 +152,7 @@ class MainActivity : ComponentActivity() {
) {
Text(
text = if (selectedIndex == 0) "None" else viewModel.feedsState.value!!.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
text = if (selectedIndex == 0) "None" else viewModel.feeds.value!!.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { expanded = true })
@ -163,13 +169,13 @@ class MainActivity : ComponentActivity() {
Text(text = "None")
}
viewModel.feedsState.value!!.filterIsInstance<FeedGroup>()
viewModel.feeds.value!!.filterIsInstance<FeedGroup>()
.forEachIndexed { index, s ->
DropdownMenuItem(onClick = {
selectedIndex = index + 1
expanded = false
}) {
viewModel.feedsState.value!![index].name?.let {
viewModel.feeds.value!![index].name?.let {
Text(text = it)
}
}
@ -197,7 +203,7 @@ class MainActivity : ComponentActivity() {
viewModel.addFeed(feed)
else {
var counter = 0
viewModel.feedsState.value!!.forEachIndexed { index, feedGroup ->
viewModel.feeds.value!!.forEachIndexed { index, feedGroup ->
if (feedGroup is FeedGroup) {
counter++
if (counter == selectedIndex) {
@ -262,17 +268,8 @@ class MainActivity : ComponentActivity() {
Modifier
.pointerInput(Unit) {
detectTapGestures(
onTap = {
WebView.webURL = article.url.toString()
WebView.barTitle =
feed.name ?: ""
startActivity(
Intent(
baseContext,
WebView::class.java
)
)
},
onTap = { openArticle(article.url.toString(), feed.name.toString()) },
onLongPress = {
val share =
Intent.createChooser(Intent().apply {
@ -397,10 +394,7 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth()
.padding(vertical = 5.dp, horizontal = 4.dp)
.clickable {
/*feeds.limit = if (feeds.limit == feed) {
null
} else feed*/
viewModel.changeFeed()
viewModel.setLimit(feed)
},
elevation = 10.dp,
backgroundColor = MaterialTheme.colors.surface,
@ -470,7 +464,8 @@ class MainActivity : ComponentActivity() {
drawerState: DrawerState,
content: @Composable () -> Unit
) {
val feedList = viewModel.feedsState?.value ?: emptyList()
val feedLiveData = viewModel.feeds.observeAsState(emptyList())
val feedList = feedLiveData.value
ModalDrawer(drawerState = drawerState, drawerContent = {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
item {
@ -479,8 +474,7 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth()
.padding(vertical = 5.dp, horizontal = 4.dp)
.clickable {
//feeds.limit = null
viewModel.changeFeed()
viewModel.setLimit(null)
},
elevation = 10.dp,
backgroundColor = MaterialTheme.colors.surface,
@ -534,6 +528,16 @@ class MainActivity : ComponentActivity() {
}
}
}*/
fun openArticle(articleURL: String, feedName: String) {
WebView.webURL = articleURL
WebView.barTitle = feedName
startActivity(
Intent(
baseContext,
WebView::class.java
)
)
}
}

View file

@ -1,5 +1,6 @@
package eu.toldi.balazs.anotherfeedreader.entities
import android.util.Log
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
@ -10,8 +11,10 @@ import org.jsoup.Jsoup
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import java.time.LocalDateTime
import java.time.Instant
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
@Entity(tableName = "article")
@ -52,12 +55,17 @@ data class Article(
val feed: Feed?
) {
var pubDate: ZonedDateTime?
get() = pubDate_stamp?.let {
ZonedDateTime.ofInstant(
Instant.ofEpochSecond(pubDate_stamp!!),
ZoneId.systemDefault()
)
}
set(value) {
pubDate_stamp = value?.toLocalDateTime()?.toEpochSecond(ZoneOffset.UTC)
}
@Ignore
val pubDate : LocalDateTime?
init {
pubDate = pubDate_stamp?.let { LocalDateTime.ofEpochSecond(it,0, ZoneOffset.UTC) }
}
override fun toString(): String {
return "Article{" +
"title='" + title + '\'' +
@ -68,12 +76,21 @@ data class Article(
", pubDate=" + pubDate +
'}'
}
constructor() : this(null,"","","","","",null,null){
constructor() : this(null, "", "", "", "", "", null, null) {
}
override fun equals(other: Any?): Boolean {
return if (other is Article) {
return title == other.title && url == other.url
} else return false
}
@ColumnInfo(name = "containingFeedId")
var feedId: Long = 0
companion object {
/**
* Cikk beolvasása egy megadott Node objektumból. Egy hírcsatorna beolvasásakor könnyedén alkalmazható egy "item" Node-ra
@ -86,7 +103,7 @@ data class Article(
var description: String? = null
var imageURL: String? = null
var author: String? = null
var pubDate: LocalDateTime? = null
var pubDate: ZonedDateTime? = null
for (j in 0 until properties.getLength()) {
val node: Node = properties.item(j)
val tagName: String = node.getNodeName()
@ -94,7 +111,7 @@ data class Article(
when (tagName) {
"title" -> title = node.getTextContent()
"author", "dc:creator" -> author = node.getTextContent()
"pubDate" -> pubDate = LocalDateTime.parse(
"pubDate" -> pubDate = ZonedDateTime.parse(
node.textContent,
DateTimeFormatter.RFC_1123_DATE_TIME
)
@ -112,6 +129,7 @@ data class Article(
}
}
}
Log.e(null, "$title ${pubDate.toString()}")
if (imageURL == null && url != null) {
runBlocking(IO) {
val con = Jsoup.connect(url)
@ -128,8 +146,16 @@ data class Article(
}
}
}
return Article(null,title, url, description, imageURL, author, pubDate?.toEpochSecond(
ZoneOffset.UTC), feed)
return Article(
null,
title,
url,
description,
imageURL,
author,
pubDate?.toEpochSecond(),
feed
)
}
}

View file

@ -7,7 +7,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.LiveData
import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticles
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticlesDAO
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
@ -24,11 +23,29 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
private val _isRefereshing: MutableState<Boolean> = mutableStateOf(false)
public val isRefeshing: State<Boolean>
val isRefeshing: State<Boolean>
get() = _isRefereshing
fun getAllArticles(): LiveData<List<FeedsAndArticles>> {
return feedsAndArticleDAO.getAll()
private var limit: Feed? = null
private val _articleState: MutableState<List<Article>> = mutableStateOf(emptyList())
val articleState: State<List<Article>> = _articleState
suspend fun initArticles() = withContext(IO) {
_articleState.value = getAllArticles()
}
suspend fun getAllArticles(): List<Article> {
return if (limit != null) {
_isRefereshing.value = true
withContext(IO) {
feedsAndArticleDAO.getArticlesByFeed(limit!!.feedId!!).also {
_isRefereshing.value = false
}
}
} else withContext(IO) { feedsAndArticleDAO.getAllList() }
}
suspend fun instert(feed: Feed, article: Article) {
@ -42,9 +59,14 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
withContext(IO) {
try {
_isRefereshing.value = true
val feeds = feedsAndArticleDAO.getFeedsList()
feeds?.forEach { feed ->
updateFeed(feed)
if (limit != null) {
updateFeed(limit!!)
} else {
val feeds = feedsAndArticleDAO.getFeedsList()
feeds?.forEach { feed ->
updateFeed(feed)
}
}
_isRefereshing.value = false
} catch (e: Exception) {
@ -71,7 +93,9 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
for (i in 0 until items.length) {
val article: Node = items.item(i)
val a = withContext(Dispatchers.IO) { Article.createFromNode(article, feed = feed) }
feedsAndArticleDAO.insertArticle(feed, a)
val a1 = withContext(IO) { a.url?.let { feedsAndArticleDAO.getArticleByURL(it) } }
if (a != a1)
feedsAndArticleDAO.insertArticle(feed, a)
}
feed.faviconURL = withContext(Dispatchers.IO) {
@ -89,4 +113,9 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
feedsAndArticleDAO.insertFeed(feed)
updateFeeds()
}
suspend fun setLimit(feed: Feed?) {
limit = feed
_articleState.value = getAllArticles()
}
}

View file

@ -9,15 +9,24 @@ import eu.toldi.balazs.anotherfeedreader.entities.Feed
interface FeedsAndArticlesDAO {
@Transaction
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate")
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate DESC")
fun getAll(): LiveData<List<FeedsAndArticles>>
@Transaction
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate DESC")
fun getAllList(): List<Article>
@Query("SELECT * FROM feed")
fun getFeeds(): LiveData<List<Feed>>
@Query("SELECT * FROM feed")
fun getFeedsList(): List<Feed>
@Query("SELECT * FROM article WHERE url = :url")
fun getArticleByURL(url: String): Article
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId WHERE feed.feedId = :feedId ORDER BY article.pubDate DESC")
fun getArticlesByFeed(feedId: Long): List<Article>
fun insertArticle(feed: Feed, article: Article) {
feed.feedId?.let { article.feedId = it }

View file

@ -5,10 +5,10 @@ import androidx.compose.runtime.State
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.repository.FeedRepository
import eu.toldi.balazs.anotherfeedreader.sqlite.AppDatabase
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticles
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -16,15 +16,19 @@ import kotlinx.coroutines.launch
class FeedViewModel(application: Application) : AndroidViewModel(application) {
private val feedRepo: FeedRepository
val articles: LiveData<List<FeedsAndArticles>>
val feedsState: LiveData<List<Feed>>
val articles: State<List<Article>>
val feeds: LiveData<List<Feed>>
val isRefereshing: State<Boolean>
init {
val feedsAndArticlesDao = AppDatabase.getInstance(application).feedsAndArticlesDao()
feedRepo = FeedRepository(feedsAndArticleDAO = feedsAndArticlesDao)
articles = feedRepo.getAllArticles()
feedsState = feedRepo.getFeedList()
articles = feedRepo.articleState
viewModelScope.launch(IO) {
//feedRepo.initArticles()
feedRepo.updateFeeds()
}
feeds = feedRepo.getFeedList()
isRefereshing = feedRepo.isRefeshing
}
@ -36,12 +40,19 @@ class FeedViewModel(application: Application) : AndroidViewModel(application) {
fun changeFeed() {
viewModelScope.launch {
}
}
fun changeFeedList() {
viewModelScope.launch {
//feedsState.value = feedRepo.getFeedList().value
}
}
fun setLimit(feed: Feed?) {
viewModelScope.launch {
feedRepo.setLimit(feed)
}
}

View file

@ -8,7 +8,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.2"
classpath 'com.android.tools.build:gradle:7.0.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
// NOTE: Do not place your application dependencies here; they belong