Added Basic feed reading

Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
Balazs Toldi 2021-09-13 14:14:51 +02:00
parent 7f5cc49103
commit e01c73b08a
Signed by: Bazsalanszky
GPG key ID: 933820884952BE27
8 changed files with 193 additions and 66 deletions

View file

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="Embedded JDK" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View file

@ -0,0 +1,20 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

View file

@ -4,10 +4,16 @@
<option name="filePathToZoomLevelMap">
<map>
<entry key="../../../../../layout/compose-model-1631186770589.xml" value="0.30277777777777776" />
<entry key="../../../../../layout/compose-model-1631262917527.xml" value="0.3108108108108108" />
<entry key="../../../../../layout/compose-model-1631263525457.xml" value="1.0" />
<entry key="../../../../../layout/compose-model-1631273263532.xml" value="0.6462962962962963" />
<entry key="../../../../../layout/compose-model-1631532588435.xml" value="0.5888888888888889" />
<entry key="../../../../../layout/compose-model-1631535061700.xml" value="0.5851851851851851" />
<entry key="app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" value="0.21458333333333332" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View file

@ -57,6 +57,7 @@ dependencies {
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View file

@ -3,68 +3,159 @@ package eu.toldi.balazs.anotherfeedreader
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
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.lazy.LazyColumn
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
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 eu.toldi.balazs.anotherfeedreader.entities.Article
import eu.toldi.balazs.anotherfeedreader.entities.Feed
import eu.toldi.balazs.anotherfeedreader.ui.theme.AnotherFeedReaderTheme
import androidx.compose.material.Icon as Icon
import java.net.URL
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
class MainActivity : ComponentActivity() {
val feed = Feed(URL("https://hvg.hu/rss"))
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) {
MenuBar()
var articleCount by remember { mutableStateOf(feed.articleCount) }
Column {
MenuBar()
LazyColumn {
items(articleCount) { index ->
showArticle(article = feed.get(index), feed.name!!)
}
}
}
}
}
}
}
}
@Composable
fun MenuBar() {
TopAppBar(
title = {
Text("Feed reader")
},
navigationIcon = {
IconButton(onClick = { }) {
Icon(
Icons.Filled.Menu,
contentDescription = "Menu Hamburger"
@Composable
fun MenuBar() {
TopAppBar(
title = {
Text("Feed reader")
},
navigationIcon = {
IconButton(onClick = { }) {
Icon(
Icons.Filled.Menu,
contentDescription = "Menu Hamburger"
)
}
},
actions = {
Row {
IconButton(onClick = {}) {
Icon(
Icons.Filled.Search,
contentDescription = "Search Article"
)
}
IconButton(onClick = {}) {
Icon(
Icons.Filled.Settings,
contentDescription = "Settings"
)
}
}
}
)
}
@Composable
fun showArticle(article: Article, feedName: String) {
Column(modifier = Modifier.padding(top = 5.dp, bottom = 5.dp)) {
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(400.dp)
)
} else Text("")
}
if (article.title != null) {
Text(
text = article.title
)
} else Text("")
if (article.description != null) {
Text(
text = article.description,
fontSize = 12.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
},
actions = {
Row {
IconButton(onClick = {}) {
Icon(
Icons.Filled.Search,
contentDescription = "Search Article"
)
}
IconButton(onClick = {}) {
Icon(
Icons.Filled.Settings,
contentDescription = "Settings"
)
}
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
AnotherFeedReaderTheme {
Column() {
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()
)
showArticle(article, "Test feed")
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"
)
}
}
)
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
AnotherFeedReaderTheme {
MenuBar()
}
class FeedFragment : Fragment() {
}

View file

@ -27,13 +27,15 @@ class Article(
/**
* A cikk szerzője
*/
val author: String? = null
val author: String? = null,
val pubDate: LocalDateTime? = null
) {
/**
* A cikk kiadásának dátuma
*/
private var pubDate: LocalDateTime? = null
override fun toString(): String {
return "Article{" +
"title='" + title + '\'' +
@ -57,6 +59,7 @@ class Article(
var description: String? = null
var imageURL: String? = null
var author: String? = null
var pubDate: LocalDateTime? = null
for (j in 0 until properties.getLength()) {
val node: Node = properties.item(j)
val tagName: String = node.getNodeName()
@ -64,7 +67,7 @@ class Article(
when (tagName) {
"title" -> title = node.getTextContent()
"author", "dc:creator" -> author = node.getTextContent()
"pubDate" -> LocalDateTime.parse(
"pubDate" -> pubDate = LocalDateTime.parse(
node.textContent,
DateTimeFormatter.RFC_1123_DATE_TIME
)
@ -82,7 +85,7 @@ class Article(
}
}
}
return Article(title,url,description,imageURL,author)
return Article(title, url, description, imageURL, author, pubDate)
}
}

View file

@ -1,34 +1,27 @@
package eu.toldi.balazs.anotherfeedreader.entities
import android.util.Log
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.Node
import org.w3c.dom.NodeList
import java.lang.NullPointerException
import java.net.URL
import java.net.URLConnection
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
class Feed
/**
* Hírcsatorna lézrehozása elérési útvonal alapján
* @param url a hírcsatorna elérési útvonala
* @throws IOException
*/(url: URL?) {
(
/**
* A hírcsatorna elérési útvonala
*/
private var link: URL? = url
/**
* A hírcsatorna nevének lekérése
*
* @return a hírcsatorna neve
*/
/**
* Hírcsatorna nevének beállítása
* @param name a hírcsatorna új neve
*/
val link: URL
) {
/**
* A hírcsatorna neve
*/
@ -37,10 +30,9 @@ class Feed
/**
* A hírcsatornához tartozó cikkek listája
*/
protected var articleList: MutableList<Article> = ArrayList()
val articleList: MutableList<Article> = ArrayList()
init {
articleList = ArrayList()
updateFeed()
}
@ -66,17 +58,23 @@ class Feed
*
*/
fun updateFeed() {
val con: URLConnection = link?.openConnection() ?: throw NullPointerException("URL cannot be null")
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
GlobalScope.launch {
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
val builder: DocumentBuilder = factory.newDocumentBuilder()
val doc: Document = builder.parse(con.getInputStream())
var doc: Document
withContext(Dispatchers.IO) {
doc = builder.parse(link.toString())
}
name = doc.getElementsByTagName("title").item(0).getTextContent()
val items: NodeList = doc.getElementsByTagName("item")
for (i in 0 until items.getLength()) {
for (i in 0 until items.length) {
val article: Node = items.item(i)
val a = Article.createFromNode(article)
addArticle(a)
}
Log.e(null, articleCount.toString())
}
}
/**
@ -105,4 +103,4 @@ class Feed
override fun toString(): String {
return name!!
}
}
}