summaryrefslogtreecommitdiff
path: root/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-22 08:08:58 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-22 08:08:58 +0000
commitd8ce4b3c0f383e844b125d2d3abd618715bb3c89 (patch)
treeb586ff9b42392f2c44645773c2a2f2aadb9db406 /app/src/main/java/org/terst/feedreader/ui/MainActivity.kt
Initial commit: Feeds, an Android RSS reader
Feed subscriptions (RSS 2.0 + Atom, hand-rolled parser), OPML import/export, WorkManager periodic fetch with conditional GET, Room-backed local storage (feeds/articles/filter rules), keyword mute/highlight filtering. Reader mode: on-device extraction via Mozilla's Readability.js (vendored from github.com/mozilla/readability, pinned to release 0.6.0) run in a hidden WebView, for feed articles whose provided content is too thin to be the real article body. Save: explicit action (from a feed article, or the Android share-sheet target for any URL) archives to Stash via POST /artifacts?sync=true, while a local on-device copy (Readability-extracted) is always kept so saved articles read offline regardless of Stash's reachability. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EZ7ikw2ukGJFTHE3bJS7zL
Diffstat (limited to 'app/src/main/java/org/terst/feedreader/ui/MainActivity.kt')
-rw-r--r--app/src/main/java/org/terst/feedreader/ui/MainActivity.kt79
1 files changed, 79 insertions, 0 deletions
diff --git a/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt b/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt
new file mode 100644
index 0000000..345bede
--- /dev/null
+++ b/app/src/main/java/org/terst/feedreader/ui/MainActivity.kt
@@ -0,0 +1,79 @@
+package org.terst.feedreader.ui
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.rememberNavController
+import androidx.navigation.navArgument
+import org.terst.feedreader.work.FeedFetchWorker
+import java.net.URLDecoder
+import java.net.URLEncoder
+
+const val UNREAD_FEED_ID = "__unread__"
+const val SAVED_FEED_ID = "__saved__"
+
+fun encodeNavArg(value: String): String = URLEncoder.encode(value, "UTF-8")
+fun decodeNavArg(value: String): String = URLDecoder.decode(value, "UTF-8")
+
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ FeedFetchWorker.schedulePeriodic(applicationContext)
+
+ setContent {
+ MaterialTheme {
+ Surface(modifier = Modifier.fillMaxSize()) {
+ FeedReaderApp()
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun FeedReaderApp() {
+ val navController = rememberNavController()
+ NavHost(navController = navController, startDestination = "feeds") {
+ composable("feeds") {
+ FeedListScreen(
+ onOpenFeed = { feedId -> navController.navigate("articles/${encodeNavArg(feedId)}") },
+ onOpenUnread = { navController.navigate("articles/${encodeNavArg(UNREAD_FEED_ID)}") },
+ onOpenSaved = { navController.navigate("articles/${encodeNavArg(SAVED_FEED_ID)}") },
+ onAddFeed = { navController.navigate("addFeed") },
+ onSettings = { navController.navigate("settings") }
+ )
+ }
+ composable(
+ "articles/{feedId}",
+ arguments = listOf(navArgument("feedId") { type = NavType.StringType })
+ ) { backStackEntry ->
+ val feedId = decodeNavArg(backStackEntry.arguments?.getString("feedId") ?: UNREAD_FEED_ID)
+ ArticleListScreen(
+ feedId = feedId,
+ onOpenArticle = { articleId -> navController.navigate("reader/${encodeNavArg(articleId)}") },
+ onBack = { navController.popBackStack() }
+ )
+ }
+ composable(
+ "reader/{articleId}",
+ arguments = listOf(navArgument("articleId") { type = NavType.StringType })
+ ) { backStackEntry ->
+ val articleId = decodeNavArg(backStackEntry.arguments?.getString("articleId") ?: "")
+ ReaderScreen(articleId = articleId, onBack = { navController.popBackStack() })
+ }
+ composable("addFeed") {
+ AddFeedScreen(onDone = { navController.popBackStack() })
+ }
+ composable("settings") {
+ SettingsScreen(onBack = { navController.popBackStack() })
+ }
+ }
+}