Files
Private_Amazon/app/src/main/java/com/example/privateamazon/MainActivity.kt
T
2026-02-17 18:50:48 +00:00

155 lines
6.0 KiB
Kotlin

package com.example.privateamazon
import android.net.http.SslError
import android.os.Bundle
import android.view.View
import android.webkit.CookieManager
import android.webkit.SslErrorHandler
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebSettings
import android.webkit.WebStorage
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewFeature
import java.io.ByteArrayInputStream
class MainActivity : ComponentActivity() {
private lateinit var webView: WebView
private val blockPatterns = mutableListOf<Regex>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 1. Fix Status Bar Overlay (Push app below the clock/battery)
val mainContainer = findViewById<View>(R.id.main_container)
ViewCompat.setOnApplyWindowInsetsListener(mainContainer) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
webView = findViewById(R.id.webview)
// 2. Configure WebSettings
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = true
webSettings.domStorageEnabled = true
webSettings.cacheMode = WebSettings.LOAD_DEFAULT
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_NEVER_ALLOW
// Privacy Hardening
webSettings.allowFileAccess = false
webSettings.allowContentAccess = false
webSettings.setGeolocationEnabled(false)
webSettings.userAgentString = "Mozilla/5.0 (Linux; Android 10; Mobile) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114 Mobile Safari/537.36"
// 3. Feature Checks (Safe Browsing & Dark Mode)
if (WebViewFeature.isFeatureSupported(WebViewFeature.START_SAFE_BROWSING)) {
WebViewCompat.startSafeBrowsing(this) { }
}
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
WebSettingsCompat.setForceDark(webSettings, WebSettingsCompat.FORCE_DARK_ON)
}
// 4. --- COOKIE CONFIGURATION (STAY LOGGED IN) ---
val cookieManager = CookieManager.getInstance()
cookieManager.setAcceptCookie(true)
cookieManager.setAcceptThirdPartyCookies(webView, false) // Block trackers, allow Amazon
// ------------------------------------------------
// 5. Handle Back Button
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (webView.canGoBack()) webView.goBack() else finish()
}
})
loadFilterList()
// 6. Set up the WebView Client
webView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
val req = request ?: return null
val urlStr = req.url.toString().lowercase()
// Block Trackers from filters.txt
for (pattern in blockPatterns) {
if (pattern.containsMatchIn(urlStr)) {
return WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream("".toByteArray()))
}
}
// Block cleartext (Force HTTPS)
if (urlStr.startsWith("http://")) {
return WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream("".toByteArray()))
}
return null
}
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
handler?.cancel() // Security: Don't allow bad SSL
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// Save cookies to disk immediately after page loads
CookieManager.getInstance().flush()
injectPrivacyScript()
}
}
// 7. Load Amazon with a custom header to hide the "App" identity
val extraHeaders = mapOf("X-Requested-With" to "")
webView.loadUrl("https://www.amazon.co.uk", extraHeaders)
}
private fun loadFilterList() {
try {
assets.open("filters.txt").bufferedReader().useLines { lines ->
lines.map { it.trim() }
.filter { it.isNotEmpty() && !it.startsWith("#") }
.forEach { rule ->
val escaped = rule.replace(".", "\\.").replace("*", ".*")
blockPatterns.add(Regex(escaped, RegexOption.IGNORE_CASE))
}
}
} catch (_: Exception) {}
}
private fun injectPrivacyScript() {
val js = """
(function() {
try {
navigator.sendBeacon = function(){ return true; };
window.RTCPeerConnection = null;
window.webkitRTCPeerConnection = null;
const getContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.getContext = function(type) {
if (type.includes('webgl')) return null;
return getContext.apply(this, arguments);
};
} catch(_){}
})();
""".trimIndent()
webView.evaluateJavascript(js, null)
}
override fun onPause() {
super.onPause()
// Save cookies to disk when app goes to background
CookieManager.getInstance().flush()
}
}