android-native: auto hunt, in-client account registration, mobile UI polish

- Auto hunt (client-only): PythonPlayerAutoHunt.cpp + AutoHuntScript.inc as the
  embedded mt_autohunt module, AUTO touch button, F9/F8.
- Account registration: tools/40250/register_server.py (rc.d mt_register, :11080)
  inserts into account.account; net.RegisterAccount/GetRegisterAccountResult
  (PythonAccountRegister.cpp) and the mt_register login-window dialog
  (RegisterScript.inc).
- --py-exec also runs in --login-screen mode for scripted login-screen tests.
- Touch controller, window manager and render command updates for the
  Android native client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-29 16:15:04 +09:00
co-authored by Claude Opus 5.5
parent 53d06c3c26
commit 3e3708ef06
31 changed files with 2975 additions and 140 deletions
@@ -5,6 +5,10 @@ import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.HapticFeedbackConstants;
import android.view.RoundedCorner;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
@@ -35,6 +39,11 @@ public class MainActivity extends SDLActivity {
applyImmersiveMode();
}
// Called from native code (touch_controller long press).
public void performHaptic() {
runOnUiThread(() -> getWindow().getDecorView().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS));
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
@@ -75,9 +84,45 @@ public class MainActivity extends SDLActivity {
Log.i("Metin2Args", "native args: " + args);
if (args == null || args.trim().isEmpty())
args = DEFAULT_ARGS;
if (!args.contains("--safe-inset-px"))
args = args.trim() + " --safe-inset-px " + safeInsetPx();
// Test phase: FPS readout in the top-left corner. Drop this line for release builds.
if (!args.contains("--show-fps"))
args = args.trim() + " --show-fps";
return args.trim().split("\\s+");
}
/**
* Pixels the game UI keeps clear at the left and right edges in landscape: the largest rounded
* screen corner (a window hugging a corner is clipped until it is a full radius in) and any
* display cutout. The 3D view still fills the screen.
*/
private int safeInsetPx() {
int inset = 0;
final Display display = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ? getDisplay()
: getWindowManager().getDefaultDisplay();
if (display == null)
return 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
final int[] positions = { RoundedCorner.POSITION_TOP_LEFT, RoundedCorner.POSITION_TOP_RIGHT,
RoundedCorner.POSITION_BOTTOM_LEFT, RoundedCorner.POSITION_BOTTOM_RIGHT };
for (int position : positions) {
final RoundedCorner corner = display.getRoundedCorner(position);
if (corner != null)
inset = Math.max(inset, corner.getRadius());
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// The activity may not be landscape yet: any side's cutout can end up left or right.
final DisplayCutout cutout = display.getCutout();
if (cutout != null)
inset = Math.max(inset, Math.max(Math.max(cutout.getSafeInsetLeft(), cutout.getSafeInsetRight()),
Math.max(cutout.getSafeInsetTop(), cutout.getSafeInsetBottom())));
}
Log.i("Metin2Args", "safe inset px: " + inset);
return inset;
}
@Override
public void setOrientationBis(int w, int h, boolean resizable, String hint) {
// Enforce landscape-only on mobile devices (sensorLandscape allows flipping 180 deg but never portrait).