diff --git a/app/build.gradle b/app/build.gradle index 26063d0..2edad42 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,12 +8,10 @@ android { defaultConfig { applicationId "mw.gri.android" - minSdk 28 + minSdk 27 targetSdk 33 versionCode 1 versionName "1.0" - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -23,10 +21,7 @@ android { } debug { minifyEnabled false - //packagingOptions { - // doNotStrip '**/*.so' - //} - //debuggable true + debuggable true } } compileOptions { @@ -40,10 +35,6 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.3' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.3' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' // To use the Android Frame Pacing library //implementation "androidx.games:games-frame-pacing:1.9.1" diff --git a/app/src/main/java/mw/gri/android/MainActivity.java b/app/src/main/java/mw/gri/android/MainActivity.java index da6ccb0..8e01af0 100644 --- a/app/src/main/java/mw/gri/android/MainActivity.java +++ b/app/src/main/java/mw/gri/android/MainActivity.java @@ -1,5 +1,6 @@ package mw.gri.android; +import android.graphics.Color; import android.os.Bundle; import android.system.ErrnoException; import android.system.Os; @@ -18,14 +19,11 @@ public class MainActivity extends GameActivity { } catch (ErrnoException e) { throw new RuntimeException(e); } - +// getDisplayCutouts(); super.onCreate(savedInstanceState); - - int navBarHeight = Utils.getNavigationBarHeight(getApplicationContext()); - findViewById(android.R.id.content).setPadding(0, 0, 0, navBarHeight); } - public Integer getStatusBarHeight() { - return Utils.getStatusBarHeight(getApplicationContext()); + public int[] getDisplayCutouts() { + return Utils.getDisplayCutouts(this); } } \ No newline at end of file diff --git a/app/src/main/java/mw/gri/android/Utils.java b/app/src/main/java/mw/gri/android/Utils.java index 2725c9d..be417aa 100644 --- a/app/src/main/java/mw/gri/android/Utils.java +++ b/app/src/main/java/mw/gri/android/Utils.java @@ -1,75 +1,52 @@ package mw.gri.android; -import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; import android.os.Build; -import android.view.Display; +import android.view.DisplayCutout; import android.view.WindowInsets; import android.view.WindowManager; +import androidx.core.graphics.Insets; +import androidx.core.view.DisplayCutoutCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; public class Utils { - public static int getStatusBarHeight(Context context) { - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + public static int[] getDisplayCutouts(Activity context) { + int[] cutouts = new int[]{0, 0, 0, 0}; if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - return windowManager - .getCurrentWindowMetrics() - .getWindowInsets() - .getInsets(WindowInsets.Type.navigationBars()) - .bottom; + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets(); + android.graphics.Insets barsInsets = insets.getInsets(WindowInsets.Type.systemBars()); + android.graphics.Insets cutoutsInsets = insets.getInsets(WindowInsets.Type.displayCutout()); + cutouts[0] = pxToDp(Integer.max(barsInsets.top, cutoutsInsets.top), context); + cutouts[1] = pxToDp(Integer.max(barsInsets.right, cutoutsInsets.right), context); + cutouts[2] = pxToDp(Integer.max(barsInsets.bottom, cutoutsInsets.bottom), context); + cutouts[3] = pxToDp(Integer.max(barsInsets.left, cutoutsInsets.left), context); + } else if (Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.Q) { + DisplayCutout displayCutout = context.getWindowManager().getDefaultDisplay().getCutout(); + cutouts[0] = displayCutout.getSafeInsetBottom(); + cutouts[1] = displayCutout.getSafeInsetRight(); + cutouts[2] = displayCutout.getSafeInsetBottom(); + cutouts[3] = displayCutout.getSafeInsetLeft(); } else { - Resources res = context.getResources(); - int statusBarHeight = 24; - @SuppressLint({"DiscouragedApi", "InternalInsetResource"}) - int resourceId = res.getIdentifier("status_bar_height", "dimen", "android"); - if (resourceId > 0) { - statusBarHeight = res.getDimensionPixelSize(resourceId); + WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(context.getWindow().getDecorView()); + if (insets != null) { + DisplayCutoutCompat displayCutout = insets.getDisplayCutout(); + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + if (displayCutout != null) { + cutouts[0] = pxToDp(Integer.max(displayCutout.getSafeInsetTop(), systemBars.top), context); + cutouts[1] = pxToDp(Integer.max(displayCutout.getSafeInsetRight(), systemBars.right), context); + cutouts[2] = pxToDp(Integer.max(displayCutout.getSafeInsetBottom(), systemBars.bottom), context); + cutouts[3] = pxToDp(Integer.max(displayCutout.getSafeInsetLeft(), systemBars.left), context); + } } - return statusBarHeight; } + return cutouts; } - public static int getNavigationBarHeight(Context context) { - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - return windowManager - .getCurrentWindowMetrics() - .getWindowInsets() - .getInsets(WindowInsets.Type.navigationBars()) - .bottom; - } else { - Point appUsableSize = getAppUsableScreenSize(context); - Point realScreenSize = getRealScreenSize(context); - - // navigation bar on the side - if (appUsableSize.x < realScreenSize.x) { - return appUsableSize.y; - } - - // navigation bar at the bottom - if (appUsableSize.y < realScreenSize.y) { - return realScreenSize.y - appUsableSize.y; - } - - // navigation bar is not present - return 0; - } - } - - private static Point getAppUsableScreenSize(Context context) { - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - Point size = new Point(); - windowManager.getDefaultDisplay().getSize(size); - return size; - } - - private static Point getRealScreenSize(Context context) { - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - Display display = windowManager.getDefaultDisplay(); - Point size = new Point(); - display.getRealSize(size); - return size; + private static int pxToDp(int px, Context context) { + return (int) (px / context.getResources().getDisplayMetrics().density); } } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 554c178..f900d41 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,7 +1,6 @@ - #FFFEF102 - #FFFEF102 - #FF000000 #FFFFFFFF + #FFFEF102 + #FF000000 \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 19b3ae1..efc90ee 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,13 +1,17 @@ - \ No newline at end of file diff --git a/fonts/jura.ttf b/fonts/jura.ttf deleted file mode 100644 index 0617e0d..0000000 Binary files a/fonts/jura.ttf and /dev/null differ diff --git a/fonts/roboto.ttf b/fonts/roboto.ttf new file mode 100644 index 0000000..e7307e7 Binary files /dev/null and b/fonts/roboto.ttf differ diff --git a/src/grim.rs b/src/grim.rs index 893debb..568f4fc 100644 --- a/src/grim.rs +++ b/src/grim.rs @@ -17,13 +17,13 @@ use log::LevelFilter::{Debug, Info, Trace, Warn}; #[cfg(target_os = "android")] use winit::platform::android::activity::AndroidApp; -use eframe::{AppCreator, CreationContext, NativeOptions, Renderer}; -use crate::gui::{MainApp}; +use eframe::{AppCreator, NativeOptions, Renderer}; +use crate::gui::PlatformApp; #[allow(dead_code)] #[cfg(target_os = "android")] #[no_mangle] -fn android_main(app: AndroidApp) { +unsafe fn android_main(app: AndroidApp) { #[cfg(debug_assertions)] { std::env::set_var("RUST_BACKTRACE", "full"); @@ -31,6 +31,7 @@ fn android_main(app: AndroidApp) { android_logger::Config::default().with_max_level(Info).with_tag("grim"), ); } + let _app = app.clone(); use winit::platform::android::EventLoopBuilderExtAndroid; let mut options = NativeOptions::default(); @@ -38,9 +39,9 @@ fn android_main(app: AndroidApp) { builder.with_android_app(app); })); - use crate::gui::AndroidUi; - start(options, Box::new(|_cc| Box::new(MainApp::new(_cc) - .with_status_bar_height(24.0) + use crate::gui::platform::app::Android; + start(options, Box::new(|_cc| Box::new( + PlatformApp::new(_cc, Android::new(_app)) ))); } @@ -54,7 +55,7 @@ fn main() { .init(); let options = NativeOptions::default(); - start(options, Box::new(|_cc| Box::new(MainApp::new(_cc)))); + start(options, Box::new(|_cc| Box::new(App::new(_cc)))); } fn start(mut options: NativeOptions, app_creator: AppCreator) { diff --git a/src/gui/app.rs b/src/gui/app.rs index c6cd797..dddeebe 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -12,55 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::FontTweak; - -#[derive(Default)] -pub struct MainApp { - root: egui_demo_lib::DemoWindows, - status_bar_height: Option -} - -impl MainApp { - pub fn new(cc: &eframe::CreationContext<'_>) -> Self { - setup_fonts(&cc.egui_ctx); - Self::default() - } -} - -#[cfg(target_os = "android")] -impl crate::gui::AndroidUi for MainApp { - fn with_status_bar_height(mut self, value: f32) -> Self { - self.status_bar_height = Some(value); - self - } -} - -impl eframe::App for MainApp { - fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { - egui::TopBottomPanel::top("top_padding_panel") - .resizable(false) - .exact_height(self.status_bar_height.unwrap_or(0.0)) - .show(ctx, |ui| {}); - self.root.ui(ctx); - } -} - -fn setup_fonts(ctx: &egui::Context) { - let mut fonts = egui::FontDefinitions::default(); - fonts.font_data.insert( - "jura".to_owned(), - egui::FontData::from_static(include_bytes!( - "../../fonts/jura.ttf" - )).tweak(FontTweak { - scale: 1.0, - y_offset_factor: -0.25, - y_offset: 0.0 - }), - ); - fonts - .families - .entry(egui::FontFamily::Proportional) - .or_default() - .insert(0, "jura".to_owned()); - ctx.set_fonts(fonts); +pub struct PlatformApp { + pub(crate) root: egui_demo_lib::DemoWindows, + // root: super::views::main + pub(crate) platform: Platform, } \ No newline at end of file diff --git a/src/gui/mod.rs b/src/gui/mod.rs index c0f877b..3ebb6ee 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2023 The Grin Developers +// Copyright 2023 The Grim Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,11 +13,7 @@ // limitations under the License. mod app; -pub mod root; +pub use crate::gui::app::PlatformApp; -pub use app::MainApp; - -#[cfg(target_os = "android")] -pub trait AndroidUi { - fn with_status_bar_height(self, value: f32) -> Self; -} \ No newline at end of file +pub mod views; +pub mod platform; \ No newline at end of file diff --git a/src/gui/platform/android/mod.rs b/src/gui/platform/android/mod.rs new file mode 100644 index 0000000..d5d7f34 --- /dev/null +++ b/src/gui/platform/android/mod.rs @@ -0,0 +1,177 @@ +// Copyright 2023 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use egui::{Color32, FontTweak, Visuals}; +use egui::epaint::Shadow; +use jni::objects::{JObject, JPrimitiveArray}; +use crate::gui::PlatformApp; + +use winit::platform::android::activity::AndroidApp; + +pub struct Android { + pub android_app: AndroidApp, + pub cutouts: [i32; 4], + pub window_size: [f32; 2] +} + +impl Android { + pub fn new(app: AndroidApp) -> Self { + Android { + android_app: app, + cutouts: Default::default(), + window_size: Default::default() + } + } +} + +impl PlatformApp { + pub fn new(cc: &eframe::CreationContext<'_>, platform: Android) -> Self { + setup_fonts(&cc.egui_ctx); + // cc.egui_ctx.set_visuals(Visuals { + // dark_mode: false, + // override_text_color: None, + // widgets: Default::default(), + // selection: Default::default(), + // hyperlink_color: Default::default(), + // faint_bg_color: Default::default(), + // extreme_bg_color: Default::default(), + // code_bg_color: Default::default(), + // warn_fg_color: Default::default(), + // error_fg_color: Default::default(), + // window_rounding: Default::default(), + // window_shadow: Default::default(), + // window_fill: Default::default(), + // window_stroke: Default::default(), + // panel_fill: Default::default(), + // popup_shadow: Default::default(), + // resize_corner_size: 0.0, + // text_cursor_width: 0.0, + // text_cursor_preview: false, + // clip_rect_margin: 0.0, + // button_frame: false, + // collapsing_header_frame: false, + // }); + Self { + root: Default::default(), + platform, + } + } +} + +fn setup_fonts(ctx: &egui::Context) { + let mut fonts = egui::FontDefinitions::default(); + fonts.font_data.insert( + "roboto".to_owned(), + egui::FontData::from_static(include_bytes!( + "../../../../fonts/roboto.ttf" + )).tweak(FontTweak { + scale: 1.0, + y_offset_factor: -0.20, + y_offset: 0.0 + }), + ); + fonts + .families + .entry(egui::FontFamily::Proportional) + .or_default() + .insert(0, "roboto".to_owned()); + ctx.set_fonts(fonts); +} + +impl eframe::App for PlatformApp { + fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + println!("12345 - X {}", frame.info().window_info.size.x); + println!("12345 - Y {}", frame.info().window_info.size.y); + + let _x = frame.info().window_info.size.x; + let _y = frame.info().window_info.size.y; + if _x != self.platform.window_size[0] || _y != self.platform.window_size[1] { + self.platform.window_size[0] = _x; + self.platform.window_size[1] = _y; + self.platform.cutouts = get_display_cutouts(&self.platform.android_app); + } + + egui::TopBottomPanel::top("top_padding_panel") + .frame(egui::Frame { + shadow: Shadow::NONE, + fill: Color32::TRANSPARENT, + ..Default::default() + }) + .show_separator_line(false) + .resizable(false) + .exact_height(self.platform.cutouts[0] as f32) + .show(ctx, |ui| {}); + + egui::SidePanel::right("right_padding_panel") + .frame(egui::Frame { + shadow: Shadow::NONE, + fill: Color32::TRANSPARENT, + ..Default::default() + }) + .show_separator_line(false) + .resizable(false) + .default_width(self.platform.cutouts[1] as f32) + .show(ctx, |ui| {}); + + egui::TopBottomPanel::bottom("bottom_padding_panel") + .frame(egui::Frame { + shadow: Shadow::NONE, + fill: Color32::TRANSPARENT, + ..Default::default() + }) + .show_separator_line(false) + .resizable(false) + .exact_height(self.platform.cutouts[2] as f32) + .show(ctx, |ui| {}); + + egui::SidePanel::left("left_padding_panel") + .frame(egui::Frame { + shadow: Shadow::NONE, + fill: Color32::TRANSPARENT, + ..Default::default() + }) + .show_separator_line(false) + .resizable(false) + .default_width(self.platform.cutouts[3] as f32) + .show(ctx, |ui| {}); + + egui::CentralPanel::default().show(ctx, |ui| { + self.root.ui(ctx); + }); + } +} + +fn get_display_cutouts(app: &AndroidApp) -> [i32; 4] { + let vm = unsafe { jni::JavaVM::from_raw(app.vm_as_ptr() as _) }.unwrap(); + let mut env = vm.attach_current_thread().unwrap(); + let activity = unsafe { + JObject::from_raw(app.activity_as_ptr() as jni::sys::jobject) + }; + let _res = env + .call_method( + activity, + "getDisplayCutouts", + "()[I", + &[], + ) + .unwrap(); + let mut array: [i32; 4] = [0; 4]; + let object: jni::sys::jobject = unsafe { _res.as_jni().l }; + unsafe { + env.get_int_array_region(JPrimitiveArray::from( + JObject::from_raw(object)), 0, array.as_mut() + ).unwrap(); + } + array +} diff --git a/src/gui/platform/desktop/mod.rs b/src/gui/platform/desktop/mod.rs new file mode 100644 index 0000000..f816e75 --- /dev/null +++ b/src/gui/platform/desktop/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2023 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::gui::PlatformApp; + +#[derive(Default)] +pub struct Desktop; + +impl PlatformApp { + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { + Self::default() + } +} diff --git a/src/gui/platform/mod.rs b/src/gui/platform/mod.rs new file mode 100644 index 0000000..81f07e5 --- /dev/null +++ b/src/gui/platform/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2023 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(target_os = "android")] +#[path = "android/mod.rs"] +pub mod app; +#[cfg(not(target_os = "android"))] +#[path = "desktop/mod.rs"] +pub mod app; + diff --git a/src/gui/root.rs b/src/gui/root.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/gui/views/main.rs b/src/gui/views/main.rs new file mode 100644 index 0000000..6ddbed9 --- /dev/null +++ b/src/gui/views/main.rs @@ -0,0 +1,14 @@ +// Copyright 2023 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs new file mode 100644 index 0000000..81e68b2 --- /dev/null +++ b/src/gui/views/mod.rs @@ -0,0 +1,14 @@ +// Copyright 2023 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +pub mod main;