155 lines
6.0 KiB
Kotlin
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()
|
|
}
|
|
} |