android: fix exit on background

This commit is contained in:
ardocrat 2023-06-13 23:45:03 +03:00
parent f95645ea81
commit 23712ca361
5 changed files with 83 additions and 36 deletions

View file

@ -28,8 +28,13 @@ public class BackgroundService extends Service {
NotificationManager manager = getSystemService(NotificationManager.class);
manager.notify(SYNC_STATUS_NOTIFICATION_ID, mNotificationBuilder.build());
if (exitAppAfterNodeStop()) {
sendBroadcast(new Intent(MainActivity.FINISH_ACTIVITY_ACTION));
mStopped = true;
}
if (!mStopped) {
mHandler.postDelayed(this, 500);
mHandler.postDelayed(this, 300);
}
}
};
@ -127,4 +132,5 @@ public class BackgroundService extends Service {
private native String getSyncStatusText();
private native String getSyncTitle();
private native boolean exitAppAfterNodeStop();
}

View file

@ -1,5 +1,9 @@
package mw.gri.android;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Process;
@ -13,10 +17,21 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class MainActivity extends GameActivity {
public static String FINISH_ACTIVITY_ACTION = "MainActivity.finish";
static {
System.loadLibrary("grim");
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent i) {
if (i.getAction().equals(FINISH_ACTIVITY_ACTION)) {
onExit();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// Setup HOME environment variable for native code configurations.
@ -40,6 +55,9 @@ public class MainActivity extends GameActivity {
}
onDisplayCutoutsChanged(Utils.getDisplayCutouts(this));
// Register receiver to finish activity from the BackgroundService.
registerReceiver(mBroadcastReceiver, new IntentFilter(FINISH_ACTIVITY_ACTION));
// Start notification service.
BackgroundService.start(this);
}
@ -62,10 +80,12 @@ public class MainActivity extends GameActivity {
@Override
protected void onDestroy() {
unregisterReceiver(mBroadcastReceiver);
if (!mManualExit) {
onTermination();
}
// Temp fix: kill process after 3 seconds to prevent app hanging at next launch
// Temp fix: kill process after 3 seconds to prevent app hanging at next launch.
new Thread(() -> {
try {
Thread.sleep(3000);
@ -80,8 +100,12 @@ public class MainActivity extends GameActivity {
mActivityDestroyed.set(true);
}
// Called from native code
// Called from native code.
public void onExit() {
// Return if exit was already requested.
if (mManualExit) {
return;
}
mManualExit = true;
BackgroundService.stop(this);
finish();

View file

@ -15,9 +15,10 @@
use egui::{Context, Stroke};
use egui::os::OperatingSystem;
use crate::gui::Colors;
use crate::gui::{Colors, Navigator};
use crate::gui::platform::PlatformCallbacks;
use crate::gui::screens::Root;
use crate::node::Node;
pub struct PlatformApp<Platform> {
pub(crate) app: App,
@ -132,3 +133,28 @@ impl App {
}
}
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Java_mw_gri_android_MainActivity_onBackButtonPress(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
_activity: jni::objects::JObject,
) {
Navigator::back();
}
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
/// Calling on unexpected Android application termination (removal from recent apps).
pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
_activity: jni::objects::JObject,
) {
Node::stop(false);
}

View file

@ -80,12 +80,9 @@ impl Root {
}
ui.add_space(16.0);
ui.vertical_centered(|ui| {
Spinner::new().size(48.0).color(Colors::GRAY).ui(ui);
Spinner::new().size(48.0).color(Colors::GOLD).ui(ui);
ui.add_space(12.0);
ui.label(RichText::new(t!("sync_status.shutdown"))
.size(17.0)
.color(Colors::TEXT)
);
ui.label(t!("sync_status.shutdown"));
});
ui.add_space(10.0);
} else {
@ -103,7 +100,7 @@ impl Root {
App::exit(frame, cb);
modal.close();
} else {
Node::stop();
Node::stop(true);
modal.disable_closing();
self.show_exit_progress = true;
}
@ -123,9 +120,9 @@ impl Root {
}
fn show_current_screen(&mut self,
ui: &mut egui::Ui,
frame: &mut eframe::Frame,
cb: &dyn PlatformCallbacks) {
ui: &mut egui::Ui,
frame: &mut eframe::Frame,
cb: &dyn PlatformCallbacks) {
let Self { screens, .. } = self;
for screen in screens.iter_mut() {
if Navigator::is_current(&screen.id()) {
@ -146,16 +143,4 @@ impl Root {
};
(is_panel_open, panel_width)
}
}
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Java_mw_gri_android_MainActivity_onBackButtonPress(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
_activity: jni::objects::JObject,
) {
Navigator::back();
}

View file

@ -25,7 +25,7 @@ use grin_config::config;
use grin_core::global;
use grin_core::global::ChainTypes;
use grin_servers::{Server, ServerStats};
use jni::sys::jstring;
use jni::sys::{jboolean, jstring};
use lazy_static::lazy_static;
use log::info;
@ -46,6 +46,8 @@ pub struct Node {
restart_needed: AtomicBool,
/// Thread flag to stop the server.
stop_needed: AtomicBool,
/// Flag to check if app exit is needed after server stop.
exit_after_stop: AtomicBool
}
impl Default for Node {
@ -56,14 +58,16 @@ impl Default for Node {
starting: AtomicBool::new(false),
restart_needed: AtomicBool::new(false),
stop_needed: AtomicBool::new(false),
exit_after_stop: AtomicBool::new(false)
}
}
}
impl Node {
/// Stop the [`Server`].
pub fn stop() {
/// Stop the [`Server`] and setup exit flag after if needed.
pub fn stop(exit_after_stop: bool) {
NODE_STATE.stop_needed.store(true, Ordering::Relaxed);
NODE_STATE.exit_after_stop.store(exit_after_stop, Ordering::Relaxed);
}
/// Start [`Server`] with provided chain type.
@ -190,14 +194,14 @@ impl Node {
/// Get synchronization status i18n text.
pub fn get_sync_status_text() -> String {
if Node::is_starting() {
return t!("sync_status.initial")
};
if Node::is_stopping() {
return t!("sync_status.shutdown")
};
if Node::is_starting() {
return t!("sync_status.initial")
};
if Node::is_restarting() {
return t!("sync_status.server_restarting")
}
@ -440,11 +444,13 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle(
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
/// Calling on unexpected Android application termination (removal from recent apps).
pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination(
/// Check if app exit is needed after node stop.
pub extern "C" fn Java_mw_gri_android_BackgroundService_exitAppAfterNodeStop(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
_activity: jni::objects::JObject,
) {
Node::stop();
) -> jboolean {
let exit_after_stop = NODE_STATE.exit_after_stop.load(Ordering::Relaxed);
let is_app_exit_needed = !Node::is_running() && exit_after_stop;
return is_app_exit_needed as jboolean;
}