Release v0.1.1 2026-07-10
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
package com.phenomrom.iwanttoheal;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Routes every Thor controller event into one JavaScript input service. */
|
||||
public abstract class ControllerBridgeActivity extends BridgeActivity {
|
||||
private static final float AXIS_DEAD_ZONE = 0.45f;
|
||||
private static final long REPEAT_THROTTLE_MS = 55L;
|
||||
private final long[] lastDpadDispatchAt = new long[16];
|
||||
private final Set<String> activeMotionTokens = new HashSet<>();
|
||||
private final Map<String, Long> lastMotionDispatchAt = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
dispatchNativeControllerToken("Button1", false);
|
||||
}
|
||||
});
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
WindowManager.LayoutParams attributes = getWindow().getAttributes();
|
||||
attributes.preferredRefreshRate = 60.0f;
|
||||
getWindow().setAttributes(attributes);
|
||||
if (bridge != null && bridge.getWebView() != null) {
|
||||
bridge.getWebView().setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
bridge.getWebView().setFocusable(true);
|
||||
bridge.getWebView().setFocusableInTouchMode(true);
|
||||
bridge.getWebView().requestFocus();
|
||||
}
|
||||
enterImmersiveMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
clearHeldControllerState();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
enterImmersiveMode();
|
||||
if (bridge != null && bridge.getWebView() != null) bridge.getWebView().requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus) enterImmersiveMode();
|
||||
else clearHeldControllerState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && bridge != null) {
|
||||
bridge.getWebView().requestFocus();
|
||||
}
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
@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(token)) return true;
|
||||
dispatchNativeControllerToken(token, repeat);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||
if (
|
||||
bridge == null
|
||||
|| event.getActionMasked() != MotionEvent.ACTION_MOVE
|
||||
|| !isControllerMotionEvent(event)
|
||||
) {
|
||||
return super.dispatchGenericMotionEvent(event);
|
||||
}
|
||||
|
||||
float leftStickX = event.getAxisValue(MotionEvent.AXIS_X);
|
||||
float leftStickY = event.getAxisValue(MotionEvent.AXIS_Y);
|
||||
dispatchNativeControllerMotion(leftStickX, leftStickY);
|
||||
|
||||
Set<String> currentTokens = new HashSet<>();
|
||||
addAxisTokens(currentTokens, event.getAxisValue(MotionEvent.AXIS_HAT_X), "Button14", "Button15");
|
||||
addAxisTokens(currentTokens, event.getAxisValue(MotionEvent.AXIS_HAT_Y), "Button12", "Button13");
|
||||
addAxisTokens(currentTokens, leftStickX, "Axis0-", "Axis0+");
|
||||
addAxisTokens(currentTokens, leftStickY, "Axis1-", "Axis1+");
|
||||
|
||||
long now = SystemClock.uptimeMillis();
|
||||
for (String token : currentTokens) {
|
||||
boolean repeat = activeMotionTokens.contains(token);
|
||||
long lastDispatch = lastMotionDispatchAt.containsKey(token)
|
||||
? lastMotionDispatchAt.get(token)
|
||||
: 0L;
|
||||
if (!repeat || now - lastDispatch >= REPEAT_THROTTLE_MS) {
|
||||
dispatchNativeControllerToken(token, repeat);
|
||||
lastMotionDispatchAt.put(token, now);
|
||||
}
|
||||
}
|
||||
activeMotionTokens.clear();
|
||||
activeMotionTokens.addAll(currentTokens);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void enterImmersiveMode() {
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||
WindowInsetsControllerCompat controller = WindowCompat.getInsetsController(
|
||||
getWindow(),
|
||||
getWindow().getDecorView()
|
||||
);
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars());
|
||||
controller.setSystemBarsBehavior(
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isControllerMotionEvent(MotionEvent event) {
|
||||
int source = event.getSource();
|
||||
return (source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|
||||
|| (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
|
||||
|| (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
|
||||
}
|
||||
|
||||
private void addAxisTokens(Set<String> tokens, float value, String negative, String positive) {
|
||||
if (value <= -AXIS_DEAD_ZONE) tokens.add(negative);
|
||||
if (value >= AXIS_DEAD_ZONE) tokens.add(positive);
|
||||
}
|
||||
|
||||
private void dispatchNativeControllerToken(String token, boolean repeat) {
|
||||
if (bridge == null || bridge.getWebView() == null) return;
|
||||
String script =
|
||||
"window.dispatchEvent(new CustomEvent('iwt-native-controller',"
|
||||
+ "{detail:{token:'" + token + "',repeat:" + repeat + "}}));";
|
||||
bridge.getWebView().post(() -> {
|
||||
bridge.getWebView().requestFocus();
|
||||
bridge.getWebView().evaluateJavascript(script, null);
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchNativeControllerMotion(float x, float y) {
|
||||
if (bridge == null || bridge.getWebView() == null) return;
|
||||
String script =
|
||||
"window.dispatchEvent(new CustomEvent('iwt-native-controller-motion',"
|
||||
+ "{detail:{x:" + x + ",y:" + y + "}}));";
|
||||
bridge.getWebView().post(() -> bridge.getWebView().evaluateJavascript(script, null));
|
||||
}
|
||||
|
||||
private void clearHeldControllerState() {
|
||||
activeMotionTokens.clear();
|
||||
lastMotionDispatchAt.clear();
|
||||
dispatchNativeControllerMotion(0.0f, 0.0f);
|
||||
if (bridge == null || bridge.getWebView() == null) return;
|
||||
bridge.getWebView().post(() -> bridge.getWebView().evaluateJavascript(
|
||||
"window.dispatchEvent(new Event('iwt-native-controller-reset'));",
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
private boolean shouldThrottleDpad(String token) {
|
||||
int index = Integer.parseInt(token.substring("Button".length()));
|
||||
long now = SystemClock.uptimeMillis();
|
||||
if (now - lastDpadDispatchAt[index] < REPEAT_THROTTLE_MS) return true;
|
||||
lastDpadDispatchAt[index] = 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_DPAD_CENTER:
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
case KeyEvent.KEYCODE_NUMPAD_ENTER:
|
||||
return "Button0";
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
case KeyEvent.KEYCODE_ESCAPE:
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,11 @@
|
||||
package com.phenomrom.iwanttoheal;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
|
||||
import com.getcapacitor.BridgeActivity;
|
||||
|
||||
public class MainActivity extends BridgeActivity {
|
||||
public class MainActivity extends ControllerBridgeActivity {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
registerPlugin(ThorDualScreenPlugin.class);
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
WindowManager.LayoutParams attributes = getWindow().getAttributes();
|
||||
attributes.preferredRefreshRate = 60.0f;
|
||||
getWindow().setAttributes(attributes);
|
||||
getBridge().getWebView().setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
getBridge().getWebView().setFocusableInTouchMode(true);
|
||||
getBridge().getWebView().requestFocus();
|
||||
enterImmersiveMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus) enterImmersiveMode();
|
||||
}
|
||||
|
||||
private void enterImmersiveMode() {
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||
WindowInsetsControllerCompat controller = WindowCompat.getInsetsController(
|
||||
getWindow(),
|
||||
getWindow().getDecorView()
|
||||
);
|
||||
controller.hide(WindowInsetsCompat.Type.systemBars());
|
||||
controller.setSystemBarsBehavior(
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
package com.phenomrom.iwanttoheal;
|
||||
|
||||
import android.app.Presentation;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.getcapacitor.JSArray;
|
||||
import com.getcapacitor.JSObject;
|
||||
import com.getcapacitor.Plugin;
|
||||
import com.getcapacitor.PluginCall;
|
||||
import com.getcapacitor.PluginMethod;
|
||||
import com.getcapacitor.annotation.CapacitorPlugin;
|
||||
|
||||
/** Keeps a dedicated WebView visible on each AYN Thor panel for the activity lifetime. */
|
||||
@CapacitorPlugin(name = "ThorDualScreen")
|
||||
public class ThorDualScreenPlugin extends Plugin implements DisplayManager.DisplayListener {
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
private DisplayManager displayManager;
|
||||
private ThorPresentation presentation;
|
||||
private boolean forceEnabled;
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
displayManager = (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
|
||||
displayManager.registerDisplayListener(this, handler);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void getDisplays(PluginCall call) {
|
||||
Display current = currentDisplay();
|
||||
JSArray displays = new JSArray();
|
||||
for (Display display : displayManager.getDisplays()) displays.put(describeDisplay(display, current));
|
||||
JSObject result = new JSObject();
|
||||
result.put("currentDisplayId", current == null ? -1 : current.getDisplayId());
|
||||
result.put("displays", displays);
|
||||
call.resolve(result);
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
public void forceBothDisplays(PluginCall call) {
|
||||
forceEnabled = true;
|
||||
getActivity().runOnUiThread(() -> {
|
||||
try {
|
||||
DisplayPlan plan = createPlan();
|
||||
if (plan.target == null) {
|
||||
call.reject("No active secondary Android display is available.");
|
||||
return;
|
||||
}
|
||||
boolean alreadyOpen = presentation != null
|
||||
&& presentation.isShowing()
|
||||
&& presentation.getDisplay().getDisplayId() == plan.target.getDisplayId();
|
||||
if (!alreadyOpen) openPlan(plan);
|
||||
JSObject result = describeDisplay(plan.target, currentDisplay());
|
||||
result.put("opened", true);
|
||||
result.put("topOnActivity", plan.currentIsTop);
|
||||
call.resolve(result);
|
||||
} catch (Exception exception) {
|
||||
closePresentation();
|
||||
call.reject("Unable to force both Thor displays.", exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleOnResume() {
|
||||
if (forceEnabled) scheduleEnsurePresentation(0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleOnDestroy() {
|
||||
forceEnabled = false;
|
||||
if (displayManager != null) displayManager.unregisterDisplayListener(this);
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
closePresentation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
if (forceEnabled) scheduleEnsurePresentation(100L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
if (presentation != null && presentation.getDisplay().getDisplayId() == displayId) {
|
||||
closePresentation();
|
||||
}
|
||||
if (forceEnabled) scheduleEnsurePresentation(250L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
if (forceEnabled) scheduleEnsurePresentation(100L);
|
||||
}
|
||||
|
||||
private void scheduleEnsurePresentation(long delayMs) {
|
||||
handler.removeCallbacks(ensurePresentation);
|
||||
handler.postDelayed(ensurePresentation, delayMs);
|
||||
}
|
||||
|
||||
private final Runnable ensurePresentation = () -> {
|
||||
if (!forceEnabled || getActivity() == null || getActivity().isFinishing()) return;
|
||||
DisplayPlan plan = createPlan();
|
||||
if (plan.target == null) return;
|
||||
if (
|
||||
presentation != null
|
||||
&& presentation.isShowing()
|
||||
&& presentation.getDisplay().getDisplayId() == plan.target.getDisplayId()
|
||||
) return;
|
||||
openPlan(plan);
|
||||
};
|
||||
|
||||
private void openPlan(DisplayPlan plan) {
|
||||
String baseUrl = bridge.getLocalUrl();
|
||||
String targetUrl = baseUrl + "/?display=" + (plan.targetIsTop ? "top" : "bottom") + "&role=presentation";
|
||||
String currentUrl = baseUrl + "/?display=" + (plan.currentIsTop ? "top" : "bottom") + "&role=activity";
|
||||
closePresentation();
|
||||
presentation = new ThorPresentation(
|
||||
getActivity(),
|
||||
plan.target,
|
||||
targetUrl
|
||||
);
|
||||
presentation.setOnDismissListener(dialog -> {
|
||||
presentation = null;
|
||||
notifyListeners("displayDisconnected", new JSObject());
|
||||
if (forceEnabled) scheduleEnsurePresentation(300L);
|
||||
});
|
||||
presentation.show();
|
||||
bridge.getWebView().loadUrl(currentUrl);
|
||||
}
|
||||
|
||||
private DisplayPlan createPlan() {
|
||||
Display current = currentDisplay();
|
||||
int currentId = current == null ? -1 : current.getDisplayId();
|
||||
Display[] displays = displayManager.getDisplays();
|
||||
Display top = largestDisplay(displays);
|
||||
boolean currentIsTop = top != null && top.getDisplayId() == currentId;
|
||||
Display target = currentIsTop
|
||||
? smallestOtherDisplay(displays, currentId)
|
||||
: top != null && top.getDisplayId() != currentId ? top : largestOtherDisplay(displays, currentId);
|
||||
boolean targetIsTop = target != null && top != null && target.getDisplayId() == top.getDisplayId();
|
||||
return new DisplayPlan(target, targetIsTop, currentIsTop);
|
||||
}
|
||||
|
||||
private Display currentDisplay() {
|
||||
return getActivity().getWindow().getDecorView().getDisplay();
|
||||
}
|
||||
|
||||
private Display largestDisplay(Display[] displays) {
|
||||
Display selected = null;
|
||||
long pixels = -1L;
|
||||
for (Display display : displays) {
|
||||
if (display.getState() == Display.STATE_OFF) continue;
|
||||
long candidate = pixelCount(display);
|
||||
if (candidate > pixels) {
|
||||
selected = display;
|
||||
pixels = candidate;
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
private Display largestOtherDisplay(Display[] displays, int currentId) {
|
||||
Display selected = null;
|
||||
long pixels = -1L;
|
||||
for (Display display : displays) {
|
||||
if (display.getDisplayId() == currentId || display.getState() == Display.STATE_OFF) continue;
|
||||
long candidate = pixelCount(display);
|
||||
if (candidate > pixels) {
|
||||
selected = display;
|
||||
pixels = candidate;
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
private Display smallestOtherDisplay(Display[] displays, int currentId) {
|
||||
Display selected = null;
|
||||
long pixels = Long.MAX_VALUE;
|
||||
for (Display display : displays) {
|
||||
if (display.getDisplayId() == currentId || display.getState() == Display.STATE_OFF) continue;
|
||||
long candidate = pixelCount(display);
|
||||
if (candidate < pixels) {
|
||||
selected = display;
|
||||
pixels = candidate;
|
||||
}
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
private long pixelCount(Display display) {
|
||||
Display.Mode mode = display.getMode();
|
||||
return (long) mode.getPhysicalWidth() * mode.getPhysicalHeight();
|
||||
}
|
||||
|
||||
private JSObject describeDisplay(Display display, Display current) {
|
||||
Display.Mode mode = display.getMode();
|
||||
JSObject result = new JSObject();
|
||||
result.put("id", display.getDisplayId());
|
||||
result.put("name", display.getName());
|
||||
result.put("width", mode.getPhysicalWidth());
|
||||
result.put("height", mode.getPhysicalHeight());
|
||||
result.put("refreshRate", mode.getRefreshRate());
|
||||
result.put("isCurrent", current != null && current.getDisplayId() == display.getDisplayId());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void closePresentation() {
|
||||
if (presentation == null) return;
|
||||
ThorPresentation closing = presentation;
|
||||
presentation = null;
|
||||
closing.setOnDismissListener(null);
|
||||
closing.dismiss();
|
||||
}
|
||||
|
||||
private final class ThorPresentation extends Presentation {
|
||||
private final String initialUrl;
|
||||
private WebView webView;
|
||||
|
||||
ThorPresentation(Context context, Display display, String initialUrl) {
|
||||
super(context, display);
|
||||
this.initialUrl = initialUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow().setBackgroundDrawableResource(android.R.color.black);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
WindowManager.LayoutParams attributes = getWindow().getAttributes();
|
||||
attributes.preferredRefreshRate = 60.0f;
|
||||
getWindow().setAttributes(attributes);
|
||||
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
|
||||
);
|
||||
|
||||
FrameLayout container = new FrameLayout(getContext());
|
||||
container.setBackgroundColor(Color.BLACK);
|
||||
webView = new WebView(getContext());
|
||||
configureWebView(webView);
|
||||
container.addView(webView, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
));
|
||||
setContentView(container);
|
||||
webView.loadUrl(initialUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (webView != null) {
|
||||
webView.stopLoading();
|
||||
webView.destroy();
|
||||
webView = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void configureWebView(WebView webView) {
|
||||
WebSettings settings = webView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setDomStorageEnabled(true);
|
||||
settings.setDatabaseEnabled(true);
|
||||
settings.setMediaPlaybackRequiresUserGesture(false);
|
||||
settings.setAllowFileAccess(true);
|
||||
settings.setAllowContentAccess(true);
|
||||
webView.setBackgroundColor(Color.BLACK);
|
||||
webView.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
webView.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
||||
return bridge.getLocalServer().shouldInterceptRequest(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final class DisplayPlan {
|
||||
private final Display target;
|
||||
private final boolean targetIsTop;
|
||||
private final boolean currentIsTop;
|
||||
|
||||
DisplayPlan(Display target, boolean targetIsTop, boolean currentIsTop) {
|
||||
this.target = target;
|
||||
this.targetIsTop = targetIsTop;
|
||||
this.currentIsTop = currentIsTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user