Added FeedGroups

Signed-off-by: Toldi Balázs Ádám <balazs@toldi.eu>
This commit is contained in:
Toldi Balázs Ádám 2021-10-03 21:26:49 +02:00
parent 3d57fe4526
commit ee16e5fe9b
5 changed files with 297 additions and 97 deletions

View 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>

View file

@ -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(

View file

@ -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")
}
}
}

View file

@ -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
}

View file

@ -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)
}
}