Added back FeedGroups
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 19:48:13 +02:00
parent 1257055704
commit ee303f25f1
Signed by: Bazsalanszky
GPG key ID: 933820884952BE27
10 changed files with 185 additions and 160 deletions

View file

@ -31,6 +31,7 @@
<entry key="../../../../../layout/compose-model-1634139145792.xml" value="0.8684834123222749" />
<entry key="../../../../../layout/compose-model-1635019781952.xml" value="0.5435185185185185" />
<entry key="../../../../../layout/compose-model-1635084830227.xml" value="0.67" />
<entry key="../../../../../layout/compose-model-1635090943966.xml" value="0.33" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.21458333333333332" />
</map>
</option>

View file

@ -130,13 +130,15 @@ class MainActivity : ComponentActivity() {
@Composable
fun showAddFeedDialog(toClose: () -> Unit) {
var text by remember { mutableStateOf("") }
var groupName by remember { mutableStateOf("New Group") }
var selectedIndex by remember { mutableStateOf(0) }
val feedGroups = viewModel.feedGroups.observeAsState(emptyList())
AlertDialog(onDismissRequest = { toClose() },
title = {
Text(text = "Add feed")
},
text = {
Column {
Column(modifier = Modifier.fillMaxHeight(0.6f)) {
OutlinedTextField(
value = text,
onValueChange = {
@ -157,7 +159,11 @@ class MainActivity : ComponentActivity() {
) {
Text(
text = if (selectedIndex == 0) "None" else viewModel.feeds.value!!.filterIsInstance<FeedGroup>()[selectedIndex - 1].name.toString(),
text = when (selectedIndex) {
0 -> "None"
feedGroups.value.size + 1 -> groupName
else -> feedGroups.value[selectedIndex - 1].name
},
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { expanded = true })
@ -174,19 +180,33 @@ class MainActivity : ComponentActivity() {
Text(text = "None")
}
viewModel.feeds.value!!.filterIsInstance<FeedGroup>()
feedGroups.value
.forEachIndexed { index, s ->
DropdownMenuItem(onClick = {
selectedIndex = index + 1
expanded = false
}) {
viewModel.feeds.value!![index].name?.let {
Text(text = it)
}
Text(text = feedGroups.value[index].name)
}
}
DropdownMenuItem(onClick = {
selectedIndex = feedGroups.value.size + 1
expanded = false
}) {
Text(text = "Create new group")
}
}
}
if (selectedIndex == feedGroups.value.size + 1) {
OutlinedTextField(
value = groupName,
onValueChange = {
groupName = it
},
label = { Text("Group name") },
modifier = Modifier.padding(all = 4.dp)
)
}
}
},
buttons = {
@ -203,23 +223,29 @@ class MainActivity : ComponentActivity() {
Button(
onClick = {
try {
val feed = Feed(null,text)
if (selectedIndex == 0)
viewModel.addFeed(feed)
else {
var counter = 0
viewModel.feeds.value!!.forEachIndexed { index, feedGroup ->
if (feedGroup is FeedGroup) {
counter++
if (counter == selectedIndex) {
feedGroup.addFeed(feed)
val feed = Feed(null, text)
when (selectedIndex) {
0 -> viewModel.addFeed(feed)
feedGroups.value.size + 1 -> {
val groupId =
if (feedGroups.value.filter { it.name == groupName }
.isNotEmpty()) {
feedGroups.value.first { it.name == groupName }.id
} else {
viewModel.addFeedGroup(FeedGroup(name = groupName))
feedGroups.value.maxOf { it.id!! } + 1
}
}
feed.feedGroupId = groupId
viewModel.addFeed(feed)
}
else -> {
feed.feedGroupId = feedGroups.value[selectedIndex - 1].id
viewModel.addFeed(feed)
}
}
viewModel.changeFeedList()
} catch (e: Exception) {
Log.e(null, e.stackTraceToString())
} finally {
toClose()
}
@ -229,7 +255,8 @@ class MainActivity : ComponentActivity() {
Text("Add Feed")
}
}
}
},
shape = MaterialTheme.shapes.large
)
}
@ -395,8 +422,8 @@ class MainActivity : ComponentActivity() {
}
@Composable
fun FeedSideBar(feed: Feed) {
Column() {
fun FeedSideBar(inputElement: Any) {
Column {
var collapsed by remember {
mutableStateOf(true)
}
@ -405,13 +432,14 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth()
.padding(vertical = 5.dp, horizontal = 4.dp)
.clickable {
viewModel.setLimit(feed)
if (inputElement is Feed || inputElement is FeedGroup)
viewModel.setLimit(inputElement)
},
elevation = 10.dp,
backgroundColor = MaterialTheme.colors.surface,
) {
Row {
if (feed is FeedGroup) {
if (inputElement is FeedGroup) {
IconButton(onClick = {
collapsed = collapsed.not()
}) {
@ -426,31 +454,37 @@ class MainActivity : ComponentActivity() {
contentDescription = "Hide feeds contained"
)
}
}
feed.name?.let {
Text(
text = it,
text = inputElement.name,
fontSize = 26.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(all = 4.dp)
)
} else if (inputElement is Feed) {
inputElement.name?.let {
Text(
text = it,
fontSize = 26.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(all = 4.dp)
)
}
}
}
}
if (!collapsed) {
val feedGroup = feed as FeedGroup
feedGroup.feedList.forEach {
Log.w(null, it.name.toString())
val feedGroup = inputElement as FeedGroup
val feeds = viewModel.getFeedByGroup(feedGroup.id).observeAsState(emptyList())
feeds.value.forEach {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(start = 25.dp, top = 5.dp, end = 4.dp, bottom = 5.dp)
.clickable {
/*feeds.limit = if (feeds.limit == it) {
null
} else it*/
viewModel.changeFeed()
viewModel.setLimit(it)
},
elevation = 10.dp,
backgroundColor = MaterialTheme.colors.surface,
@ -475,7 +509,8 @@ class MainActivity : ComponentActivity() {
drawerState: DrawerState,
content: @Composable () -> Unit
) {
val feedLiveData = viewModel.feeds.observeAsState(emptyList())
val feedLiveData = viewModel.getFeedByGroup(null).observeAsState(emptyList())
val groups = viewModel.feedGroups.observeAsState(initial = emptyList())
val feedList = feedLiveData.value
ModalDrawer(drawerState = drawerState, drawerContent = {
LazyColumn(modifier = Modifier.fillMaxHeight()) {
@ -499,8 +534,11 @@ class MainActivity : ComponentActivity() {
)
}
}
items(groups.value.size) { index ->
FeedSideBar(inputElement = groups.value[index])
}
items(feedList.size) { index ->
FeedSideBar(feed = feedList[index])
FeedSideBar(feedList[index])
}
}

View file

@ -15,7 +15,9 @@ open class Feed
* A hírcsatorna elérési útvonala
*/
@ColumnInfo(name = "feedURL")
var feedURL: String
var feedURL: String,
@ColumnInfo(name = "feedGroupId")
var feedGroupId: Long? = null
) {
@ColumnInfo(name = "faviconURL")
var faviconURL: String? = null

View file

@ -1,15 +0,0 @@
package eu.toldi.balazs.anotherfeedreader.entities
class FeedData : FeedGroup("MainFrame") {
var limit : Feed? = null
private var _articleList = emptyList<Article>()
override var articleList: List<Article>
get() = limit?.articleList?.sortedByDescending { it.pubDate } ?: _articleList
set(value) {
_articleList = value.sortedByDescending { it.pubDate }
}
}

View file

@ -1,95 +1,14 @@
package eu.toldi.balazs.anotherfeedreader.entities
import java.util.*
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
open class FeedGroup(name: String) : Feed(name) {
/**
* A hírcsatornák listája
*/
var feedList: MutableList<Feed> = ArrayList()
private var _articleList = emptyList<Article>()
override var articleList: List<Article>
get() = _articleList
set(value) {
_articleList = value.sortedByDescending { it.pubDate }
}
/**
* Hírcsatorna hozzáadása a csoporthoz
* @param feed hozzáadandó hírcsatorna
* Megjegyzés: Meglévő csatorna nem adható hozzá mégegyszer
*/
fun addFeed(feed: Feed) {
if (!feedExists(feed)) {
feedList.add(feed)
feed.articleList.forEach { article ->
if (articleList.firstOrNull { it.url == article.url } == null)
articleList += article
}
}
}
/**
* Ellenőrzi,hogy a megadott hírcsatorna metalálható-e benne
* @param f az ellenőrizendő csatorna
* @return logikai érték az alapján,hogy a csatorna megtalálható-e a csoportban
* Megjegyzés: Csak abban az esetben tér vissza igazzal,ha a két objektum megegyezik egy a csoportban találhatóval
*/
fun feedExists(f: Feed): Boolean {
for (i in feedList.indices) {
if (f === feedList[i]) return true
}
return false
}
/**
* @see Feed.getArticleCount
* @return
*/
override val articleCount: Int
get() {
var result = 0
for (f in feedList) {
result += f.articleCount
}
return result
}
/**
* Hírcsatorna törlése a csoportból
* @param feed a törlendő hírcsatorna
* Megjegyzés: Ez törli az össze a csatornához tartozó cikket.
*/
fun removeFeed(feed: Feed) {
var found = false
for (i in feedList.indices) {
//Csak ha PONTOSAN megegyezik
if (feedList[i] === feed) {
feedList.removeAt(i)
found = true
break
}
}
if (found) {
val similar: MutableList<Article> = ArrayList(articleList)
val different: MutableList<Article> = ArrayList()
different.addAll(articleList)
different.addAll(feed.articleList)
similar.retainAll(feed.articleList)
different.removeAll(similar)
articleList = different
}
}
/**
* Egy hírcsatorna áthelyezése a listában. Átrendezésre használatos.
* @param f az áthelyezendő hírcsatorna
* @param index a hírcsatorna új helye
*/
fun setLocation(f: Feed, index: Int) {
feedList.remove(f)
feedList.add(index, f)
}
}
@Entity(tableName = "feedGroup", indices = [Index(value = ["groupName"], unique = true)])
open class FeedGroup(
@PrimaryKey(autoGenerate = true)
val id: Long? = null,
@ColumnInfo(name = "groupName")
val name: String
)

View file

@ -7,6 +7,7 @@ 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.entities.FeedGroup
import eu.toldi.balazs.anotherfeedreader.sqlite.FeedsAndArticlesDAO
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
@ -26,7 +27,7 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
val isRefeshing: State<Boolean>
get() = _isRefereshing
private var limit: Feed? = null
private var limit: Any? = null
private val _articleState: MutableState<List<Article>> = mutableStateOf(emptyList())
@ -36,16 +37,31 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
_articleState.value = getAllArticles()
}
fun getFeedGroups(): LiveData<List<FeedGroup>> = feedsAndArticleDAO.getFeedGroups()
fun getFeedsByGroup(id: Long?) = feedsAndArticleDAO.getFeedsByGroup(id)
suspend fun getAllArticles(): List<Article> {
return if (limit != null) {
_isRefereshing.value = true
withContext(IO) {
feedsAndArticleDAO.getArticlesByFeed(limit!!.feedId!!).also {
_isRefereshing.value = false
return when (limit) {
is Feed -> {
_isRefereshing.value = true
withContext(IO) {
feedsAndArticleDAO.getArticlesByFeed((limit as Feed).feedId!!).also {
_isRefereshing.value = false
}
}
}
} else withContext(IO) { feedsAndArticleDAO.getAllList() }
is FeedGroup -> {
_isRefereshing.value = true
withContext(IO) {
feedsAndArticleDAO.getAllFromGroup((limit as FeedGroup).id!!).also {
_isRefereshing.value = false
}
}
}
else -> withContext(IO) { feedsAndArticleDAO.getAllList() }
}
}
suspend fun instert(feed: Feed, article: Article) {
@ -59,13 +75,21 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
withContext(IO) {
try {
_isRefereshing.value = true
if (limit != null) {
updateFeed(limit!!)
} else {
val feeds = feedsAndArticleDAO.getFeedsList()
feeds?.forEach { feed ->
updateFeed(feed)
when (limit) {
is Feed -> {
updateFeed((limit as Feed))
}
is FeedGroup -> {
val feeds = feedsAndArticleDAO.getFeedsListByGroup((limit as FeedGroup).id)
feeds.forEach { feed ->
updateFeed(feed)
}
}
else -> {
val feeds = feedsAndArticleDAO.getFeedsList()
feeds.forEach { feed ->
updateFeed(feed)
}
}
}
_isRefereshing.value = false
@ -116,8 +140,15 @@ class FeedRepository(private val feedsAndArticleDAO: FeedsAndArticlesDAO) {
updateFeeds()
}
suspend fun setLimit(feed: Feed?) {
suspend fun getGroup(name: String) =
withContext(IO) { feedsAndArticleDAO.getFeedGroupByName(name) }
suspend fun setLimit(feed: Any?) {
limit = feed
_articleState.value = getAllArticles()
}
suspend fun addFeedGroup(feedGroup: FeedGroup) {
feedsAndArticleDAO.insertFeedGroup(feedGroup = feedGroup)
}
}

View file

@ -6,8 +6,9 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
@Database(entities = [Article::class, Feed::class], version = 4)
@Database(entities = [Article::class, Feed::class, FeedGroup::class], version = 7)
abstract class AppDatabase : RoomDatabase() {
abstract fun articleDao(): ArticleDAO
abstract fun feedsAndArticlesDao(): FeedsAndArticlesDAO

View file

@ -0,0 +1,16 @@
package eu.toldi.balazs.anotherfeedreader.sqlite
import androidx.room.Embedded
import androidx.room.Relation
import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
data class FeedAndGroups(
@Embedded
val feedGroup: FeedGroup,
@Relation(
parentColumn = "groupId",
entityColumn = "feedGroupId"
)
val feeds: List<Feed>
)

View file

@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData
import androidx.room.*
import eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.entities.FeedGroup
@Dao
interface FeedsAndArticlesDAO {
@ -16,18 +17,34 @@ interface FeedsAndArticlesDAO {
@Query("SELECT * FROM feed INNER JOIN article ON feed.feedId = article.containingFeedId ORDER BY article.pubDate DESC")
fun getAllList(): List<Article>
@Transaction
@Query("SELECT * FROM feedGroup INNER JOIN feed ON feedGroupId = feedGroup.id INNER JOIN article ON feed.feedId = article.containingFeedId WHERE feedGroupId = :id ORDER BY article.pubDate DESC")
fun getAllFromGroup(id: Long): List<Article>
@Query("SELECT * FROM feed")
fun getFeeds(): LiveData<List<Feed>>
@Query("SELECT * FROM feed")
fun getFeedsList(): List<Feed>
@Query("SELECT * FROM feed WHERE feedGroupId = :id")
fun getFeedsByGroup(id: Long?): LiveData<List<Feed>>
@Query("SELECT * FROM feed WHERE feedGroupId = :id")
fun getFeedsListByGroup(id: Long?): 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>
@Query("SELECT * FROM feedGroup")
fun getFeedGroups(): LiveData<List<FeedGroup>>
@Query("SELECT * FROM feedGroup WHERE groupName = :name")
fun getFeedGroupByName(name: String): FeedGroup
fun insertArticle(feed: Feed, article: Article) {
feed.feedId?.let { article.feedId = it }
_insertArticles(listOf(article))
@ -36,6 +53,9 @@ interface FeedsAndArticlesDAO {
@Insert
fun _insertArticles(articles: List<Article>)
@Insert
fun insertFeedGroup(feedGroup: FeedGroup)
@Update
fun updateFeed(feed: Feed)

View file

@ -7,6 +7,7 @@ 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.entities.FeedGroup
import eu.toldi.balazs.anotherfeedreader.repository.FeedRepository
import eu.toldi.balazs.anotherfeedreader.sqlite.AppDatabase
import kotlinx.coroutines.Dispatchers.IO
@ -18,6 +19,7 @@ class FeedViewModel(application: Application) : AndroidViewModel(application) {
private val feedRepo: FeedRepository
val articles: State<List<Article>>
val feeds: LiveData<List<Feed>>
val feedGroups: LiveData<List<FeedGroup>>
val isRefereshing: State<Boolean>
init {
@ -29,6 +31,7 @@ class FeedViewModel(application: Application) : AndroidViewModel(application) {
}
feeds = feedRepo.getFeedList()
feedGroups = feedRepo.getFeedGroups()
isRefereshing = feedRepo.isRefeshing
}
@ -50,7 +53,7 @@ class FeedViewModel(application: Application) : AndroidViewModel(application) {
}
}
fun setLimit(feed: Feed?) {
fun setLimit(feed: Any?) {
viewModelScope.launch {
feedRepo.setLimit(feed)
}
@ -69,4 +72,13 @@ class FeedViewModel(application: Application) : AndroidViewModel(application) {
}
fun addFeedGroup(feedGroup: FeedGroup) {
viewModelScope.launch(IO) {
feedRepo.addFeedGroup(feedGroup)
}
}
fun getFeedByGroup(id: Long?) = feedRepo.getFeedsByGroup(id)
}