build(android): simplify WebViewRequestHandler

This commit is contained in:
Johannes Millan 2024-12-14 11:32:50 +01:00
parent 125c26a486
commit 0e3356f8e6
5 changed files with 39 additions and 97 deletions

3
android/.gitignore vendored
View file

@ -92,3 +92,6 @@ app/src/main/res/xml/config.xml
/app/fdroid/release
/app/play/release
/app/src/play/java/com/superproductivity/superproductivity/GoogleClientId.java
###############
.idea/deploymentTargetSelector.xml

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="SP">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2024-11-13T18:07:26.343977020Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/home/johannes/.android/avd/A11_Pixel_6_API_30.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View file

@ -7,7 +7,6 @@ import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.util.Log
import com.superproductivity.superproductivity.BuildConfig
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.ByteArrayInputStream
@ -35,6 +34,7 @@ class WebViewRequestHandler(private val activity: Activity, private val serviceH
}
}
fun interceptWebRequest(request: WebResourceRequest?): WebResourceResponse? {
try {
if (request == null || request.isForMainFrame) {
@ -53,10 +53,7 @@ class WebViewRequestHandler(private val activity: Activity, private val serviceH
return null
}
Log.v(
"TW",
"interceptRequest mf:${request?.isForMainFrame.toString()} ${request.method} ${request?.url}"
)
Log.v("TW", "interceptRequest mf:${request?.isForMainFrame.toString()} ${request.method} ${request?.url}")
// since we currently don't have a way to also post the body, we only handle GET, HEAD and OPTIONS requests
// see https://github.com/KonstantinSchubert/request_data_webviewclient for a possible solution
@ -64,70 +61,30 @@ class WebViewRequestHandler(private val activity: Activity, private val serviceH
return null
}
// remove user agent header in the hopes that we're treated better by the remotes :D
val keysToRemove =
request.requestHeaders.keys.filter { it.equals("User-Agent", ignoreCase = true) }
for (key in keysToRemove) {
request.requestHeaders.remove(key)
}
val client = OkHttpClient()
val newRequestBuilder = Request.Builder()
.url(request.url.toString())
.removeHeader("User-Agent")
.removeHeader("Origin")
.removeHeader("Referer")
.method(request.method, null)
// Add each header from the original request to the new request
for ((key, value) in request.requestHeaders) {
newRequestBuilder.addHeader(key, value)
}
val newRequest = newRequestBuilder.build()
// currently we can't handle POST requests since everything
if (request.method.uppercase() == "OPTIONS") {
Log.v("TW", "OPTIONS request triggered")
client.newCall(newRequest).execute().use { response ->
Log.v(
"TW",
"OPTIONS original response: ${response.code} ${response.message} ${response.body?.string()}"
)
Log.v("TW", "OPTIONS original response: ${response.code} ${response.message} ${response.body?.string()}")
if (response.code != 200) {
Log.v("TW", "OPTIONS overwrite")
return OptionsAllowResponse.build()
}
}
}
// Handle HEAD requests
if (request.method.uppercase() == "HEAD") {
Log.v("TW", "HEAD request triggered")
client.newCall(newRequest).execute().use { response ->
Log.v("TW", "HEAD response ${response.code} ${response.message}")
val responseHeaders = response.headers.names()
.associateWith { response.headers(it)?.joinToString() }
.toMutableMap()
val keysToRemoveI = responseHeaders.keys.filter {
it.equals("Access-Control-Allow-Origin", ignoreCase = true)
}
for (key in keysToRemoveI) {
responseHeaders.remove(key)
}
responseHeaders["Access-Control-Allow-Origin"] = "*"
val contentType = response.header("Content-Type", "text/plain")
val contentEncoding = response.header("Content-Encoding", "utf-8")
val reasonPhrase = response.message.ifEmpty { "OK" }
return WebResourceResponse(
contentType,
contentEncoding,
response.code,
reasonPhrase,
responseHeaders,
null
)
}
}
//-------------
Log.v("TW", "exec request ${request.url}")
@ -137,23 +94,16 @@ class WebViewRequestHandler(private val activity: Activity, private val serviceH
.associateWith { response.headers(it)?.joinToString() }
.toMutableMap()
val keysToRemoveI =
responseHeaders.keys.filter {
it.equals(
"Access-Control-Allow-Origin",
ignoreCase = true
)
}
for (key in keysToRemoveI) {
responseHeaders.remove(key)
}
responseHeaders["Access-Control-Allow-Origin"] = "*"
upsertKeyValue(responseHeaders, "Access-Control-Allow-Origin", "*")
upsertKeyValue(responseHeaders, "Access-Control-Allow-Methods", "GET, POST, OPTIONS")
val contentType = response.header("Content-Type", "text/plain")
val contentEncoding = response.header("Content-Encoding", "utf-8")
// TODO check if this needs to be adjusted for HEAD requests
val inputStream = ByteArrayInputStream(response.body?.bytes())
val reasonPhrase =
response.message.ifEmpty { "OK" } // provide a default value if the message is null or empty
return WebResourceResponse(
contentType,
contentEncoding,
@ -168,4 +118,18 @@ class WebViewRequestHandler(private val activity: Activity, private val serviceH
return null
}
}
fun upsertKeyValue(responseHeaders: MutableMap<String, String?>, keyToChange: String, value: String): MutableMap<String, String?> {
val keyToChangeLower = keyToChange.lowercase()
for (key in responseHeaders.keys) {
if (key.lowercase() == keyToChangeLower) {
// Reassign old key
responseHeaders[key] = value
// Done
return responseHeaders
}
}
responseHeaders[keyToChange] = value
return responseHeaders
}
}

View file

@ -52,6 +52,7 @@ import { SS } from '../../../../core/persistence/storage-keys.const';
import { MatDialog } from '@angular/material/dialog';
import { DialogPromptComponent } from '../../../../ui/dialog-prompt/dialog-prompt.component';
import { stripTrailing } from '../../../../util/strip-trailing';
import { IS_ANDROID_WEB_VIEW } from '../../../../util/is-android-web-view';
const BLOCK_ACCESS_KEY = 'SUP_BLOCK_JIRA_ACCESS';
const API_VERSION = 'latest';
@ -85,13 +86,14 @@ export class JiraApiService {
private _requestsLog: { [key: string]: JiraRequestLogItem } = {};
private _isBlockAccess: boolean = !!sessionStorage.getItem(BLOCK_ACCESS_KEY);
private _isExtension: boolean = false;
private _isInterfacesReadyIfNeeded$: Observable<boolean> = IS_ELECTRON
? of(true).pipe()
: this._chromeExtensionInterfaceService.onReady$.pipe(
mapTo(true),
shareReplay(1),
timeoutWith(500, throwError('Jira: Extension not installed or not ready')),
);
private _isInterfacesReadyIfNeeded$: Observable<boolean> =
IS_ELECTRON || IS_ANDROID_WEB_VIEW
? of(true).pipe()
: this._chromeExtensionInterfaceService.onReady$.pipe(
mapTo(true),
shareReplay(1),
timeoutWith(500, throwError('Jira: Extension not installed or not ready')),
);
constructor(
private _chromeExtensionInterfaceService: ChromeExtensionInterfaceService,
@ -371,7 +373,7 @@ export class JiraApiService {
settings.host &&
settings.userName &&
settings.password &&
(IS_ELECTRON || this._isExtension)
(IS_ELECTRON || this._isExtension || IS_ANDROID_WEB_VIEW)
);
}
@ -408,7 +410,7 @@ export class JiraApiService {
this._snackService.open({
type: 'ERROR',
msg:
!IS_ELECTRON && !this._isExtension
!IS_ELECTRON && !this._isExtension && !IS_ANDROID_WEB_VIEW
? T.F.JIRA.S.EXTENSION_NOT_LOADED
: T.F.JIRA.S.INSUFFICIENT_SETTINGS,
});