package com.warren.iwanttoheal; import android.content.Intent; import android.os.Bundle; import android.os.SystemClock; import android.view.KeyEvent; import android.view.View; import com.getcapacitor.BridgeActivity; import java.io.File; public abstract class ControllerBridgeActivity extends BridgeActivity { public static final String EXTRA_INITIAL_URL = "com.warren.iwanttoheal.INITIAL_URL"; private static final long DPAD_THROTTLE_MS = 125; private long lastDpadDispatchAt = 0; @Override public void onCreate(Bundle savedInstanceState) { clearWebViewServiceWorkers(); super.onCreate(savedInstanceState); if (bridge != null) { bridge.getWebView().clearCache(true); } loadIntentUrl(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); loadIntentUrl(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) enableImmersiveMode(); } private void loadIntentUrl() { if (bridge == null || getIntent() == null) return; String initialUrl = getIntent().getStringExtra(EXTRA_INITIAL_URL); if (initialUrl == null || initialUrl.isEmpty()) return; bridge.getWebView().post(() -> bridge.getWebView().loadUrl(initialUrl)); } private void enableImmersiveMode() { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE ); } private void clearWebViewServiceWorkers() { File webViewData = new File(getApplicationInfo().dataDir, "app_webview"); deleteIfExists(new File(webViewData, "Default/Service Worker")); deleteIfExists(new File(webViewData, "Service Worker")); } private void deleteIfExists(File file) { if (!file.exists()) return; if (file.isDirectory()) { File[] children = file.listFiles(); if (children != null) { for (File child : children) { deleteIfExists(child); } } } file.delete(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { String token = controllerToken(event.getKeyCode()); if (token == null || bridge == null) return super.dispatchKeyEvent(event); if (event.getAction() == KeyEvent.ACTION_DOWN) { boolean repeat = event.getRepeatCount() > 0; if (isDpadToken(token) && shouldThrottleDpad()) return true; String script = "window.dispatchEvent(new CustomEvent('ashen-halls-native-controller'," + "{detail:{token:'" + token + "',repeat:" + repeat + "}}));"; bridge.getWebView().post( () -> bridge.getWebView().evaluateJavascript(script, null) ); } return true; } private boolean shouldThrottleDpad() { long now = SystemClock.uptimeMillis(); if (now - lastDpadDispatchAt < DPAD_THROTTLE_MS) return true; lastDpadDispatchAt = now; return false; } private boolean isDpadToken(String token) { return token.equals("Button12") || token.equals("Button13") || token.equals("Button14") || token.equals("Button15"); } private String controllerToken(int keyCode) { switch (keyCode) { case KeyEvent.KEYCODE_BUTTON_A: case KeyEvent.KEYCODE_ENTER: return "Button0"; case KeyEvent.KEYCODE_BUTTON_B: case KeyEvent.KEYCODE_BACK: return "Button1"; case KeyEvent.KEYCODE_BUTTON_X: return "Button2"; case KeyEvent.KEYCODE_BUTTON_Y: return "Button3"; case KeyEvent.KEYCODE_BUTTON_L1: return "Button4"; case KeyEvent.KEYCODE_BUTTON_R1: return "Button5"; case KeyEvent.KEYCODE_BUTTON_L2: return "Button6"; case KeyEvent.KEYCODE_BUTTON_R2: return "Button7"; case KeyEvent.KEYCODE_BUTTON_SELECT: return "Button8"; case KeyEvent.KEYCODE_BUTTON_START: return "Button9"; case KeyEvent.KEYCODE_BUTTON_THUMBL: return "Button10"; case KeyEvent.KEYCODE_BUTTON_THUMBR: return "Button11"; case KeyEvent.KEYCODE_DPAD_UP: return "Button12"; case KeyEvent.KEYCODE_DPAD_DOWN: return "Button13"; case KeyEvent.KEYCODE_DPAD_LEFT: return "Button14"; case KeyEvent.KEYCODE_DPAD_RIGHT: return "Button15"; default: return null; } } }