Added more design elements and fixed some bugs
All checks were successful
continuous-integration/drone/push Build is passing

Signed-off-by: Balazs Toldi <balazs@toldi.eu>
This commit is contained in:
Balazs Toldi 2021-10-06 23:28:20 +02:00
parent 22cd651405
commit 1770781655
Signed by: Bazsalanszky
GPG key ID: 933820884952BE27
4 changed files with 83 additions and 48 deletions

View file

@ -62,6 +62,7 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
implementation "com.google.accompanist:accompanist-swiperefresh:0.19.0"
implementation "org.jsoup:jsoup:1.13.1"
implementation "net.mm2d.touchicon:touchicon:0.9.1"
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View file

@ -2,7 +2,6 @@ package eu.toldi.balazs.anotherfeedreader
import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
@ -10,6 +9,7 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
@ -18,10 +18,12 @@ import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@ -62,11 +64,10 @@ class MainActivity : ComponentActivity() {
Surface(color = MaterialTheme.colors.background) {
feedData.updateFeed()
val articleList = remember { mutableStateOf(feedData.articleList) }
val scope = rememberCoroutineScope()
Log.w(null, articleList.value.size.toString())
val scope = rememberCoroutineScope()
val drawerState = rememberDrawerState(DrawerValue.Closed)
val articleList = remember { mutableStateOf(feedData.articleList) }
SideBar(articleList, drawerState = drawerState) {
@ -143,7 +144,7 @@ class MainActivity : ComponentActivity() {
val haptic = LocalHapticFeedback.current
Card(
Modifier
.pointerInput(Unit) {
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
val share =
@ -164,51 +165,58 @@ class MainActivity : ComponentActivity() {
}
)
}
.padding(
start = 6.dp,
top = 2.dp,
bottom = 6.dp,
end = 6.dp
)
.clickable {
WebView.webURL = article.url.toString()
WebView.barTitle =
article.feedName.toString()
article.feed.name.toString()
startActivity(
Intent(
baseContext,
WebView::class.java
)
)
}
.padding(
start = 6.dp,
top = 2.dp,
bottom = 6.dp,
end = 6.dp
),
},
elevation = 10.dp,
backgroundColor = MaterialTheme.colors.surface
) {
Column {
if (article.imageURL != null) {
Row(
modifier = Modifier.padding(horizontal = 3.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
val painter = if (article.feed.faviconURL != null) {
rememberImagePainter(article.feed.faviconURL)
} else {
painterResource(id = R.drawable.ic_launcher_background)
}
Image(
painter = rememberImagePainter(article.imageURL),
painter = painter,
contentDescription = null,
contentScale = ContentScale.Crop,
alignment = Alignment.Center,
modifier = Modifier
.height(200.dp)
.fillMaxWidth()
.fillMaxHeight()
.size(30.dp)
.clip(CircleShape)
)
}
Row {
if (article.author != null) {
article.author?.let {
Text(
text = (fun(): String {
if (article.author.length > 25)
return article.author.dropLast(article.author.length - 25)
else return article.author
if (it.length > 25)
return it.dropLast(it.length - 25)
else return it
}).invoke(),
fontSize = 11.sp
fontSize = 11.sp,
modifier = Modifier.padding(horizontal = 2.dp)
)
} else Text("")
article.feedName?.let {
}
article.feed.name?.let {
Text(
text = (fun(): String {
return if (it.length > 25)
@ -222,14 +230,26 @@ class MainActivity : ComponentActivity() {
overflow = TextOverflow.Ellipsis
)
}
if (article.pubDate != null) {
article.pubDate?.let {
Text(
article.pubDate.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)),
it.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)),
fontSize = 11.sp,
textAlign = TextAlign.Right,
//modifier = Modifier.width(350.dp)
)
} else Text("")
}
}
if (article.imageURL != null) {
Image(
painter = rememberImagePainter(article.imageURL),
contentDescription = null,
contentScale = ContentScale.Crop,
alignment = Alignment.Center,
modifier = Modifier
.height(200.dp)
.fillMaxWidth()
.fillMaxHeight()
)
}
if (article.title != null) {
Text(
@ -308,7 +328,7 @@ class MainActivity : ComponentActivity() {
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(),
feedName = "Teszt feed"
feed = Feed("Test 1")
)
Column(
modifier = Modifier.padding(horizontal = 1.dp)
@ -320,8 +340,8 @@ class MainActivity : ComponentActivity() {
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),
feedName = "Teszt feed 2"
),
feed = Feed("Test 1")
)
)
}

View file

@ -33,7 +33,7 @@ class Article(
val pubDate: LocalDateTime? = null,
val feedName: String? = null
val feed: Feed
) {
/**
@ -56,7 +56,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, feedName: String?): Article {
fun createFromNode(article: Node, feed: Feed): Article {
val properties: NodeList = article.getChildNodes()
var title: String? = null
var url: String? = null
@ -105,7 +105,7 @@ class Article(
}
}
}
return Article(title, url, description, imageURL, author, pubDate, feedName)
return Article(title, url, description, imageURL, author, pubDate, feed)
}
}

View file

@ -1,12 +1,11 @@
package eu.toldi.balazs.anotherfeedreader.entities
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import android.util.Log
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.mm2d.touchicon.TouchIconExtractor
import org.w3c.dom.Document
import org.w3c.dom.Node
import org.w3c.dom.NodeList
@ -23,14 +22,17 @@ open class Feed
val link: URL
) {
var faviconURL: String? = null
/**
* A hírcsatorna neve
*/
var name: String? = null
constructor(name : String) : this(URL("http://example.com")) {
constructor(name: String) : this(URL("http://example.com")) {
this.name = name
}
/**
* A hírcsatornához tartozó cikkek listája
*/
@ -39,6 +41,7 @@ open class Feed
val isRefreshing: StateFlow<Boolean>
get() = _isRefreshing.asStateFlow()
init {
updateFeed()
}
@ -69,19 +72,30 @@ open class Feed
_isRefreshing.emit(true)
val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance()
val builder: DocumentBuilder = factory.newDocumentBuilder()
var doc: Document
withContext(Dispatchers.IO) {
doc = builder.parse(link.toString())
var doc: Document = withContext(Dispatchers.IO) {
try {
builder.parse(link.toString())
} catch (e: Exception) {
Log.e(null, e.stackTraceToString())
builder.newDocument()
}
}
name = doc.getElementsByTagName("title").item(0).getTextContent()
if (doc.getElementsByTagName("title").length > 0)
name = doc.getElementsByTagName("title").item(0).getTextContent()
val items: NodeList = doc.getElementsByTagName("item")
for (i in 0 until items.length) {
val article: Node = items.item(i)
val a = Article.createFromNode(article,name)
val a = Article.createFromNode(article, this@Feed)
addArticle(a)
}
articleList.sortedByDescending { it.pubDate }
faviconURL = withContext(Dispatchers.IO) {
val extractor = TouchIconExtractor()
extractor.fromPage("https://" + link.host)[0].url
}
Log.e(null, faviconURL.toString())
_isRefreshing.emit(false)
}
}