diff --git a/app/build.gradle b/app/build.gradle index a0b390a..426ee8e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/MainActivity.kt b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/MainActivity.kt index e43e404..1667afa 100644 --- a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/MainActivity.kt +++ b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/MainActivity.kt @@ -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>) { + 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>, + 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() { } \ No newline at end of file diff --git a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Article.kt b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Article.kt index c0da49b..2a48387 100644 --- a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Article.kt +++ b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Article.kt @@ -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("","")) + 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) } } diff --git a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Feed.kt b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Feed.kt index 8bc23f9..b5e058e 100644 --- a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Feed.kt +++ b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/Feed.kt @@ -35,7 +35,7 @@ open class Feed /** * A hírcsatornához tartozó cikkek listája */ - var articleList: List
= ArrayList() + open var articleList: List
= ArrayList() private val _isRefreshing = MutableStateFlow(false) val isRefreshing: StateFlow @@ -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()) diff --git a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedContainer.kt b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedContainer.kt new file mode 100644 index 0000000..bc83711 --- /dev/null +++ b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedContainer.kt @@ -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> = _articles + + fun updateFeed(){ + innerFeed.updateFeed() + _articles.value = innerFeed.articleList.drop(0) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedData.kt b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedData.kt new file mode 100644 index 0000000..54f0d8a --- /dev/null +++ b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedData.kt @@ -0,0 +1,14 @@ +package eu.toldi.balazs.anotherfeedreader.entities + +class FeedData : FeedGroup("MainFrame") { + + var limit : Feed? = null + + + private var _articleList = emptyList
() + override var articleList: List
+ get() =limit?.articleList ?: _articleList + set(value) {_articleList = value.sortedByDescending { it.pubDate }} + + +} \ No newline at end of file diff --git a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedGroup.kt b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedGroup.kt index 4ac8d00..7aef1bf 100644 --- a/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedGroup.kt +++ b/app/src/main/java/eu/toldi/balazs/anotherfeedreader/entities/FeedGroup.kt @@ -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 = ArrayList() + private var _articleList = emptyList
() + override var articleList: List
+ get() = _articleList + set(value) { + _articleList = value.sortedByDescending { it.pubDate } + } + /** * Hírcsatorna hozzáadása a csoporthoz * @param feed hozzáadandó hírcsatorna diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7b3603e..c1bb171 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,4 +1,7 @@ AnotherFeedReader WebView + Feed reader + All feeds + Bookmarks \ No newline at end of file