Yarrand is a simple Android wrapper application that provides a native mobile experience for the Yarr RSS reader web application. The architecture is intentionally minimal, focusing on a single WebView with essential native features.
The app follows a simplified MVC pattern:
- Model:
YarrandPreferences- manages app settings and preferences - View: XML layouts and Android UI components
- Controller:
MainActivityandSettingsActivity- handle user interactions and coordinate between model and view
Purpose: Main screen that displays the Yarr web interface
Responsibilities:
- Initialize and configure WebView
- Handle navigation (back button)
- Manage toolbar and menu actions
- Implement feed refresh via Yarr API and status polling (no pull-to-refresh gesture)
- Monitor page loading progress
- Enforce security constraints (URL filtering)
Key Features:
YarrandWebViewClient: Custom WebViewClient to handle page loading and navigationYarrandWebChromeClient: Custom WebChromeClient to show progress and page titles- Back button override to navigate within SPA instead of closing app
Purpose: Configuration screen for app settings
Responsibilities:
- Display and edit server URL
- Toggle persistent authentication (Keep me logged in)
- Allow self-signed certificates (for localhost / trusted dev servers)
- Validate user input
- Persist settings
- Provide connection testing
Validation:
- URL must start with http:// or https://
- URL cannot be empty
- Trailing slashes are automatically removed
Purpose: Centralized preferences management
Responsibilities:
- Read/write app settings using SharedPreferences
- Provide type-safe access to configuration values
- Handle default values
Stored Settings:
server_url: The Yarr server URLdark_mode: Boolean flag for forced dark mode
User Action → Activity → YarrandPreferences → SharedPreferences
↓
WebView (loads Yarr)
- App launches → MainActivity.onCreate()
- Load preferences via YarrandPreferences
- Check if server URL is configured
- If not configured → Show SettingsActivity
- If configured → Load URL in WebView
- WebView renders Yarr interface
- Enable cookie persistence for session management
- User opens Settings from menu
- SettingsActivity loads current preferences
- User modifies settings
- Validation on save
- If valid → Save to SharedPreferences
- Return to MainActivity
- MainActivity checks if URL changed
- If changed → Reload WebView
- User presses back button
- MainActivity.onBackPressed() called
- Check if WebView.canGoBack()
- If true → WebView.goBack()
- If false → Exit app (super.onBackPressed())
javaScriptEnabled = true // Required for modern web apps
domStorageEnabled = true // LocalStorage support
databaseEnabled = true // IndexedDB supportcacheMode = WebSettings.LOAD_DEFAULT // Standard HTTP cacheallowFileAccess = false // Prevent file:// URLs
allowContentAccess = false // Prevent content:// URLs
mixedContentMode = MIXED_CONTENT_COMPATIBILITY_MODEFeed refresh is implemented by POSTing to /api/feeds/refresh and then polling /api/status. The app injects a small JavaScript helper to start the refresh and monitor status. When the running value reaches 0 the WebView reloads to display updated feeds.
The back navigation handler uses JavaScript injection to detect the SPA view (article / articles list / feeds) and programmatically clicks the appropriate in-page back button so the Android back gesture maps to in-app navigation.
CookieManager.setAcceptCookie(true)
CookieManager.setAcceptThirdPartyCookies(webView, true)Cookies are persisted to support the "Keep me logged in" setting. When enabled the app calls CookieManager.flush() during lifecycle pause to persist cookies; when disabled the app clears cookies on startup.
- Only allows navigation within the configured Yarr server
- External links are blocked and show a toast message
- Prevents unintended navigation to malicious sites
- HTTPS is recommended for production
- HTTP is allowed for local development/testing
- Cleartext traffic enabled in manifest for local servers
Additional behaviour:
- The app includes a setting to allow self-signed TLS certificates for localhost/trusted dev servers. This option is disabled by default and limited to
localhost,127.0.0.1or the configured server host. If disabled, WebView will cancel loads on SSL errors.
- No @JavascriptInterface methods exposed
- Prevents potential XSS attacks from web content
res/
├── layout/
│ ├── activity_main.xml # Main screen with WebView
│ └── activity_settings.xml # Settings screen
├── menu/
│ └── main_menu.xml # Toolbar menu items
├── values/
│ ├── strings.xml # All user-facing strings
│ ├── colors.xml # App color palette
│ └── themes.xml # Material Design theme
└── mipmap/ # App launcher icons
- Root project: Configuration and plugin versions
- app module: Application code and resources
- AndroidX: Core Android libraries
- Material Components: UI components following Material Design
- WebKit: Advanced WebView features
- SwipeRefreshLayout: Pull-to-refresh gesture
- Debug: No minification, debugging enabled
- Release: ProGuard enabled, optimized
YarrandPreferences- preferences management- URL validation logic
- Dark mode detection
- WebView initialization
- Navigation flow
- Settings persistence
- Settings screen interactions
- Menu actions
- Back button behavior
- Properly pause/resume WebView with activity lifecycle
- Destroy WebView in onDestroy to prevent memory leaks
- Horizontal progress bar for page loading
- Swipe refresh indicator
- Both automatically hidden when loading completes
- Single WebView instance per activity
- No WebView caching across activities
- Cookies persisted via CookieManager, not in-memory
- Service Worker Support: Enable for offline caching
- JavaScript Bridge: Add interface for native features (share, notifications)
- Multiple Profiles: Support multiple Yarr servers
- Background Sync: Periodic feed checking with notifications
- Custom User Agent: Identify as Yarrand for server-side customization
As features grow, consider:
- ViewModel + LiveData: For settings management
- Repository Pattern: For data access abstraction
- Dependency Injection: Using Hilt for testability
- Navigation Component: For multi-screen flows
- Language: Kotlin
- Naming: camelCase for variables/functions, PascalCase for classes
- Comments: Document non-obvious behavior
- String Resources: All user-facing text in strings.xml
- Null Safety: Leverage Kotlin's null safety features
MIT License - See LICENSE file for details