mirror of
https://github.com/jwinterm/grim.git
synced 2026-10-25 16:08:32 +01:00
android: migrate back to gameactivity, update to API 36, fix back navigation at network panel
This commit is contained in:
+31
-4
@@ -12,8 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use egui::epaint::RectShape;
|
||||
use egui::{Align, Context, CornerRadius, CursorIcon, LayerId, Layout, Modifiers, Order, ResizeDirection, Stroke, StrokeKind, UiBuilder, ViewportCommand};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::gui::icons::{ARROWS_IN, ARROWS_OUT, CARET_DOWN, MOON, SUN, X};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
@@ -22,6 +24,11 @@ use crate::gui::views::{Content, KeyboardContent, Modal, TitlePanel, View};
|
||||
use crate::gui::Colors;
|
||||
use crate::AppConfig;
|
||||
|
||||
lazy_static! {
|
||||
/// State to check if platform Back button was pressed.
|
||||
static ref BACK_BUTTON_PRESSED: AtomicBool = AtomicBool::new(false);
|
||||
}
|
||||
|
||||
/// Implements ui entry point and contains platform-specific callbacks.
|
||||
pub struct App<Platform> {
|
||||
/// Handles platform-specific functionality.
|
||||
@@ -63,10 +70,15 @@ impl<Platform: PlatformCallbacks> App<Platform> {
|
||||
self.first_draw = false;
|
||||
}
|
||||
|
||||
// Handle Esc keyboard key event.
|
||||
if ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape) ||
|
||||
// Handle Esc keyboard key event and platform Back button key event.
|
||||
let back_pressed = BACK_BUTTON_PRESSED.load(Ordering::Relaxed);
|
||||
if back_pressed || ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape) ||
|
||||
i.consume_key(Modifiers::NONE, egui::Key::BrowserBack)) {
|
||||
self.content.on_back(&self.platform);
|
||||
// Pass event to content.
|
||||
self.content.on_back(ctx, &self.platform);
|
||||
if back_pressed {
|
||||
BACK_BUTTON_PRESSED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
// Request repaint to update previous content.
|
||||
ctx.request_repaint();
|
||||
}
|
||||
@@ -134,7 +146,9 @@ impl<Platform: PlatformCallbacks> App<Platform> {
|
||||
false
|
||||
};
|
||||
if keyboard_showing {
|
||||
ctx.move_to_top(LayerId::new(Order::Middle, egui::Id::new(KeyboardContent::WINDOW_ID)));
|
||||
ctx.move_to_top(
|
||||
LayerId::new(Order::Middle, egui::Id::new(KeyboardContent::WINDOW_ID))
|
||||
);
|
||||
}
|
||||
}
|
||||
// Reset keyboard state for newly opened modal.
|
||||
@@ -397,4 +411,17 @@ impl<Platform: PlatformCallbacks> eframe::App for App<Platform> {
|
||||
}
|
||||
Colors::TRANSPARENT.to_normalized_gamma_f32()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Handle Back key code event from Android.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onBack(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
_activity: jni::objects::JObject,
|
||||
) {
|
||||
BACK_BUTTON_PRESSED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
@@ -146,9 +146,15 @@ impl Content {
|
||||
pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal";
|
||||
|
||||
/// Called to navigate back, return `true` if action was not consumed.
|
||||
pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool {
|
||||
pub fn on_back(&mut self, ctx: &egui::Context, cb: &dyn PlatformCallbacks) -> bool {
|
||||
if Modal::on_back() {
|
||||
if self.wallets.on_back(cb) {
|
||||
let dual_panel = Self::is_dual_panel_mode(ctx);
|
||||
if !dual_panel && Self::is_network_panel_open() {
|
||||
if self.network.on_back() {
|
||||
Self::toggle_network_panel();
|
||||
return false;
|
||||
}
|
||||
} else if self.wallets.on_back(cb) {
|
||||
Self::show_exit_modal();
|
||||
return false;
|
||||
}
|
||||
|
||||
+89
-11
@@ -12,8 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use egui::{Layout, TextBuffer, TextStyle, Widget, Align};
|
||||
use std::sync::Arc;
|
||||
use egui::{Layout, TextBuffer, TextStyle, Widget, Align, ViewportCommand};
|
||||
use egui::text_edit::TextEditState;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
@@ -24,11 +28,11 @@ use crate::gui::views::{KeyboardEvent, View};
|
||||
pub struct TextEdit {
|
||||
/// View identifier.
|
||||
id: egui::Id,
|
||||
/// Check if horizontal centering is needed.
|
||||
/// Horizontal text centering is needed.
|
||||
h_center: bool,
|
||||
/// Check if focus is needed.
|
||||
/// Focus is needed.
|
||||
focus: bool,
|
||||
/// Check if focus request was passed.
|
||||
/// Focus request was passed.
|
||||
focus_request: bool,
|
||||
/// Hide letters and draw button to show/hide letters.
|
||||
password: bool,
|
||||
@@ -38,9 +42,9 @@ pub struct TextEdit {
|
||||
paste: bool,
|
||||
/// Show button to scan QR code into text.
|
||||
scan_qr: bool,
|
||||
/// Callback when scan button was pressed.
|
||||
/// Scan button was pressed.
|
||||
pub scan_pressed: bool,
|
||||
/// Callback when Enter key was pressed.
|
||||
/// Tab or Enter keys were pressed to focus on next line.
|
||||
pub enter_pressed: bool,
|
||||
/// Flag to enter only numbers.
|
||||
numeric: bool,
|
||||
@@ -65,7 +69,7 @@ impl TextEdit {
|
||||
scan_pressed: false,
|
||||
enter_pressed: false,
|
||||
numeric: false,
|
||||
no_soft_keyboard: false,
|
||||
no_soft_keyboard: is_android(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +155,7 @@ impl TextEdit {
|
||||
|
||||
// Reset keyboard state for newly focused.
|
||||
if clicked || self.focus_request {
|
||||
ui.ctx().send_viewport_cmd(ViewportCommand::IMEAllowed(true));
|
||||
KeyboardContent::reset_window_state();
|
||||
}
|
||||
|
||||
@@ -160,9 +165,10 @@ impl TextEdit {
|
||||
data.insert_temp(focused_input_id, self.id);
|
||||
});
|
||||
self.enter_pressed = self.on_soft_input(ui, self.id, false, input);
|
||||
// Check Enter key input.
|
||||
// Check Enter or Tab keys press.
|
||||
if !self.focus_request {
|
||||
if ui.ctx().input(|i| i.key_pressed(egui::Key::Enter)) {
|
||||
if ui.ctx().input(|i| i.key_pressed(egui::Key::Enter) ||
|
||||
i.key_pressed(egui::Key::Tab)) {
|
||||
self.enter_pressed = true;
|
||||
}
|
||||
}
|
||||
@@ -175,12 +181,24 @@ impl TextEdit {
|
||||
}
|
||||
});
|
||||
});
|
||||
// Repaint on Android to handle input from Java code without delays.
|
||||
if is_android() {
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply soft keyboard input data to provided String, returns `true` if Enter was pressed.
|
||||
fn on_soft_input(&self, ui: &mut egui::Ui, id: egui::Id, multiline: bool, value: &mut String)
|
||||
-> bool {
|
||||
if let Some(input) = KeyboardContent::consume_event() {
|
||||
let event: Option<KeyboardEvent> = if is_android() {
|
||||
let mut w_input = LAST_SOFT_KEYBOARD_EVENT.write();
|
||||
w_input.take()
|
||||
} else {
|
||||
KeyboardContent::consume_event()
|
||||
};
|
||||
|
||||
// Handle keyboard input event.
|
||||
if let Some(e) = event {
|
||||
let mut enter_pressed = false;
|
||||
let mut state = TextEditState::load(ui.ctx(), id).unwrap();
|
||||
match state.cursor.char_range() {
|
||||
@@ -194,7 +212,7 @@ impl TextEdit {
|
||||
r.secondary.index as f32) as usize;
|
||||
let end_select = f32::max(r.primary.index as f32,
|
||||
r.secondary.index as f32) as usize;
|
||||
match input {
|
||||
match e {
|
||||
KeyboardEvent::TEXT(text) => {
|
||||
if selected {
|
||||
*value = {
|
||||
@@ -318,4 +336,64 @@ impl TextEdit {
|
||||
self.no_soft_keyboard = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if current system is Android.
|
||||
fn is_android() -> bool {
|
||||
egui::os::OperatingSystem::from_target_os() == egui::os::OperatingSystem::Android
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref LAST_SOFT_KEYBOARD_EVENT: Arc<RwLock<Option<KeyboardEvent>>> = Arc::new(RwLock::new(None));
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Callback from Java code with last entered character from soft keyboard.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onTextInput(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
char: jni::sys::jstring
|
||||
) {
|
||||
use jni::objects::JString;
|
||||
|
||||
unsafe {
|
||||
let j_obj = JString::from_raw(char);
|
||||
let j_str = _env.get_string_unchecked(j_obj.as_ref()).unwrap();
|
||||
match j_str.to_str() {
|
||||
Ok(str) => {
|
||||
let mut w_input = LAST_SOFT_KEYBOARD_EVENT.write();
|
||||
*w_input = Some(KeyboardEvent::TEXT(str.to_string()));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Callback from Java code when Clear key was pressed at soft keyboard.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onClearInput(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
) {
|
||||
let mut w_input = LAST_SOFT_KEYBOARD_EVENT.write();
|
||||
*w_input = Some(KeyboardEvent::CLEAR);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "android")]
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
/// Callback from Java code when Enter key was pressed at soft keyboard.
|
||||
pub extern "C" fn Java_mw_gri_android_MainActivity_onEnterInput(
|
||||
_env: jni::JNIEnv,
|
||||
_class: jni::objects::JObject,
|
||||
) {
|
||||
let mut w_input = LAST_SOFT_KEYBOARD_EVENT.write();
|
||||
*w_input = Some(KeyboardEvent::ENTER);
|
||||
}
|
||||
@@ -213,6 +213,16 @@ impl NetworkContent {
|
||||
}
|
||||
}
|
||||
|
||||
/// Navigate back, return `true` if action was not consumed.
|
||||
pub fn on_back(&mut self) -> bool {
|
||||
if self.showing_settings() {
|
||||
// Close settings.
|
||||
self.settings_content = None;
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Check if application settings content is showing.
|
||||
pub fn showing_settings(&self) -> bool {
|
||||
self.settings_content.is_some()
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::time::Duration;
|
||||
use egui::scroll_area::ScrollBarVisibility;
|
||||
use egui::{Align, CornerRadius, Id, Layout, Margin, RichText, ScrollArea, StrokeKind};
|
||||
use std::time::Duration;
|
||||
|
||||
use egui::os::OperatingSystem;
|
||||
use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_OPEN, FOLDER_PLUS, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::settings::SettingsContent;
|
||||
@@ -130,6 +130,12 @@ impl ContentContainer for WalletsContent {
|
||||
}
|
||||
|
||||
fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||
ui.ctx().request_repaint_after(if OperatingSystem::from_target_os() == OperatingSystem::Android {
|
||||
Duration::from_millis(100)
|
||||
} else {
|
||||
Duration::from_millis(1000)
|
||||
});
|
||||
|
||||
if let Some(data) = crate::consume_incoming_data() {
|
||||
if !data.is_empty() {
|
||||
self.on_data(ui, Some(data), cb);
|
||||
@@ -221,9 +227,6 @@ impl ContentContainer for WalletsContent {
|
||||
..Default::default()
|
||||
})
|
||||
.show_animated_inside(ui, !list_hidden, |ui| {
|
||||
if !dual_panel && !showing_wallet {
|
||||
ui.ctx().request_repaint_after(Duration::from_millis(1000));
|
||||
}
|
||||
// Show wallet list.
|
||||
self.wallet_list_ui(ui, cb);
|
||||
});
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
use egui::scroll_area::ScrollBarVisibility;
|
||||
use egui::{Id, Margin, RichText, ScrollArea};
|
||||
use grin_chain::SyncStatus;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::gui::icons::{ARROWS_CLOCKWISE, FILE_ARROW_DOWN, FILE_ARROW_UP, GEAR_FINE, POWER, STACK};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
@@ -83,12 +82,6 @@ impl WalletContentContainer for WalletContent {
|
||||
}
|
||||
|
||||
fn container_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) {
|
||||
if self.account_content.can_back() {
|
||||
ui.ctx().request_repaint();
|
||||
} else {
|
||||
ui.ctx().request_repaint_after(Duration::from_millis(1000));
|
||||
}
|
||||
|
||||
let dual_panel = Content::is_dual_panel_mode(ui.ctx());
|
||||
let show_wallets_dual = AppConfig::show_wallets_at_dual_panel();
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ fn android_main(app: AndroidApp) {
|
||||
{
|
||||
std::env::set_var("RUST_BACKTRACE", "full");
|
||||
let log_config = android_logger::Config::default()
|
||||
.with_max_level(log::LevelFilter::Info)
|
||||
.with_max_level(log::LevelFilter::Debug)
|
||||
.with_tag("grim");
|
||||
android_logger::init_once(log_config);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user