Compare commits

..

1 Commits

Author SHA1 Message Date
Manuel Beck
0bbe43b5be fix: log missing optional assets not as exception
- When no plugins are added, `cordova_plugins.js` is not present and will give an error in console: `java.io.FileNotFoundException: www/cordova_plugins.js``
- Chromium tries to load `favicon.ico` which is not present: `java.io.FileNotFoundException: www/favicon.ico`
- Generated-By: GPT-5.3-Codex, GitHub Copilot Chat
2026-03-13 17:24:25 +01:00
2 changed files with 48 additions and 50 deletions

View File

@@ -32,12 +32,11 @@ import android.os.Build;
import android.content.IntentFilter;
import android.telephony.TelephonyManager;
import android.view.KeyEvent;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import java.util.HashMap;
import androidx.activity.OnBackPressedCallback;
import androidx.activity.OnBackPressedDispatcherOwner;
/**
* This class exposes methods in Cordova that can be called from JavaScript.
*/
@@ -49,7 +48,7 @@ public class CoreAndroid extends CordovaPlugin {
private CallbackContext messageChannel;
private PluginResult pendingResume;
private PluginResult pendingPause;
private OnBackPressedCallback backCallback;
private OnBackInvokedCallback backCallback;
private final Object messageChannelLock = new Object();
private final Object backButtonHandlerLock = new Object();
@@ -254,56 +253,30 @@ public class CoreAndroid extends CordovaPlugin {
*/
public void overrideBackbutton(boolean override) {
LOG.i("App", "WARNING: Back Button Default Behavior will be overridden. The backbutton event will be fired!");
if (cordova.getActivity() == null) {
return;
}
final boolean shouldOverride = override;
cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (shouldOverride) {
synchronized (backButtonHandlerLock) {
if (backCallback == null) {
registerBackPressedCallback();
}
}
} else {
synchronized (backButtonHandlerLock) {
if (backCallback != null) {
unregisterBackPressedCallback();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
if (override) {
synchronized (backButtonHandlerLock) {
if (backCallback == null) {
// The callback is intentionally empty. Since API 36, intercepting the back button is ignored, which means
// the onDispatchKeyEvent boolean result won't actually stop native from consuming the back button and doing
// it's own logic, UNLESS if there is an OnBackInvokedCallback registered on the dispatcher.
// The key dispatch events will still fire, which still handles propagating back button events to the webview.
// See https://developer.android.com/about/versions/16/behavior-changes-16#predictive-back for more info on the caveat.
backCallback = () -> {};
this.cordova.getActivity().getOnBackInvokedDispatcher().registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, backCallback);
}
}
} else {
synchronized (backButtonHandlerLock) {
if (backCallback != null) {
this.cordova.getActivity().getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(backCallback);
backCallback = null;
}
}
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, shouldOverride);
}
});
}
}
/**
* Registers an AndroidX back callback so Cordova can keep routing back presses through its
* existing key dispatch path across Android versions without directly referencing newer
* platform-only back APIs.
*/
private void registerBackPressedCallback() {
final OnBackPressedDispatcherOwner backPressedDispatcherOwner = this.cordova.getActivity();
backCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
// Intentionally empty.
// On modern Android versions, registering a callback keeps back handling
// routed through Cordova's existing key dispatch path.
}
};
backPressedDispatcherOwner.getOnBackPressedDispatcher().addCallback(backPressedDispatcherOwner, backCallback);
}
private void unregisterBackPressedCallback() {
backCallback.remove();
backCallback = null;
webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_BACK, override);
}
/**

View File

@@ -111,6 +111,13 @@ public class SystemWebViewClient extends WebViewClient {
return new WebResourceResponse(mimeType, null, is);
} catch (Exception e) {
// Some files are requested by default but might not exist in a valid project setup.
// When these files are missing, the request should quietly fall through instead of
// being logged as an application error.
if (isOptionalMissingAsset(path, e)) {
LOG.d(TAG, "Optional Web resource not found at \"" + path + "\"");
return null;
}
e.printStackTrace();
LOG.e(TAG, "Exception handling Web resource at \"" + path + "\"", e);
}
@@ -132,6 +139,24 @@ public class SystemWebViewClient extends WebViewClient {
}
}
/**
* Returns `true` when the request failure is expected and non-fatal.
*
* Some web resources are requested by default but might not exist in a valid project setup:
* - {@code cordova_plugins.js} can be absent when no plugins are installed.
* - {@code favicon.ico} is often requested by the WebView/Chromium engine automatically.
*
* When these files are missing, the request should quietly fall through instead of being logged
* as an application error.
*/
private static boolean isOptionalMissingAsset(String path, Exception exception) {
if (!(exception instanceof FileNotFoundException)) {
return false;
}
return "cordova_plugins.js".equals(path) || "favicon.ico".equals(path);
}
/**
* Give the host application a chance to take over the control when a new url
* is about to be loaded in the current WebView.