This commit is contained in:
parent
652e2f2f32
commit
af94e16f4e
12 changed files with 317 additions and 78 deletions
|
@ -1,6 +1,7 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
}
|
||||
|
||||
android {
|
||||
|
@ -47,6 +48,15 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
def room_version = "2.3.0"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
annotationProcessor "androidx.room:room-compiler:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
// optional - RxJava2 support for Room
|
||||
implementation "androidx.room:room-rxjava2:$room_version"
|
||||
|
||||
|
||||
def lifecycle_version = "2.4.0-rc01"
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
|||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
|
@ -16,6 +17,7 @@ import androidx.compose.material.*
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
@ -30,14 +32,13 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import coil.compose.rememberImagePainter
|
||||
import com.google.accompanist.swiperefresh.SwipeRefresh
|
||||
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedData
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.Repo
|
||||
import eu.toldi.balazs.anotherfeedreader.ui.theme.AnotherFeedReaderTheme
|
||||
import eu.toldi.balazs.anotherfeedreader.viewmodel.FeedViewModel
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
|
@ -46,40 +47,31 @@ import kotlinx.coroutines.Job
|
|||
import kotlinx.coroutines.launch
|
||||
import java.net.URL
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
val feeds = FeedData().apply {
|
||||
addFeed(FeedGroup("magyar").apply {
|
||||
addFeed(Feed(URL("https://hvg.hu/rss")))
|
||||
addFeed(Feed(URL("https://telex.hu/rss")))
|
||||
addFeed(Feed(URL("https://24.hu/feed")))
|
||||
addFeed(Feed(URL("https://444.hu/feed")))
|
||||
})
|
||||
addFeed(Feed(URL("https://www.theguardian.com/world/rss")))
|
||||
}
|
||||
lateinit var viewModel: FeedViewModel
|
||||
|
||||
private val viewModel: FeedViewModel by viewModels()
|
||||
var syncJob: Job? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
AnotherFeedReaderTheme {
|
||||
// A surface container using the 'background' color from the theme
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
viewModel = ViewModelProvider(this).get(FeedViewModel::class.java).apply {
|
||||
feedData = feeds
|
||||
}
|
||||
viewModel.updateFeed()
|
||||
viewModel.changeFeedList()
|
||||
viewModel.updateFeeds()
|
||||
|
||||
var isAddFeedDialogOpen by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val articleList = viewModel.articles.value
|
||||
val articleList = viewModel.articles.observeAsState(emptyList())
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
|
@ -102,7 +94,7 @@ class MainActivity : ComponentActivity() {
|
|||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = {
|
||||
viewModel.updateFeed()
|
||||
viewModel.updateFeeds()
|
||||
},
|
||||
) {
|
||||
LazyColumn(
|
||||
|
@ -111,8 +103,8 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
|
||||
) {
|
||||
items(articleList.size) { index ->
|
||||
val article = articleList[index]
|
||||
items(articleList.value.size) { index ->
|
||||
val article = articleList.value[index]
|
||||
showArticle(article = article)
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +129,7 @@ class MainActivity : ComponentActivity() {
|
|||
Text(text = "Add feed")
|
||||
},
|
||||
text = {
|
||||
Column() {
|
||||
Column {
|
||||
OutlinedTextField(
|
||||
value = text,
|
||||
onValueChange = {
|
||||
|
@ -157,7 +149,8 @@ class MainActivity : ComponentActivity() {
|
|||
.wrapContentSize(Alignment.TopStart)
|
||||
) {
|
||||
Text(
|
||||
text = if (selectedIndex == 0) "None" else feeds.feedList.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
|
||||
|
||||
text = if (selectedIndex == 0) "None" else viewModel.feedsState.value.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { expanded = true })
|
||||
|
@ -173,13 +166,13 @@ class MainActivity : ComponentActivity() {
|
|||
}) {
|
||||
Text(text = "None")
|
||||
}
|
||||
feeds.feedList.filterIsInstance<FeedGroup>()
|
||||
viewModel.feedsState.value.filterIsInstance<FeedGroup>()
|
||||
.forEachIndexed { index, s ->
|
||||
DropdownMenuItem(onClick = {
|
||||
selectedIndex = index + 1
|
||||
expanded = false
|
||||
}) {
|
||||
feeds.feedList[index].name?.let {
|
||||
viewModel.feedsState.value[index].name?.let {
|
||||
Text(text = it)
|
||||
}
|
||||
}
|
||||
|
@ -205,10 +198,10 @@ class MainActivity : ComponentActivity() {
|
|||
val feed = Feed(URL(text))
|
||||
syncJob = GlobalScope.launch(IO) { feed.updateFeed() }
|
||||
if (selectedIndex == 0)
|
||||
feeds.addFeed(feed)
|
||||
viewModel.addFeed(feed)
|
||||
else {
|
||||
var counter = 0
|
||||
feeds.feedList.forEachIndexed { index, feedGroup ->
|
||||
viewModel.feedsState.value.forEachIndexed { index, feedGroup ->
|
||||
if (feedGroup is FeedGroup) {
|
||||
counter++
|
||||
if (counter == selectedIndex) {
|
||||
|
@ -217,7 +210,6 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
viewModel.feedData = feeds
|
||||
viewModel.changeFeedList()
|
||||
} catch (e: Exception) {
|
||||
|
||||
|
@ -303,7 +295,7 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
WebView.webURL = article.url.toString()
|
||||
WebView.barTitle =
|
||||
article.feed.name.toString()
|
||||
article.feed?.name.toString() ?: ""
|
||||
startActivity(
|
||||
Intent(
|
||||
baseContext,
|
||||
|
@ -322,7 +314,7 @@ class MainActivity : ComponentActivity() {
|
|||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val painter = if (article.feed.faviconURL != null) {
|
||||
val painter = if (article.feed?.faviconURL != null) {
|
||||
rememberImagePainter(article.feed.faviconURL)
|
||||
} else {
|
||||
painterResource(id = R.drawable.ic_launcher_background)
|
||||
|
@ -345,7 +337,7 @@ class MainActivity : ComponentActivity() {
|
|||
modifier = Modifier.padding(horizontal = 2.dp)
|
||||
)
|
||||
}
|
||||
article.feed.name?.let {
|
||||
article.feed?.name?.let {
|
||||
Text(
|
||||
text = (fun(): String {
|
||||
return if (it.length > 25)
|
||||
|
@ -382,7 +374,7 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
if (article.title != null) {
|
||||
Text(
|
||||
text = article.title.trim(),
|
||||
text = article.title!!.trim(),
|
||||
fontSize = 20.sp,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
|
@ -390,7 +382,7 @@ class MainActivity : ComponentActivity() {
|
|||
} else Text("")
|
||||
if (article.description != null) {
|
||||
Text(
|
||||
text = article.description,
|
||||
text = article.description!!,
|
||||
fontSize = 17.sp,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
|
@ -411,9 +403,9 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp, horizontal = 4.dp)
|
||||
.clickable {
|
||||
feeds.limit = if (feeds.limit == feed) {
|
||||
/*feeds.limit = if (feeds.limit == feed) {
|
||||
null
|
||||
} else feed
|
||||
} else feed*/
|
||||
viewModel.changeFeed()
|
||||
},
|
||||
elevation = 10.dp,
|
||||
|
@ -456,9 +448,9 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
.padding(start = 25.dp, top = 5.dp, end = 4.dp, bottom = 5.dp)
|
||||
.clickable {
|
||||
feeds.limit = if (feeds.limit == it) {
|
||||
/*feeds.limit = if (feeds.limit == it) {
|
||||
null
|
||||
} else it
|
||||
} else it*/
|
||||
viewModel.changeFeed()
|
||||
},
|
||||
elevation = 10.dp,
|
||||
|
@ -493,7 +485,7 @@ class MainActivity : ComponentActivity() {
|
|||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp, horizontal = 4.dp)
|
||||
.clickable {
|
||||
feeds.limit = null
|
||||
//feeds.limit = null
|
||||
viewModel.changeFeed()
|
||||
},
|
||||
elevation = 10.dp,
|
||||
|
@ -522,11 +514,11 @@ class MainActivity : ComponentActivity() {
|
|||
AnotherFeedReaderTheme {
|
||||
Column() {
|
||||
MenuBar({})
|
||||
val article = Article(
|
||||
val article = Article(null,
|
||||
title = "Teszt Article",
|
||||
description = "Ez az cikk azért létezik, hogy kipróbájam a cikkek megjelenítését.",
|
||||
author = "Toldi Balázs",
|
||||
pubDate = LocalDateTime.now(),
|
||||
pubDate_stamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC),
|
||||
feed = Feed("Test 1")
|
||||
)
|
||||
Column(
|
||||
|
@ -534,11 +526,12 @@ class MainActivity : ComponentActivity() {
|
|||
) {
|
||||
showArticle(article)
|
||||
showArticle(
|
||||
Article(
|
||||
Article(null,
|
||||
title = "Teszt Article #2",
|
||||
description = "Ez egy másik megjelenítést tesztelő cikk. Ebben egy nagyon hosszú szöveget akarok ábrázolni, aminek nem szabad egyben kiférnie. Legfeljebb 2 sort jeleníthet meg",
|
||||
author = "Toldi Balázs",
|
||||
pubDate = LocalDateTime.now().plusDays(-1),
|
||||
pubDate_stamp = LocalDateTime.now().plusDays(-1).toEpochSecond(
|
||||
ZoneOffset.UTC),
|
||||
feed = Feed("Test 1")
|
||||
)
|
||||
)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -9,40 +13,53 @@ import org.w3c.dom.Element
|
|||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
|
||||
class Article(
|
||||
@Entity(tableName = "article")
|
||||
data class Article(
|
||||
@PrimaryKey(autoGenerate= true) var articleId: Long?,
|
||||
/**
|
||||
* A Cikk címe
|
||||
*/
|
||||
val title: String? = null,
|
||||
@ColumnInfo(name = "title")
|
||||
var title: String? = null,
|
||||
/**
|
||||
* A cíkk elérési útvonala
|
||||
*/
|
||||
val url: String? = null,
|
||||
@ColumnInfo(name = "url")
|
||||
var url: String? = null,
|
||||
/**
|
||||
* A cikkhez tartozó leírás
|
||||
*/
|
||||
val description: String? = null,
|
||||
@ColumnInfo(name = "description")
|
||||
var description: String? = null,
|
||||
/**
|
||||
* Cikkhez tartozó kép elérési útvonala
|
||||
*/
|
||||
val imageURL: String? = null,
|
||||
@ColumnInfo(name = "imageURL")
|
||||
var imageURL: String? = null,
|
||||
/**
|
||||
* A cikk szerzője
|
||||
*/
|
||||
val author: String? = null,
|
||||
|
||||
val pubDate: LocalDateTime? = null,
|
||||
|
||||
val feed: Feed
|
||||
) {
|
||||
|
||||
@ColumnInfo(name = "author")
|
||||
var author: String? = null,
|
||||
/**
|
||||
* A cikk kiadásának dátuma
|
||||
*/
|
||||
@ColumnInfo(name = "pubDate")
|
||||
var pubDate_stamp : Long?,
|
||||
|
||||
@Ignore
|
||||
val feed: Feed?
|
||||
) {
|
||||
|
||||
|
||||
@Ignore
|
||||
val pubDate : LocalDateTime?
|
||||
init {
|
||||
pubDate = pubDate_stamp?.let { LocalDateTime.ofEpochSecond(it,0, ZoneOffset.UTC) }
|
||||
}
|
||||
override fun toString(): String {
|
||||
return "Article{" +
|
||||
"title='" + title + '\'' +
|
||||
|
@ -53,7 +70,12 @@ class Article(
|
|||
", pubDate=" + pubDate +
|
||||
'}'
|
||||
}
|
||||
constructor() : this(null,"","","","","",null,null){
|
||||
|
||||
}
|
||||
|
||||
@ColumnInfo(name = "")
|
||||
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
|
||||
|
@ -93,7 +115,7 @@ class Article(
|
|||
}
|
||||
}
|
||||
if (imageURL == null && url != null) {
|
||||
runBlocking (IO) {
|
||||
runBlocking(IO) {
|
||||
val con = Jsoup.connect(url)
|
||||
val doc = con.userAgent("Mozilla").get()
|
||||
|
||||
|
@ -108,7 +130,8 @@ class Article(
|
|||
}
|
||||
}
|
||||
}
|
||||
return Article(title, url, description, imageURL, author, pubDate, feed)
|
||||
return Article(null,title, url, description, imageURL, author, pubDate?.toEpochSecond(
|
||||
ZoneOffset.UTC), feed)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
import android.util.Log
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import eu.toldi.balazs.anotherfeedreader.MainActivity
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.Repo
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import net.mm2d.touchicon.TouchIconExtractor
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Node
|
||||
|
@ -10,23 +17,29 @@ import java.net.URL
|
|||
import javax.xml.parsers.DocumentBuilder
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
|
||||
@Entity
|
||||
open class Feed
|
||||
(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var feedId: Long? = null,
|
||||
/**
|
||||
* A hírcsatorna elérési útvonala
|
||||
*/
|
||||
val link: URL
|
||||
@ColumnInfo(name = "feedURL")
|
||||
var feedURL: String
|
||||
) {
|
||||
|
||||
@ColumnInfo(name = "faviconURL")
|
||||
var faviconURL: String? = null
|
||||
|
||||
/**
|
||||
* A hírcsatorna neve
|
||||
*/
|
||||
@ColumnInfo(name = "title")
|
||||
var name: String? = null
|
||||
@Ignore
|
||||
val link = URL(feedURL)
|
||||
|
||||
constructor(name: String) : this(URL("http://example.com")) {
|
||||
constructor(name: String) : this(null,"http://example.com") {
|
||||
this.name = name
|
||||
}
|
||||
|
||||
|
@ -70,7 +83,6 @@ open class Feed
|
|||
if (doc.getElementsByTagName("title").length > 0)
|
||||
name = doc.getElementsByTagName("title").item(0).getTextContent()
|
||||
val items: NodeList = doc.getElementsByTagName("item")
|
||||
|
||||
for (i in 0 until items.length) {
|
||||
val article: Node = items.item(i)
|
||||
val a = withContext(Dispatchers.IO) {Article.createFromNode(article, this@Feed)}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.repository
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedData
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.ArticleDAO
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.net.URL
|
||||
|
||||
class FeedRepository(private val articleDAO: ArticleDAO) {
|
||||
|
||||
val feedData = FeedData().apply {
|
||||
addFeed(FeedGroup("magyar").apply {
|
||||
addFeed(Feed(feedURL = "https://hvg.hu/rss"))
|
||||
addFeed(Feed(feedURL = "https://telex.hu/rss"))
|
||||
addFeed(Feed(feedURL = "https://24.hu/feed"))
|
||||
addFeed(Feed(feedURL = "https://444.hu/feed"))
|
||||
})
|
||||
addFeed(Feed(feedURL = "https://www.theguardian.com/world/rss"))
|
||||
}
|
||||
|
||||
fun getAllArticles(): LiveData<List<Article>> {
|
||||
return articleDAO.getAll()
|
||||
}
|
||||
|
||||
suspend fun instert(article: Article){
|
||||
articleDAO.insertAll(article)
|
||||
}
|
||||
|
||||
suspend fun delete(article: Article){
|
||||
articleDAO.delete(article)
|
||||
}
|
||||
|
||||
fun getFeedList() : List<Feed> = feedData.feedList
|
||||
|
||||
suspend fun updateFeeds() {
|
||||
var done = false
|
||||
feedData.limit?.let {
|
||||
it.updateFeed()
|
||||
done = true
|
||||
}
|
||||
if (!done)
|
||||
feedData.updateFeed()
|
||||
|
||||
feedData.articleList.forEach {
|
||||
withContext(IO){
|
||||
instert(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addFeed(feed: Feed) {
|
||||
feedData.addFeed(feed)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.sqlite
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
|
||||
@Database(entities = [Article::class], version = 1)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun articleDao(): ArticleDAO
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
|
||||
fun getInstance(context: Context): AppDatabase {
|
||||
return INSTANCE ?: synchronized(this) {
|
||||
INSTANCE ?: Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"feed_database"
|
||||
).fallbackToDestructiveMigration().build().also { INSTANCE = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.sqlite
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
|
||||
@Dao
|
||||
interface ArticleDAO {
|
||||
@Query("SELECT * FROM article")
|
||||
fun getAll(): LiveData<List<Article>>
|
||||
|
||||
@Query("SELECT * FROM article WHERE articleId IN (:articleIds)")
|
||||
fun loadAllByIds(articleIds: IntArray): List<Article>
|
||||
|
||||
@Query("SELECT * FROM article WHERE url=:url")
|
||||
fun findByName(url: String): Article?
|
||||
|
||||
@Insert
|
||||
fun insertAll(vararg articles: Article)
|
||||
|
||||
@Delete
|
||||
fun delete(article: Article)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.sqlite
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
|
||||
@Dao
|
||||
interface FeedDAO {
|
||||
@Query("SELECT * FROM feed")
|
||||
fun getAll(): LiveData<List<Feed>>
|
||||
|
||||
@Query("SELECT * FROM feed WHERE feedId IN (:feedIds)")
|
||||
fun loadAllByIds(feedIds: IntArray): List<Feed>
|
||||
|
||||
@Query("SELECT * FROM article WHERE url=:url")
|
||||
fun findByName(url: String): Feed?
|
||||
|
||||
@Insert
|
||||
fun insertAll(vararg feeds: Feed)
|
||||
|
||||
@Delete
|
||||
fun delete(feed: Feed)
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.sqlite
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Relation
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
|
||||
data class FeedsAndArticles(
|
||||
@Embedded val feed: Feed,
|
||||
@Relation(
|
||||
parentColumn = "feedId",
|
||||
entityColumn = "feedId"
|
||||
)
|
||||
val articles: List<Article>
|
||||
)
|
|
@ -0,0 +1,25 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.sqlite
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
|
||||
@Dao
|
||||
interface FeedsAndArticlesDAO {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM feed")
|
||||
fun getAll(): LiveData<List<FeedsAndArticles>>
|
||||
|
||||
@Query("SELECT * FROM article WHERE articleId IN (:articleIds)")
|
||||
fun loadAllByIds(articleIds: IntArray): List<Article>
|
||||
|
||||
@Query("SELECT * FROM article WHERE url=:url")
|
||||
fun findArticleByURL(url: String): Article?
|
||||
|
||||
@Insert
|
||||
fun insertArticles(vararg articles: Article)
|
||||
|
||||
@Delete
|
||||
fun deleteArticle(article: Article)
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.sqlite
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
|
||||
object Repo {
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
fun initDb(context: Context) {
|
||||
db = Room.databaseBuilder(
|
||||
context,
|
||||
AppDatabase::class.java, "article"
|
||||
).build()
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +1,42 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Article
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.Feed
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedData
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
|
||||
import eu.toldi.balazs.anotherfeedreader.repository.FeedRepository
|
||||
import eu.toldi.balazs.anotherfeedreader.sqlite.AppDatabase
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URL
|
||||
|
||||
class FeedViewModel : ViewModel() {
|
||||
class FeedViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
var feedData = FeedData()
|
||||
val articles: MutableState<List<Article>> = mutableStateOf(listOf())
|
||||
private val feedRepo: FeedRepository
|
||||
val articles: LiveData<List<Article>>
|
||||
val feedsState = mutableStateOf(emptyList<Feed>())
|
||||
val isRefereshing: MutableState<Boolean> = mutableStateOf(false)
|
||||
|
||||
init {
|
||||
val articleDAO = AppDatabase.getInstance(application).articleDao()
|
||||
feedRepo = FeedRepository(articleDAO = articleDAO)
|
||||
articles = feedRepo.getAllArticles()
|
||||
viewModelScope.launch {
|
||||
articles.value = feedData.articleList
|
||||
feedsState.value = feedData.feedList
|
||||
feedsState.value = feedRepo.getFeedList()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFeed(): Job {
|
||||
fun updateFeeds(): Job {
|
||||
return viewModelScope.launch {
|
||||
isRefereshing.value = true
|
||||
var done = false
|
||||
feedData.limit?.let {
|
||||
it.updateFeed()
|
||||
done = true
|
||||
}
|
||||
if (!done)
|
||||
feedData.updateFeed()
|
||||
articles.value = feedData.articleList
|
||||
feedRepo.updateFeeds()
|
||||
isRefereshing.value = false
|
||||
}
|
||||
}
|
||||
|
@ -42,14 +44,19 @@ class FeedViewModel : ViewModel() {
|
|||
fun changeFeed() {
|
||||
viewModelScope.launch {
|
||||
isRefereshing.value = true
|
||||
articles.value = feedData.articleList
|
||||
///articles.value = feedRepo.getFeedList()
|
||||
isRefereshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun changeFeedList() {
|
||||
viewModelScope.launch {
|
||||
feedsState.value = feedData.feedList
|
||||
feedsState.value = feedRepo.getFeedList()
|
||||
}
|
||||
}
|
||||
|
||||
fun addFeed(feed: Feed){
|
||||
feedRepo.addFeed(feed)
|
||||
feedsState.value = feedRepo.getFeedList()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue