Added Basic feed reading
Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
parent
7f5cc49103
commit
e01c73b08a
8 changed files with 193 additions and 66 deletions
|
@ -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$" />
|
||||
|
|
20
.idea/inspectionProfiles/Project_Default.xml
Normal file
20
.idea/inspectionProfiles/Project_Default.xml
Normal 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>
|
|
@ -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
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -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'
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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!!
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue