Added model, and made some design changes
This commit is contained in:
parent
62ed014799
commit
c213fd0f23
8 changed files with 263 additions and 112 deletions
|
@ -55,12 +55,13 @@ dependencies {
|
|||
implementation "androidx.compose.ui:ui:$compose_version"
|
||||
implementation "androidx.compose.material:material:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||
implementation("io.coil-kt:coil-compose:1.3.1")
|
||||
implementation "com.google.accompanist:accompanist-swiperefresh:0.19.0"
|
||||
implementation "androidx.compose.material:material-icons-extended:$compose_version"
|
||||
implementation("io.coil-kt:coil-compose:1.3.2")
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
|
||||
implementation 'androidx.activity:activity-compose:1.3.1'
|
||||
implementation "androidx.navigation:navigation-compose:2.4.0-alpha09"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
|
||||
implementation "com.google.accompanist:accompanist-swiperefresh:0.19.0"
|
||||
testImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
|
|
@ -3,16 +3,12 @@ package eu.toldi.balazs.anotherfeedreader
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
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.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
|
@ -20,15 +16,11 @@ 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
|
||||
|
@ -36,16 +28,25 @@ 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 kotlinx.coroutines.launch
|
||||
import java.net.URL
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.toldi.balazs.anotherfeedreader.entities.FeedData
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
val feed = FeedGroup("magyar").apply {
|
||||
addFeed(Feed(URL("https://hvg.hu/rss")))
|
||||
addFeed(Feed(URL("https://telex.hu/rss")))
|
||||
val feedData = FeedData().apply {
|
||||
addFeed(FeedGroup("magyar").apply {
|
||||
addFeed(Feed(URL("https://hvg.hu/rss")))
|
||||
addFeed(Feed(URL("https://telex.hu/rss")))
|
||||
})
|
||||
addFeed(Feed(URL("https://www.theguardian.com/world/rss")))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -55,53 +56,62 @@ class MainActivity : ComponentActivity() {
|
|||
// A surface container using the 'background' color from the theme
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
|
||||
feed.updateFeed()
|
||||
var articleList = remember { mutableStateOf(feed.articleList) }
|
||||
|
||||
feedData.updateFeed()
|
||||
val articleList = remember { mutableStateOf(feedData.articleList) }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Log.w(null, articleList.value.size.toString())
|
||||
Column {
|
||||
MenuBar()
|
||||
val isRefreshing by feed.isRefreshing.collectAsState()
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
SideBar(articleList, drawerState = drawerState) {
|
||||
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = {
|
||||
feed.updateFeed()
|
||||
articleList.value = feed.articleList
|
||||
},
|
||||
) {
|
||||
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!!)
|
||||
}
|
||||
|
||||
Column {
|
||||
MenuBar {
|
||||
scope.launch {
|
||||
drawerState.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
val isRefreshing by feedData.isRefreshing.collectAsState()
|
||||
|
||||
SwipeRefresh(
|
||||
state = rememberSwipeRefreshState(isRefreshing),
|
||||
onRefresh = {
|
||||
feedData.updateFeed()
|
||||
articleList.value = feedData.articleList
|
||||
},
|
||||
) {
|
||||
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 = feedData.name.toString()
|
||||
startActivity(
|
||||
Intent(
|
||||
baseContext,
|
||||
WebView::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
.padding(
|
||||
horizontal = 3.dp
|
||||
)
|
||||
) {
|
||||
showArticle(article = article)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,13 +119,13 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun MenuBar() {
|
||||
fun MenuBar(hamburgerClick: () -> Unit) {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text("Feed reader")
|
||||
Text(stringResource(id = R.string.app_name))
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { }) {
|
||||
IconButton(onClick = hamburgerClick) {
|
||||
Icon(
|
||||
Icons.Filled.Menu,
|
||||
contentDescription = "Menu Hamburger"
|
||||
|
@ -142,88 +152,143 @@ class MainActivity : ComponentActivity() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun showArticle(article: Article, feedName: String) {
|
||||
Row {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Row {
|
||||
if (article.author != null) {
|
||||
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)),
|
||||
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.imageURL != null)
|
||||
fun showArticle(article: Article) {
|
||||
Column {
|
||||
if (article.imageURL != null) {
|
||||
Image(
|
||||
painter = rememberImagePainter(article.imageURL),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
alignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.height(150.dp)
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
)
|
||||
}
|
||||
Row {
|
||||
if (article.author != null) {
|
||||
Text(
|
||||
article.author,
|
||||
fontSize = 11.sp
|
||||
)
|
||||
} else Text("")
|
||||
article.feedName?.let {
|
||||
Text(
|
||||
it,
|
||||
fontSize = 11.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = 10.dp),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FeedSideBar(feed: Feed, articleList: MutableState<List<Article>>) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
feed.name?.let {
|
||||
Text(
|
||||
text = it,
|
||||
fontSize = 26.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.clickable {
|
||||
feedData.limit = if (feedData.limit == feed) {
|
||||
null
|
||||
} else feed
|
||||
articleList.value = feedData.articleList
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SideBar(
|
||||
articleList: MutableState<List<Article>>,
|
||||
drawerState: DrawerState,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
|
||||
ModalDrawer(drawerState = drawerState, drawerContent = {
|
||||
LazyColumn {
|
||||
item {
|
||||
Text(text=stringResource(id = R.string.all_feeds),
|
||||
fontSize = 26.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.clickable {
|
||||
feedData.limit = null
|
||||
articleList.value = feedData.articleList
|
||||
})
|
||||
}
|
||||
items(feedData.feedList.size) { index ->
|
||||
FeedSideBar(feed = feedData.feedList[index], articleList)
|
||||
}
|
||||
}
|
||||
}, content = content)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun DefaultPreview() {
|
||||
AnotherFeedReaderTheme {
|
||||
Column() {
|
||||
MenuBar()
|
||||
MenuBar({})
|
||||
val article = Article(
|
||||
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(),
|
||||
imageURL = "https://cloud.toldi.eu/index.php/apps/files_sharing/publicpreview/Aicw77SAJYBiM8B?x=1920&y=571&a=true&file=P2.JPG&scalingup=0"
|
||||
imageURL = "https://cloud.toldi.eu/index.php/apps/files_sharing/publicpreview/Aicw77SAJYBiM8B?x=1920&y=571&a=true&file=P2.JPG&scalingup=0",
|
||||
feedName = "Teszt feed"
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = 1.dp)
|
||||
) {
|
||||
showArticle(article, "Test feed")
|
||||
showArticle(article)
|
||||
showArticle(
|
||||
Article(
|
||||
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)
|
||||
), "Test2 feed"
|
||||
pubDate = LocalDateTime.now().plusDays(-1),
|
||||
feedName = "Teszt feed 2"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FeedFragment : Fragment() {
|
||||
|
||||
}
|
|
@ -1,10 +1,23 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import org.w3c.dom.NodeList
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import javax.xml.parsers.DocumentBuilder
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import android.R.xml
|
||||
|
||||
import java.io.StringReader
|
||||
|
||||
import org.xml.sax.InputSource
|
||||
import java.net.URL
|
||||
|
||||
|
||||
class Article(
|
||||
|
@ -29,7 +42,9 @@ class Article(
|
|||
*/
|
||||
val author: String? = null,
|
||||
|
||||
val pubDate: LocalDateTime? = null
|
||||
val pubDate: LocalDateTime? = null,
|
||||
|
||||
val feedName: String? = null
|
||||
) {
|
||||
|
||||
/**
|
||||
|
@ -52,7 +67,7 @@ class Article(
|
|||
* Cikk beolvasása egy megadott Node objektumból. Egy hírcsatorna beolvasásakor könnyedén alkalmazható egy "item" Node-ra
|
||||
* @param article Egy "item" Node egy RSS hírcsatornáról
|
||||
*/
|
||||
fun createFromNode(article: Node) : Article{
|
||||
fun createFromNode(article: Node,feedName: String?) : Article{
|
||||
val properties: NodeList = article.getChildNodes()
|
||||
var title: String? = null
|
||||
var url: String? = null
|
||||
|
@ -85,7 +100,32 @@ class Article(
|
|||
}
|
||||
}
|
||||
}
|
||||
return Article(title, url, description, imageURL, author, pubDate)
|
||||
if(imageURL == null && url != null) {
|
||||
/* GlobalScope.launch {
|
||||
|
||||
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
|
||||
val builder: DocumentBuilder = factory.newDocumentBuilder()
|
||||
var doc: Document
|
||||
withContext(Dispatchers.IO) {
|
||||
val ips = InputSource()
|
||||
ips.characterStream = StringReader(URL(url).readText().replace("<!doctype html>","<?xml version=\"1.0\" encoding=\"utf-8\"?>"))
|
||||
doc = builder.parse(ips)
|
||||
}
|
||||
|
||||
val metas: NodeList = doc.getElementsByTagName("meta")
|
||||
|
||||
for (i in 0 until metas.length) {
|
||||
val meta: Node = metas.item(i)
|
||||
val property = meta.attributes.getNamedItem("property")
|
||||
if (property != null && property.textContent == "og:image"){
|
||||
val imageProperty = meta.attributes.getNamedItem("content")
|
||||
if(imageProperty != null)
|
||||
imageURL = imageProperty.textContent
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
return Article(title, url, description, imageURL, author, pubDate,feedName)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ open class Feed
|
|||
/**
|
||||
* A hírcsatornához tartozó cikkek listája
|
||||
*/
|
||||
var articleList: List<Article> = ArrayList()
|
||||
open var articleList: List<Article> = ArrayList()
|
||||
private val _isRefreshing = MutableStateFlow(false)
|
||||
|
||||
val isRefreshing: StateFlow<Boolean>
|
||||
|
@ -79,7 +79,7 @@ open class Feed
|
|||
|
||||
for (i in 0 until items.length) {
|
||||
val article: Node = items.item(i)
|
||||
val a = Article.createFromNode(article)
|
||||
val a = Article.createFromNode(article,name)
|
||||
addArticle(a)
|
||||
}
|
||||
Log.e(null, articleCount.toString())
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import java.net.URL
|
||||
|
||||
class FeedContainer(feedURL : URL) {
|
||||
|
||||
private val innerFeed = Feed(feedURL)
|
||||
|
||||
|
||||
private val _articles = MutableLiveData(innerFeed.articleList.drop(0))
|
||||
val articles : LiveData<List<Article>> = _articles
|
||||
|
||||
fun updateFeed(){
|
||||
innerFeed.updateFeed()
|
||||
_articles.value = innerFeed.articleList.drop(0)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
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 ?: _articleList
|
||||
set(value) {_articleList = value.sortedByDescending { it.pubDate }}
|
||||
|
||||
|
||||
}
|
|
@ -1,16 +1,24 @@
|
|||
package eu.toldi.balazs.anotherfeedreader.entities
|
||||
|
||||
import android.util.Log
|
||||
import java.util.ArrayList
|
||||
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
class FeedGroup(name: String) : Feed(name) {
|
||||
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
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name">AnotherFeedReader</string>
|
||||
<string name="title_activity_web_view">WebView</string>
|
||||
<string name="feed_name">Feed reader</string>
|
||||
<string name="all_feeds">All feeds</string>
|
||||
<string name="bookmarks">Bookmarks</string>
|
||||
</resources>
|
Loading…
Reference in a new issue