Added FeedGroups
Signed-off-by: Toldi Balázs Ádám <balazs@toldi.eu>
This commit is contained in:
parent
3d57fe4526
commit
ee16e5fe9b
5 changed files with 297 additions and 97 deletions
17
.idea/deploymentTargetDropDown.xml
Normal file
17
.idea/deploymentTargetDropDown.xml
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<targetSelectedWithDropDown>
|
||||
<Target>
|
||||
<type value="QUICK_BOOT_TARGET" />
|
||||
<deviceKey>
|
||||
<Key>
|
||||
<type value="VIRTUAL_DEVICE_PATH" />
|
||||
<value value="$USER_HOME$/.android/avd/Pixel_3a_XL_API_30.avd" />
|
||||
</Key>
|
||||
</deviceKey>
|
||||
</Target>
|
||||
</targetSelectedWithDropDown>
|
||||
<timeTargetWasSelectedWithDropDown value="2021-10-02T16:31:04.808303Z" />
|
||||
</component>
|
||||
</project>
|
|
@ -6,13 +6,11 @@ import android.util.Log
|
|||
import android.widget.ScrollView
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.scrollable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.*
|
||||
|
@ -22,14 +20,21 @@ import androidx.compose.material.icons.filled.Search
|
|||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.fragment.app.Fragment
|
||||
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.FeedGroup
|
||||
import eu.toldi.balazs.anotherfeedreader.ui.theme.AnotherFeedReaderTheme
|
||||
import java.net.URL
|
||||
import java.time.LocalDateTime
|
||||
|
@ -38,7 +43,10 @@ import java.time.format.FormatStyle
|
|||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
val feed = Feed(URL("https://hvg.hu/rss"))
|
||||
val feed = FeedGroup("magyar").apply {
|
||||
addFeed(Feed(URL("https://hvg.hu/rss")))
|
||||
addFeed(Feed(URL("https://telex.hu/rss")))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -47,34 +55,50 @@ class MainActivity : ComponentActivity() {
|
|||
// A surface container using the 'background' color from the theme
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
|
||||
var articleCount = remember { mutableStateOf(feed.articleCount) }
|
||||
Log.w(null,articleCount.toString())
|
||||
feed.updateFeed()
|
||||
var articleList = remember { mutableStateOf(feed.articleList) }
|
||||
|
||||
|
||||
Log.w(null, articleList.value.size.toString())
|
||||
Column {
|
||||
MenuBar()
|
||||
val scrollState = rememberScrollState()
|
||||
LazyColumn(
|
||||
modifier = Modifier.scrollable(
|
||||
state = scrollState,
|
||||
orientation = Orientation.Vertical
|
||||
)
|
||||
val isRefreshing by feed.isRefreshing.collectAsState()
|
||||
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = {
|
||||
feed.updateFeed()
|
||||
articleList.value = feed.articleList
|
||||
},
|
||||
) {
|
||||
items(articleCount.value) { index ->
|
||||
Column(
|
||||
Modifier
|
||||
.clickable {
|
||||
Log.i(null, feed[index].title.toString())
|
||||
WebView.webURL = feed[index].url.toString()
|
||||
WebView.barTitle = feed.name.toString()
|
||||
startActivity(Intent(baseContext, WebView::class.java))
|
||||
}
|
||||
.padding(
|
||||
horizontal = 3.dp
|
||||
)
|
||||
) {
|
||||
showArticle(article = feed[index], feed.name!!)
|
||||
}
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
|
||||
) {
|
||||
items(articleList.value.size) { index ->
|
||||
val article = articleList.value[index]
|
||||
Column(
|
||||
|
||||
Modifier
|
||||
.clickable {
|
||||
Log.i(null, article.title.toString())
|
||||
WebView.webURL = article.url.toString()
|
||||
WebView.barTitle = feed.name.toString()
|
||||
startActivity(
|
||||
Intent(
|
||||
baseContext,
|
||||
WebView::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
.padding(
|
||||
horizontal = 3.dp
|
||||
)
|
||||
) {
|
||||
showArticle(article = article, feed.name!!)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,44 +143,52 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
@Composable
|
||||
fun showArticle(article: Article, feedName: String) {
|
||||
Column(modifier = Modifier.padding(all = 5.dp)) {
|
||||
Row {
|
||||
if (article.author != null) {
|
||||
Row {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Row {
|
||||
if (article.author != null) {
|
||||
Text(
|
||||
article.author,
|
||||
fontSize = 11.sp
|
||||
)
|
||||
} else Text("")
|
||||
Text(
|
||||
article.author,
|
||||
fontSize = 11.sp
|
||||
)
|
||||
} else Text("")
|
||||
Text(
|
||||
feedName,
|
||||
fontSize = 11.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = 10.dp)
|
||||
)
|
||||
if (article.pubDate != null) {
|
||||
Text(
|
||||
article.pubDate.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)),
|
||||
feedName,
|
||||
fontSize = 11.sp,
|
||||
textAlign = TextAlign.Right,
|
||||
modifier = Modifier.width(400.dp)
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = 10.dp)
|
||||
)
|
||||
if (article.pubDate != null) {
|
||||
Text(
|
||||
article.pubDate.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)),
|
||||
fontSize = 11.sp,
|
||||
textAlign = TextAlign.Right,
|
||||
//modifier = Modifier.width(350.dp)
|
||||
)
|
||||
} else Text("")
|
||||
}
|
||||
if (article.title != null) {
|
||||
Text(
|
||||
text = article.title.trim(),
|
||||
fontSize= 20.sp,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
} else Text("")
|
||||
if (article.description != null) {
|
||||
Text(
|
||||
text = article.description,
|
||||
fontSize = 17.sp,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
if (article.title != null) {
|
||||
Text(
|
||||
text = article.title.trim(),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
if (article.imageURL != null)
|
||||
Image(
|
||||
painter = rememberImagePainter(article.imageURL),
|
||||
contentDescription = null,
|
||||
)
|
||||
} else Text("")
|
||||
if (article.description != null) {
|
||||
Text(
|
||||
text = article.description,
|
||||
fontSize = 12.sp,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,10 +202,11 @@ class MainActivity : ComponentActivity() {
|
|||
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 = LocalDateTime.now(),
|
||||
imageURL = "https://cloud.toldi.eu/index.php/apps/files_sharing/publicpreview/Aicw77SAJYBiM8B?x=1920&y=571&a=true&file=P2.JPG&scalingup=0"
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding( horizontal = 1.dp)
|
||||
modifier = Modifier.padding(horizontal = 1.dp)
|
||||
) {
|
||||
showArticle(article, "Test feed")
|
||||
showArticle(
|
||||
|
|
|
@ -5,10 +5,9 @@ import android.webkit.WebView
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.OpenInBrowser
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
|
@ -17,6 +16,14 @@ import eu.toldi.balazs.anotherfeedreader.WebView.Companion.webURL
|
|||
import eu.toldi.balazs.anotherfeedreader.ui.theme.AnotherFeedReaderTheme
|
||||
import eu.toldi.balazs.anotherfeedreader.views.CustomWebViewClient
|
||||
|
||||
import android.net.Uri
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.material.icons.filled.ArrowLeft
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
|
||||
|
||||
class WebView : ComponentActivity() {
|
||||
companion object {
|
||||
var webURL = "http://example.rog"
|
||||
|
@ -25,44 +32,70 @@ class WebView : ComponentActivity() {
|
|||
|
||||
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) {
|
||||
Column() {
|
||||
TopBar(topTitle = barTitle)
|
||||
ShowWebView("Android")
|
||||
ShowWebView("Android")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun TopBar(topTitle: String) {
|
||||
TopAppBar (
|
||||
title = { Text(text = topTitle) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShowWebView(name: String) {
|
||||
AndroidView(factory = { ctx ->
|
||||
WebView(ctx).apply {
|
||||
loadUrl(webURL)
|
||||
webViewClient = CustomWebViewClient()
|
||||
}
|
||||
})
|
||||
}
|
||||
@Composable
|
||||
fun TopBar(topTitle: String) {
|
||||
|
||||
TopAppBar(
|
||||
title = { Text(text = topTitle) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
finish()
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.ArrowLeft,
|
||||
contentDescription = "Back to feed"
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(webURL))
|
||||
startActivity(browserIntent)
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Filled.OpenInBrowser,
|
||||
contentDescription = "Open in browser"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShowWebView(name: String) {
|
||||
AndroidView(factory = { ctx ->
|
||||
WebView(ctx).apply {
|
||||
loadUrl(webURL)
|
||||
webViewClient = CustomWebViewClient()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
AnotherFeedReaderTheme {
|
||||
Column {
|
||||
TopBar("WebView")
|
||||
ShowWebView("Android")
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
AnotherFeedReaderTheme {
|
||||
Column {
|
||||
TopBar("WebView")
|
||||
ShowWebView("Android")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@ package eu.toldi.balazs.anotherfeedreader.entities
|
|||
import android.util.Log
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.w3c.dom.Document
|
||||
|
@ -13,7 +16,7 @@ import javax.xml.parsers.DocumentBuilder
|
|||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
|
||||
class Feed
|
||||
open class Feed
|
||||
(
|
||||
/**
|
||||
* A hírcsatorna elérési útvonala
|
||||
|
@ -26,12 +29,17 @@ class Feed
|
|||
* A hírcsatorna neve
|
||||
*/
|
||||
var name: String? = null
|
||||
|
||||
constructor(name : String) : this(URL("http://example.com")) {
|
||||
this.name = name
|
||||
}
|
||||
/**
|
||||
* A hírcsatornához tartozó cikkek listája
|
||||
*/
|
||||
val articleList: MutableList<Article> = ArrayList()
|
||||
var articleList: List<Article> = ArrayList()
|
||||
private val _isRefreshing = MutableStateFlow(false)
|
||||
|
||||
val isRefreshing: StateFlow<Boolean>
|
||||
get() = _isRefreshing.asStateFlow()
|
||||
init {
|
||||
updateFeed()
|
||||
}
|
||||
|
@ -40,7 +48,7 @@ class Feed
|
|||
* A hírcsatornán található cikkek számának lekérése
|
||||
* @return a hírcsatorna található cikkek száma
|
||||
*/
|
||||
val articleCount: Int
|
||||
open val articleCount: Int
|
||||
get() = articleList.size
|
||||
|
||||
/**
|
||||
|
@ -57,8 +65,9 @@ class Feed
|
|||
* Lekéri a hírcsatorna elérési útvonalán található összes új cikket és hozzáadja a cikkek listájához.
|
||||
*
|
||||
*/
|
||||
fun updateFeed() {
|
||||
open fun updateFeed() {
|
||||
GlobalScope.launch {
|
||||
_isRefreshing.emit(true)
|
||||
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
|
||||
val builder: DocumentBuilder = factory.newDocumentBuilder()
|
||||
var doc: Document
|
||||
|
@ -74,6 +83,7 @@ class Feed
|
|||
addArticle(a)
|
||||
}
|
||||
Log.e(null, articleCount.toString())
|
||||
_isRefreshing.emit(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +106,7 @@ class Feed
|
|||
* Megjegyzés: cikket csak akkor veszi fel,ha az még nem található a listájában
|
||||
*/
|
||||
fun addArticle(a: Article) {
|
||||
if (!hasAricle(a)) articleList.add(a)
|
||||
if (!hasAricle(a)) articleList += a
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class FeedGroup(name: String) : Feed(name) {
|
||||
/**
|
||||
* A hírcsatornák listája
|
||||
*/
|
||||
var feedList: MutableList<Feed> = ArrayList()
|
||||
|
||||
/**
|
||||
* 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)
|
||||
for (i in 0 until feed.articleCount) {
|
||||
if (!hasAricle(feed[i]))
|
||||
articleList += feed[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.updateFeed
|
||||
* @throws IOException
|
||||
*/
|
||||
override fun updateFeed() {
|
||||
if(feedList == null) feedList = ArrayList()
|
||||
if (articleList == null) articleList = ArrayList()
|
||||
for (f in feedList) {
|
||||
f.updateFeed()
|
||||
for (i in 0 until f.articleCount) {
|
||||
if (!hasAricle(f[i]))
|
||||
articleList += f[i]
|
||||
}
|
||||
}
|
||||
articleList = articleList.sortedByDescending { it.pubDate }
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue