mirror of
https://github.com/doukutsu-rs/doukutsu-rs.git
synced 2026-08-24 13:02:49 -07:00
Make the app always run in landscape mode and use insets
[-] Generate debug builds for only two arches
This commit is contained in:
@@ -64,6 +64,10 @@ android {
|
||||
resValue("string", "app_name", "doukutsu-rs (debug)")
|
||||
|
||||
isJniDebuggable = true
|
||||
ndk {
|
||||
// ARM for testing on a physical device, and x86_64 for testing on the emulator
|
||||
abiFilters.addAll(listOf("arm64-v8a", "x86_64"))
|
||||
}
|
||||
|
||||
val applicationId = defaultConfig.applicationId!!
|
||||
val documentsAuthorityValue = "$applicationId$applicationIdSuffix.documents"
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:exported="true"
|
||||
android:launchMode="standard"
|
||||
android:resizeableActivity="false"
|
||||
android:screenOrientation="sensorLandscape">
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
|
||||
@@ -2,20 +2,21 @@ package io.github.doukutsu_rs;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
import android.app.NativeActivity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.view.OrientationEventListener;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.io.File;
|
||||
|
||||
public class GameActivity extends org.libsdl.app.SDLActivity {
|
||||
private int[] displayInsets = new int[]{0, 0, 0, 0};
|
||||
|
||||
@Override
|
||||
protected String[] getLibraries() {
|
||||
return new String[] {
|
||||
@@ -24,6 +25,40 @@ public class GameActivity extends org.libsdl.app.SDLActivity {
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
this.updateCutouts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
this.updateCutouts();
|
||||
}
|
||||
|
||||
private void updateCutouts() {
|
||||
Arrays.fill(this.displayInsets, 0);
|
||||
|
||||
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
|
||||
if (insets != null) {
|
||||
this.displayInsets[0] = Math.max(this.displayInsets[0], insets.getStableInsetLeft());
|
||||
this.displayInsets[1] = Math.max(this.displayInsets[1], insets.getStableInsetTop());
|
||||
this.displayInsets[2] = Math.max(this.displayInsets[2], insets.getStableInsetRight());
|
||||
this.displayInsets[3] = Math.max(this.displayInsets[3], insets.getStableInsetBottom());
|
||||
}
|
||||
|
||||
if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||
DisplayCutout cutout = insets.getDisplayCutout();
|
||||
if (cutout != null) {
|
||||
this.displayInsets[0] = Math.max(this.displayInsets[0], cutout.getSafeInsetLeft());
|
||||
this.displayInsets[1] = Math.max(this.displayInsets[0], cutout.getSafeInsetTop());
|
||||
this.displayInsets[2] = Math.max(this.displayInsets[0], cutout.getSafeInsetRight());
|
||||
this.displayInsets[3] = Math.max(this.displayInsets[0], cutout.getSafeInsetBottom());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void openDir(String path) {
|
||||
var uri = DocumentsContract.buildDocumentUri(BuildConfig.DOCUMENTS_AUTHORITY, path);
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
"controller": {
|
||||
"entry": "Controller...",
|
||||
"keyboard": "Keyboard",
|
||||
"touch_controls": "Touch controls"
|
||||
"touch_controls_or_keyboard": "Touch controls/Keyboard"
|
||||
},
|
||||
"rebind": "Rebind...",
|
||||
"rebind_menu": {
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
"controller": {
|
||||
"entry": "コントローラ",
|
||||
"keyboard": "キーボード",
|
||||
"touch_controls": "タッチコントロール"
|
||||
"touch_controls_or_keyboard": "タタッチコントロール/キーボードッチ操作/キーボード"
|
||||
},
|
||||
"rebind": "再バインド",
|
||||
"rebind_menu": {
|
||||
|
||||
@@ -156,3 +156,10 @@ pub enum SpriteBatchCommand {
|
||||
DrawRectTinted(Rect<f32>, Rect<f32>, Color),
|
||||
DrawRectFlipTinted(Rect<f32>, Rect<f32>, bool, bool, Color),
|
||||
}
|
||||
|
||||
pub fn get_scaled_size(width: u32, height: u32) -> (f32, f32) {
|
||||
let scaled_height = ((height / 480).max(1) * 480) as f32;
|
||||
let scaled_width = (width as f32 * (scaled_height as f32 / height as f32)).floor();
|
||||
|
||||
(scaled_width, scaled_height)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use imgui::{DrawCmdParams, DrawData, DrawIdx, DrawVert};
|
||||
use winit::window::Icon;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand};
|
||||
use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand, get_scaled_size};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::framework::filesystem;
|
||||
@@ -155,7 +155,8 @@ fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
|
||||
|
||||
vm_env.delete_local_ref(JObject::from_raw(field));
|
||||
|
||||
//Game always runs with horizontal orientation so top and bottom cutouts not needed and only wastes piece of the screen
|
||||
// The app always run in landscape mode, so the top and bottom cutouts are redundant
|
||||
// and only waste part of the screen, causing UI elements to overlap.
|
||||
elements[1] = 0;
|
||||
elements[3] = 0;
|
||||
|
||||
@@ -163,13 +164,6 @@ fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_scaled_size(width: u32, height: u32) -> (f32, f32) {
|
||||
let scaled_height = ((height / 480).max(1) * 480) as f32;
|
||||
let scaled_width = (width as f32 * (scaled_height as f32 / height as f32)).floor();
|
||||
|
||||
(scaled_width, scaled_height)
|
||||
}
|
||||
|
||||
impl BackendEventLoop for GlutinEventLoop {
|
||||
fn run(&mut self, game: &mut Game, ctx: &mut Context) {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
@@ -28,7 +28,7 @@ use sdl2::{controller, keyboard, pixels, EventPump, GameControllerSubsystem, Sdl
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::backend::{
|
||||
Backend, BackendEventLoop, BackendGamepad, BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand,
|
||||
VertexData, WindowParams,
|
||||
VertexData, WindowParams, get_scaled_size
|
||||
};
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::{GameError, GameResult};
|
||||
@@ -169,13 +169,15 @@ impl SDL2EventLoop {
|
||||
|
||||
if cfg!(target_os = "android") {
|
||||
gl_attr.set_context_profile(GLProfile::GLES);
|
||||
gl_attr.set_context_version(2, 0);
|
||||
} else {
|
||||
gl_attr.set_context_profile(GLProfile::Compatibility);
|
||||
gl_attr.set_context_version(2, 1);
|
||||
}
|
||||
gl_attr.set_context_version(2, 1);
|
||||
|
||||
let mut win_builder = video.window("Cave Story (doukutsu-rs)", ctx.window.size_hint.0 as _, ctx.window.size_hint.1 as _);
|
||||
win_builder.position_centered();
|
||||
#[cfg(not(target_os = "android"))]
|
||||
win_builder.resizable();
|
||||
|
||||
if ctx.window.mode.is_fullscreen() {
|
||||
@@ -221,11 +223,31 @@ impl SDL2EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_scaled_size(width: u32, height: u32) -> (f32, f32) {
|
||||
let scaled_height = ((height / 480).max(1) * 480) as f32;
|
||||
let scaled_width = (width as f32 * (scaled_height as f32 / height as f32)).floor();
|
||||
#[cfg(target_os = "android")]
|
||||
fn get_insets() -> GameResult<(f32, f32, f32, f32)> {
|
||||
unsafe {
|
||||
use jni::objects::JObject;
|
||||
use jni::JavaVM;
|
||||
|
||||
(scaled_width, scaled_height)
|
||||
let vm_ptr = ndk_context::android_context().vm().cast();
|
||||
let vm = JavaVM::from_raw(vm_ptr)?;
|
||||
let vm_env = vm.attach_current_thread()?;
|
||||
|
||||
let class = vm_env.new_global_ref(JObject::from_raw(ndk_context::android_context().context().cast()))?;
|
||||
let field = vm_env.get_field(class.as_obj(), "displayInsets", "[I")?.to_jni().l as jni::sys::jintArray;
|
||||
|
||||
let mut elements = [0; 4];
|
||||
vm_env.get_int_array_region(field, 0, &mut elements)?;
|
||||
|
||||
vm_env.delete_local_ref(JObject::from_raw(field));
|
||||
|
||||
// The app always run in landscape mode, so the top and bottom cutouts are redundant
|
||||
// and only waste part of the screen, causing UI elements to overlap.
|
||||
elements[1] = 0;
|
||||
elements[3] = 0;
|
||||
|
||||
Ok((elements[0] as f32, elements[1] as f32, elements[2] as f32, elements[3] as f32))
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendEventLoop for SDL2EventLoop {
|
||||
@@ -464,6 +486,18 @@ impl BackendEventLoop for SDL2EventLoop {
|
||||
|
||||
game.update(ctx).unwrap();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
match get_insets() {
|
||||
Ok(insets) => {
|
||||
ctx.screen_insets = insets;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to update insets: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_) = &state.next_scene {
|
||||
game.scene = mem::take(&mut state.next_scene);
|
||||
game.scene.as_mut().unwrap().init(state, ctx).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user