platform: passed data at lib, desktop user attention, check existing file on share at android

This commit is contained in:
ardocrat 2024-09-12 21:27:37 +03:00
parent dd45f7ce38
commit d78ec570b0
6 changed files with 172 additions and 134 deletions

View file

@ -42,8 +42,6 @@ pub struct App<Platform> {
/// Flag to check if it's first draw.
first_draw: bool,
/// Flag to check if attention required after window focus.
attention_required: bool,
}
impl<Platform: PlatformCallbacks> App<Platform> {
@ -52,14 +50,13 @@ impl<Platform: PlatformCallbacks> App<Platform> {
platform,
content: Content::default(),
resize_direction: None,
first_draw: true,
attention_required: false,
first_draw: true
}
}
/// Draw application content.
pub fn ui(&mut self, ctx: &Context) {
// Set Desktop platform context on first draw.
// Set platform context on first draw.
if self.first_draw {
if View::is_desktop() {
self.platform.set_context(ctx);
@ -113,22 +110,17 @@ impl<Platform: PlatformCallbacks> App<Platform> {
}
// Provide incoming data to wallets.
if let Some(data) = self.platform.consume_data() {
if let Some(data) = crate::consume_passed_data() {
if !data.is_empty() {
self.content.wallets.on_data(ui, Some(data), &self.platform);
}
self.attention_required = true;
}
});
// Check if desktop window was focused after requested attention.
if self.attention_required && View::is_desktop()
&& ctx.input(|i| i.viewport().focused.unwrap_or(true)) {
self.attention_required = false;
ctx.send_viewport_cmd(
ViewportCommand::RequestUserAttention(egui::UserAttentionType::Reset)
);
ctx.send_viewport_cmd(ViewportCommand::WindowLevel(egui::WindowLevel::Normal));
if self.platform.user_attention_required() &&
ctx.input(|i| i.viewport().focused.unwrap_or(true)) {
self.platform.clear_user_attention();
}
}

View file

@ -30,7 +30,11 @@ use crate::gui::platform::PlatformCallbacks;
/// Android platform implementation.
#[derive(Clone)]
pub struct Android {
/// Android related state.
android_app: AndroidApp,
/// Context to repaint content and handle viewport commands.
ctx: Arc<RwLock<Option<egui::Context>>>,
}
impl Android {
@ -38,6 +42,7 @@ impl Android {
pub fn new(app: AndroidApp) -> Self {
Self {
android_app: app,
ctx: Arc::new(RwLock::new(None)),
}
}
@ -56,6 +61,11 @@ impl Android {
}
impl PlatformCallbacks for Android {
fn set_context(&mut self, ctx: &egui::Context) {
let mut w_ctx = self.ctx.write();
*w_ctx = Some(ctx.clone());
}
fn show_keyboard(&self) {
// Disable NDK soft input show call before fix for egui.
// self.android_app.show_soft_input(false);
@ -131,9 +141,12 @@ impl PlatformCallbacks for Android {
cache.push("images");
std::fs::create_dir_all(cache.to_str().unwrap())?;
cache.push(name);
let mut image = File::create_new(cache.clone()).unwrap();
image.write_all(data.as_slice()).unwrap();
image.sync_all().unwrap();
if cache.exists() {
std::fs::remove_file(cache.clone())?;
}
let mut image = File::create_new(cache.clone())?;
image.write_all(data.as_slice())?;
image.sync_all()?;
// Call share modal at system.
let vm = unsafe { jni::JavaVM::from_raw(self.android_app.vm_as_ptr() as _) }.unwrap();
let env = vm.attach_current_thread().unwrap();
@ -168,9 +181,13 @@ impl PlatformCallbacks for Android {
None
}
fn consume_data(&mut self) -> Option<String> {
None
fn request_user_attention(&self) {}
fn user_attention_required(&self) -> bool {
false
}
fn clear_user_attention(&self) {}
}
lazy_static! {

View file

@ -27,10 +27,14 @@ use crate::gui::platform::PlatformCallbacks;
/// Desktop platform related actions.
#[derive(Clone)]
pub struct Desktop {
/// Flag to check if camera stop is needed.
stop_camera: Arc<AtomicBool>,
/// Context to repaint content and handle viewport commands.
ctx: Arc<RwLock<Option<egui::Context>>>,
/// Flag to check if camera stop is needed.
stop_camera: Arc<AtomicBool>,
/// Flag to check if attention required after window focusing.
attention_required: Arc<AtomicBool>,
}
impl PlatformCallbacks for Desktop {
@ -120,38 +124,7 @@ impl PlatformCallbacks for Desktop {
None
}
fn consume_data(&mut self) -> Option<String> {
let has_data = {
let r_data = PASSED_DATA.read();
r_data.is_some()
};
if has_data {
// Clear data.
let mut w_data = PASSED_DATA.write();
let data = w_data.clone();
*w_data = None;
return data;
}
None
}
}
impl Desktop {
/// Create new instance with provided extra data from app opening.
pub fn new(data: Option<String>) -> Self {
let mut w_data = PASSED_DATA.write();
*w_data = data;
Self {
stop_camera: Arc::new(AtomicBool::new(false)),
ctx: Arc::new(RwLock::new(None)),
}
}
/// Handle data passed to application.
pub fn on_data(&self, data: String) {
let mut w_data = PASSED_DATA.write();
*w_data = Some(data);
fn request_user_attention(&self) {
let r_ctx = self.ctx.read();
if r_ctx.is_some() {
let ctx = r_ctx.as_ref().unwrap();
@ -170,6 +143,33 @@ impl Desktop {
}
ctx.request_repaint();
}
self.attention_required.store(true, Ordering::Relaxed);
}
fn user_attention_required(&self) -> bool {
self.attention_required.load(Ordering::Relaxed)
}
fn clear_user_attention(&self) {
let r_ctx = self.ctx.read();
if r_ctx.is_some() {
let ctx = r_ctx.as_ref().unwrap();
ctx.send_viewport_cmd(
ViewportCommand::RequestUserAttention(UserAttentionType::Reset)
);
ctx.send_viewport_cmd(ViewportCommand::WindowLevel(WindowLevel::Normal));
}
self.attention_required.store(false, Ordering::Relaxed);
}
}
impl Desktop {
pub fn new() -> Self {
Self {
stop_camera: Arc::new(AtomicBool::new(false)),
ctx: Arc::new(RwLock::new(None)),
attention_required: Arc::new(AtomicBool::new(false)),
}
}
#[allow(dead_code)]
@ -255,7 +255,4 @@ impl Desktop {
lazy_static! {
/// Last captured image from started camera.
static ref LAST_CAMERA_IMAGE: Arc<RwLock<Option<(Vec<u8>, u32)>>> = Arc::new(RwLock::new(None));
/// Data passed from deeplink or opened file.
static ref PASSED_DATA: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
}

View file

@ -35,5 +35,7 @@ pub trait PlatformCallbacks {
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error>;
fn pick_file(&self) -> Option<String>;
fn picked_file(&self) -> Option<String>;
fn consume_data(&mut self) -> Option<String>;
fn request_user_attention(&self);
fn user_attention_required(&self) -> bool;
fn clear_user_attention(&self);
}

View file

@ -17,6 +17,9 @@ extern crate rust_i18n;
use eframe::NativeOptions;
use egui::{Context, Stroke};
use lazy_static::lazy_static;
use std::sync::Arc;
use parking_lot::RwLock;
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;
@ -256,3 +259,30 @@ fn setup_i18n() {
}
}
}
/// Get data provided from deeplink or opened file.
pub fn consume_passed_data() -> Option<String> {
let has_data = {
let r_data = PASSED_DATA.read();
r_data.is_some()
};
if has_data {
// Clear data.
let mut w_data = PASSED_DATA.write();
let data = w_data.clone();
*w_data = None;
return data;
}
None
}
/// Provide data from deeplink or opened file.
pub fn on_data(data: String) {
let mut w_data = PASSED_DATA.write();
*w_data = Some(data);
}
lazy_static! {
/// Data provided from deeplink or opened file.
pub static ref PASSED_DATA: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
}

View file

@ -41,11 +41,6 @@ fn real_main() {
data = content
}
// Check if another app instance already running.
if is_app_running(&data) {
return;
}
// Setup callback on panic crash.
std::panic::set_hook(Box::new(|info| {
let backtrace = backtrace::Backtrace::new();
@ -67,7 +62,14 @@ fn real_main() {
// Start GUI.
match std::panic::catch_unwind(|| {
start_desktop_gui(data);
if is_app_running(&data) {
return;
} else if let Some(data) = data {
grim::on_data(data);
}
let platform = grim::gui::platform::Desktop::new();
start_app_socket(platform.clone());
start_desktop_gui(platform);
}) {
Ok(_) => {}
Err(e) => println!("{:?}", e)
@ -77,7 +79,7 @@ fn real_main() {
/// Start GUI with Desktop related setup passing data from opening.
#[allow(dead_code)]
#[cfg(not(target_os = "android"))]
fn start_desktop_gui(data: Option<String>) {
fn start_desktop_gui(platform: grim::gui::platform::Desktop) {
use grim::AppConfig;
use dark_light::Mode;
@ -127,15 +129,6 @@ fn start_desktop_gui(data: Option<String>) {
eframe::Renderer::Wgpu
};
let mut platform = grim::gui::platform::Desktop::new(data);
// Start app socket at separate thread.
let socket_pl = platform.clone();
platform = socket_pl.clone();
std::thread::spawn(move || {
start_app_socket(socket_pl);
});
// Start GUI.
let app = grim::gui::App::new(platform.clone());
match grim::start(options.clone(), grim::app_creator(app)) {
@ -176,16 +169,19 @@ fn is_app_running(data: &Option<String>) -> bool {
let socket_path = grim::Settings::socket_path();
let name = if GenericNamespaced::is_supported() {
"grim.sock".to_ns_name::<GenericNamespaced>()?
grim::Settings::SOCKET_NAME.to_ns_name::<GenericNamespaced>()?
} else {
socket_path.clone().to_fs_name::<GenericFilePath>()?
};
// Connect to running application socket.
let conn = Stream::connect(name).await?;
let data = data.clone().unwrap_or("".to_string());
if data.is_empty() {
return Ok(());
}
let (rec, mut sen) = conn.split();
// Send data to socket.
let data = data.clone().unwrap_or("".to_string());
let _ = sen.write_all(data.as_bytes()).await;
drop((rec, sen));
@ -201,6 +197,7 @@ fn is_app_running(data: &Option<String>) -> bool {
#[allow(dead_code)]
#[cfg(not(target_os = "android"))]
fn start_app_socket(platform: grim::gui::platform::Desktop) {
std::thread::spawn(move || {
use tor_rtcompat::BlockOn;
let runtime = tor_rtcompat::tokio::TokioNativeTlsRuntime::create().unwrap();
let _: Result<_, _> = runtime
@ -213,6 +210,7 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) {
use tokio::{
io::{AsyncBufReadExt, BufReader},
};
use grim::gui::platform::PlatformCallbacks;
// Handle incoming connection.
async fn handle_conn(conn: Stream)
@ -226,7 +224,7 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) {
let socket_path = grim::Settings::socket_path();
let name = if GenericNamespaced::is_supported() {
"grim.sock".to_ns_name::<GenericNamespaced>()?
grim::Settings::SOCKET_NAME.to_ns_name::<GenericNamespaced>()?
} else {
socket_path.clone().to_fs_name::<GenericFilePath>()?
};
@ -244,7 +242,6 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) {
x => x?,
};
// Handle connections.
loop {
let conn = match listener.accept().await {
Ok(c) => c,
@ -253,13 +250,16 @@ fn start_app_socket(platform: grim::gui::platform::Desktop) {
continue
}
};
// Handle connection.
let res = handle_conn(conn).await;
match res {
Ok(data) => {
platform.on_data(data)
grim::on_data(data);
platform.request_user_attention();
},
Err(_) => {}
}
}
});
});
}