package org.terst.feedreader.ui import android.annotation.SuppressLint import android.content.Intent import android.net.Uri import android.webkit.CookieManager import android.webkit.WebChromeClient import android.webkit.WebView import android.webkit.WebViewClient import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.OpenInBrowser import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.viewinterop.AndroidView /** * A real, interactive mini-browser -- JS and DOM storage on, ordinary navigation, no * Readability interception. Unlike LinkReaderScreen (always tries to extract a clean * article view), this is for the two cases where the user needs to actually USE a * page rather than just read it: browsing search results to find an alternate, * non-paywalled source for a story, or logging into a site they already subscribe to. * * Login needs no extra plumbing beyond turning cookies on: Android's CookieManager is * a single process-wide store shared by every WebView in the app, so once a session * cookie is set here, the reader's (JS-off) extraction WebView picks it up automatically * on the next visit to that domain -- same as a normal browser remembering you're signed in. */ @SuppressLint("SetJavaScriptEnabled") @OptIn(ExperimentalMaterial3Api::class) @Composable fun BrowserScreen(startUrl: String, onBack: () -> Unit) { val context = LocalContext.current var title by remember(startUrl) { mutableStateOf(null) } var webViewRef by remember(startUrl) { mutableStateOf(null) } // Deliberately NOT "goBack() if the WebView has history, else exit" -- that means // however many pages deep a search/login session went (search results -> article -> // another link -> ...), leaving requires tapping back that many times first. Reported // live as not making sense for this kind of multi-page browsing. Back always just // closes the screen now, both the system gesture and the in-app arrow; there's no // separate in-page-back affordance, matching how every other screen in this app behaves. BackHandler(onBack = onBack) Scaffold( topBar = { TopAppBar( title = { Text(title ?: "Browser", maxLines = 1, overflow = TextOverflow.Ellipsis) }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Default.ArrowBack, contentDescription = "Close browser") } }, actions = { IconButton(onClick = { val url = webViewRef?.url ?: startUrl context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) }) { Icon(Icons.Default.OpenInBrowser, contentDescription = "Open in browser") } } ) } ) { padding -> Box(modifier = Modifier.fillMaxSize().padding(padding)) { AndroidView( modifier = Modifier.fillMaxSize(), factory = { ctx -> WebView(ctx).apply { settings.javaScriptEnabled = true settings.domStorageEnabled = true CookieManager.getInstance().setAcceptCookie(true) CookieManager.getInstance().setAcceptThirdPartyCookies(this, true) webViewClient = object : WebViewClient() { override fun onPageFinished(view: WebView, url: String) { title = view.title } } webChromeClient = WebChromeClient() loadUrl(startUrl) webViewRef = this } }, onRelease = { webView -> // Flush explicitly so a just-completed login survives even if the // process is killed shortly after (modern WebView auto-persists, but // this makes it immediate rather than relying on that timing). CookieManager.getInstance().flush() webView.destroy() } ) } } }