Android build v1.1.7
This commit is contained in:
Binary file not shown.
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "com.warren.iwanttoheal"
|
applicationId "com.warren.iwanttoheal"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 85
|
versionCode 86
|
||||||
versionName "1.1.6"
|
versionName "1.1.7"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -7,19 +7,27 @@ import android.content.pm.PackageManager;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.view.InputDevice;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import com.getcapacitor.BridgeActivity;
|
import com.getcapacitor.BridgeActivity;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public abstract class ControllerBridgeActivity extends BridgeActivity {
|
public abstract class ControllerBridgeActivity extends BridgeActivity {
|
||||||
|
|
||||||
public static final String EXTRA_INITIAL_URL = "com.warren.iwanttoheal.INITIAL_URL";
|
public static final String EXTRA_INITIAL_URL = "com.warren.iwanttoheal.INITIAL_URL";
|
||||||
private static final String PREFS_NAME = "com.warren.iwanttoheal.performance";
|
private static final String PREFS_NAME = "com.warren.iwanttoheal.performance";
|
||||||
private static final String CACHE_CLEARED_VERSION_KEY = "webview_cache_cleared_version";
|
private static final String CACHE_CLEARED_VERSION_KEY = "webview_cache_cleared_version";
|
||||||
|
private static final float AXIS_DEAD_ZONE = 0.45f;
|
||||||
private static final long DPAD_THROTTLE_MS = 55;
|
private static final long DPAD_THROTTLE_MS = 55;
|
||||||
private final long[] lastDpadDispatchAt = new long[16];
|
private final long[] lastDpadDispatchAt = new long[16];
|
||||||
|
private final Set<String> activeMotionTokens = new HashSet<>();
|
||||||
|
private final Map<String, Long> lastMotionDispatchAt = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -140,19 +148,98 @@ public abstract class ControllerBridgeActivity extends BridgeActivity {
|
|||||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||||
boolean repeat = event.getRepeatCount() > 0;
|
boolean repeat = event.getRepeatCount() > 0;
|
||||||
if (isDpadToken(token) && shouldThrottleDpad(token)) return true;
|
if (isDpadToken(token) && shouldThrottleDpad(token)) return true;
|
||||||
String script =
|
dispatchNativeControllerToken(token, repeat);
|
||||||
"window.dispatchEvent(new CustomEvent('ashen-halls-native-controller',"
|
|
||||||
+ "{detail:{token:'" + token + "',repeat:" + repeat + "}}));";
|
|
||||||
bridge.getWebView().post(
|
|
||||||
() -> {
|
|
||||||
bridge.getWebView().requestFocus();
|
|
||||||
bridge.getWebView().evaluateJavascript(script, null);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||||
|
if (
|
||||||
|
bridge == null
|
||||||
|
|| event.getActionMasked() != MotionEvent.ACTION_MOVE
|
||||||
|
|| !isControllerMotionEvent(event)
|
||||||
|
) {
|
||||||
|
return super.dispatchGenericMotionEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
event.getAxisValue(MotionEvent.AXIS_X),
|
||||||
|
"Axis0-",
|
||||||
|
"Axis0+"
|
||||||
|
);
|
||||||
|
addAxisTokens(
|
||||||
|
currentTokens,
|
||||||
|
event.getAxisValue(MotionEvent.AXIS_Y),
|
||||||
|
"Axis1-",
|
||||||
|
"Axis1+"
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean hadMotionTokens = !activeMotionTokens.isEmpty();
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
for (String token : currentTokens) {
|
||||||
|
boolean repeat = activeMotionTokens.contains(token);
|
||||||
|
long lastDispatchAt = lastMotionDispatchAt.containsKey(token)
|
||||||
|
? lastMotionDispatchAt.get(token)
|
||||||
|
: 0L;
|
||||||
|
if (!repeat || now - lastDispatchAt >= DPAD_THROTTLE_MS) {
|
||||||
|
dispatchNativeControllerToken(token, repeat);
|
||||||
|
lastMotionDispatchAt.put(token, now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeMotionTokens.clear();
|
||||||
|
activeMotionTokens.addAll(currentTokens);
|
||||||
|
|
||||||
|
if (currentTokens.isEmpty() && !hadMotionTokens) {
|
||||||
|
return super.dispatchGenericMotionEvent(event);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 negativeToken,
|
||||||
|
String positiveToken
|
||||||
|
) {
|
||||||
|
if (value <= -AXIS_DEAD_ZONE) tokens.add(negativeToken);
|
||||||
|
if (value >= AXIS_DEAD_ZONE) tokens.add(positiveToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchNativeControllerToken(String token, boolean repeat) {
|
||||||
|
if (bridge == null || bridge.getWebView() == null) return;
|
||||||
|
String script =
|
||||||
|
"window.dispatchEvent(new CustomEvent('ashen-halls-native-controller',"
|
||||||
|
+ "{detail:{token:'" + token + "',repeat:" + repeat + "}}));";
|
||||||
|
bridge.getWebView().post(
|
||||||
|
() -> {
|
||||||
|
bridge.getWebView().requestFocus();
|
||||||
|
bridge.getWebView().evaluateJavascript(script, null);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean shouldThrottleDpad(String token) {
|
private boolean shouldThrottleDpad(String token) {
|
||||||
int buttonIndex = Integer.parseInt(token.substring("Button".length()));
|
int buttonIndex = Integer.parseInt(token.substring("Button".length()));
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
@@ -171,10 +258,13 @@ public abstract class ControllerBridgeActivity extends BridgeActivity {
|
|||||||
private String controllerToken(int keyCode) {
|
private String controllerToken(int keyCode) {
|
||||||
switch (keyCode) {
|
switch (keyCode) {
|
||||||
case KeyEvent.KEYCODE_BUTTON_A:
|
case KeyEvent.KEYCODE_BUTTON_A:
|
||||||
|
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||||
case KeyEvent.KEYCODE_ENTER:
|
case KeyEvent.KEYCODE_ENTER:
|
||||||
|
case KeyEvent.KEYCODE_NUMPAD_ENTER:
|
||||||
return "Button0";
|
return "Button0";
|
||||||
case KeyEvent.KEYCODE_BUTTON_B:
|
case KeyEvent.KEYCODE_BUTTON_B:
|
||||||
case KeyEvent.KEYCODE_BACK:
|
case KeyEvent.KEYCODE_BACK:
|
||||||
|
case KeyEvent.KEYCODE_ESCAPE:
|
||||||
return "Button1";
|
return "Button1";
|
||||||
case KeyEvent.KEYCODE_BUTTON_X:
|
case KeyEvent.KEYCODE_BUTTON_X:
|
||||||
return "Button2";
|
return "Button2";
|
||||||
|
|||||||
+199
@@ -1433,6 +1433,14 @@ textarea:focus-visible,
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-shell.settings-shell {
|
||||||
|
padding: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-shell.settings-shell > .app-header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-shell {
|
.auth-shell {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1988,6 +1996,14 @@ h2 {
|
|||||||
outline-color: var(--gold);
|
outline-color: var(--gold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-nav {
|
||||||
|
align-items: stretch;
|
||||||
|
display: grid;
|
||||||
|
gap: 8px;
|
||||||
|
grid-template-columns: minmax(92px, auto) minmax(0, 1fr);
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-tabs,
|
.settings-tabs,
|
||||||
.talent-page-tabs {
|
.talent-page-tabs {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -1996,6 +2012,10 @@ h2 {
|
|||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-tabs {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-tabs button,
|
.settings-tabs button,
|
||||||
.talent-page-tabs button {
|
.talent-page-tabs button {
|
||||||
background: #15161c;
|
background: #15161c;
|
||||||
@@ -2025,9 +2045,15 @@ h2 {
|
|||||||
height: calc(100dvh - 92px);
|
height: calc(100dvh - 92px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-shell .settings-screen {
|
||||||
|
height: calc(100dvh - 12px);
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-tab-panel {
|
.settings-tab-panel {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-bindings-panel {
|
.settings-bindings-panel {
|
||||||
@@ -2045,6 +2071,7 @@ h2 {
|
|||||||
.settings-bindings-panel .binding-list {
|
.settings-bindings-panel .binding-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.equipment-screen .gear-summary,
|
.equipment-screen .gear-summary,
|
||||||
@@ -6962,6 +6989,178 @@ h2 {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-screen {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-shell .settings-screen {
|
||||||
|
height: calc(100dvh - 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-nav {
|
||||||
|
gap: 6px;
|
||||||
|
grid-template-columns: minmax(76px, auto) minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tabs {
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tabs button,
|
||||||
|
.settings-back-button {
|
||||||
|
font-size: 7px;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-tab-panel {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-settings,
|
||||||
|
.controller-preferences {
|
||||||
|
gap: 10px;
|
||||||
|
margin: 8px 0 0;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-settings.settings-tab-panel,
|
||||||
|
.controller-preferences.settings-tab-panel {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-settings {
|
||||||
|
align-items: start;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(214px, auto);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-settings h2,
|
||||||
|
.settings-heading h2 {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-settings p:not(.eyebrow),
|
||||||
|
.controller-preferences p:not(.eyebrow) {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.15;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-settings > small {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-actions {
|
||||||
|
align-content: start;
|
||||||
|
align-self: start;
|
||||||
|
gap: 7px;
|
||||||
|
min-width: 214px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dual-screen-actions button,
|
||||||
|
.controller-preferences button {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 5px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-preferences {
|
||||||
|
align-items: stretch;
|
||||||
|
align-content: start;
|
||||||
|
grid-template-columns: minmax(0, 1fr) repeat(2, minmax(158px, auto));
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-preferences h3 {
|
||||||
|
color: var(--ink);
|
||||||
|
font-family: var(--pixel-font);
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.35;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-icon-options {
|
||||||
|
gap: 6px;
|
||||||
|
grid-template-columns: minmax(112px, 1fr) repeat(3, minmax(104px, auto));
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-icon-options > span {
|
||||||
|
font-size: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-icon-options button {
|
||||||
|
font-size: 6px;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-style-preview .controller-face-icon {
|
||||||
|
height: 18px;
|
||||||
|
min-width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller-style-preview .controller-face-playstation {
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-bindings-panel .settings-heading {
|
||||||
|
padding: 4px 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-heading > p {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-heading .eyebrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-tabs {
|
||||||
|
gap: 7px;
|
||||||
|
margin: 6px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-tabs button {
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-list {
|
||||||
|
gap: 4px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-list button {
|
||||||
|
min-height: 24px;
|
||||||
|
padding: 3px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-list button > span {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-list kbd {
|
||||||
|
font-size: 6px;
|
||||||
|
min-height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-list .controller-face-icon {
|
||||||
|
font-size: 10px;
|
||||||
|
height: 17px;
|
||||||
|
min-width: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.binding-list .controller-face-playstation {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-footer {
|
||||||
|
font-size: 13px;
|
||||||
|
margin-top: 6px;
|
||||||
|
padding-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-footer .text-button {
|
||||||
|
padding: 6px 9px;
|
||||||
|
}
|
||||||
|
|
||||||
.dungeon-card {
|
.dungeon-card {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
grid-template-columns: 72px 1fr;
|
grid-template-columns: 72px 1fr;
|
||||||
|
|||||||
+1
-1
@@ -456,7 +456,7 @@ function App() {
|
|||||||
const canShowCloudSync = account.id !== -1 && cloudSync.available
|
const canShowCloudSync = account.id !== -1 && cloudSync.available
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={`game-shell ${screen === 'dungeons' || screen === 'raids' ? 'dungeon-shell' : ''} ${screen === 'customize' ? 'workshop-shell' : ''}`}>
|
<main className={`game-shell ${screen === 'dungeons' || screen === 'raids' ? 'dungeon-shell' : ''} ${screen === 'customize' ? 'workshop-shell' : ''} ${screen === 'settings' ? 'settings-shell' : ''}`}>
|
||||||
<header className="topbar app-header">
|
<header className="topbar app-header">
|
||||||
<button
|
<button
|
||||||
className="brand-button"
|
className="brand-button"
|
||||||
|
|||||||
@@ -91,33 +91,28 @@ export function SettingsScreen({ onBack }: { onBack: () => void }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="content-screen settings-screen">
|
<section className="content-screen settings-screen">
|
||||||
<div className="screen-heading">
|
<div className="settings-nav">
|
||||||
<div>
|
<button className="back-button settings-back-button" onClick={onBack} type="button">Back</button>
|
||||||
<p className="eyebrow">Game Options</p>
|
<nav className="settings-tabs" role="tablist" aria-label="Settings sections">
|
||||||
<h1>Settings</h1>
|
{([
|
||||||
</div>
|
{ key: 'display', label: 'Display' },
|
||||||
<button className="back-button" onClick={onBack} type="button">Back</button>
|
{ key: 'input', label: 'Input' },
|
||||||
|
{ key: 'bindings', label: 'Bindings' },
|
||||||
|
] as const).map((tab) => (
|
||||||
|
<button
|
||||||
|
aria-selected={settingsTab === tab.key}
|
||||||
|
className={settingsTab === tab.key ? 'selected' : ''}
|
||||||
|
key={tab.key}
|
||||||
|
onClick={() => setSettingsTab(tab.key)}
|
||||||
|
role="tab"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="settings-tabs" role="tablist" aria-label="Settings sections">
|
|
||||||
{([
|
|
||||||
{ key: 'display', label: 'Display' },
|
|
||||||
{ key: 'input', label: 'Input' },
|
|
||||||
{ key: 'bindings', label: 'Bindings' },
|
|
||||||
] as const).map((tab) => (
|
|
||||||
<button
|
|
||||||
aria-selected={settingsTab === tab.key}
|
|
||||||
className={settingsTab === tab.key ? 'selected' : ''}
|
|
||||||
key={tab.key}
|
|
||||||
onClick={() => setSettingsTab(tab.key)}
|
|
||||||
role="tab"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{tab.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{settingsTab === 'display' && (
|
{settingsTab === 'display' && (
|
||||||
<section className="dual-screen-settings settings-tab-panel">
|
<section className="dual-screen-settings settings-tab-panel">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user