android: show sync progress at notification, prevent app to sleep, temp fix of hanging after closing from recent apps

This commit is contained in:
ardocrat 2023-05-23 01:46:42 +03:00
parent f05d930f60
commit 22c5b945c6
20 changed files with 426 additions and 234 deletions

1
Cargo.lock generated
View file

@ -1736,6 +1736,7 @@ dependencies = [
"grin_servers",
"grin_util",
"jni",
"lazy_static",
"log",
"once_cell",
"openssl-sys",

View file

@ -46,6 +46,7 @@ once_cell = "1.10.0"
rust-i18n = "1.1.4"
sys-locale = "0.3.0"
chrono = "0.4.23"
lazy_static = "1.4.0"
[patch.crates-io]
winit = { git = "https://github.com/rib/winit", branch = "android-activity" }
@ -63,5 +64,5 @@ jni = "0.21.1"
winit = { version = "0.27.2", features = [ "android-game-activity" ] }
[lib]
name="grim_android"
name="grim"
crate_type=["cdylib"]

View file

@ -9,7 +9,7 @@ android {
defaultConfig {
applicationId "mw.gri.android"
minSdk 24
targetSdk 31
targetSdk 33
versionCode 1
versionName "0.1.0"
}

View file

@ -2,28 +2,35 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Grim"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Main">
android:hardwareAccelerated="true"
android:largeHeap="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Grim"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Main">
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode|keyboard"
android:exported="true">
android:launchMode="singleTask"
android:name=".MainActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode|keyboard"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="grim_android" />
<meta-data android:name="android.app.lib_name" android:value="grim" />
</activity>
<service android:name=".BackgroundService" android:stopWithTask="true" />
</application>
</manifest>

View file

@ -0,0 +1,131 @@
package mw.gri.android;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.os.*;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import java.util.List;
public class BackgroundService extends Service {
private static final String TAG = BackgroundService.class.getSimpleName();
private PowerManager.WakeLock mWakeLock;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private boolean mStopped = false;
private static final int SYNC_STATUS_NOTIFICATION_ID = 1;
private NotificationCompat.Builder mNotificationBuilder;
private final Runnable mUpdateSyncStatus = new Runnable() {
@Override
public void run() {
mNotificationBuilder.setContentText(getSyncStatusText());
NotificationManager manager = getSystemService(NotificationManager.class);
manager.notify(SYNC_STATUS_NOTIFICATION_ID, mNotificationBuilder.build());
if (!mStopped) {
mHandler.postDelayed(this, 500);
}
}
};
@Override
public void onCreate() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.acquire();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel = new NotificationChannel(
TAG, TAG, NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
Intent i = getPackageManager().getLaunchIntentForPackage(this.getPackageName());
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_IMMUTABLE);
mNotificationBuilder = new NotificationCompat.Builder(this, TAG)
.setContentTitle(this.getSyncTitle())
.setContentText(this.getSyncStatusText())
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent);
Notification notification = mNotificationBuilder.build();
startForeground(SYNC_STATUS_NOTIFICATION_ID, notification);
mHandler.post(mUpdateSyncStatus);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
onStop();
super.onTaskRemoved(rootIntent);
}
@Override
public void onDestroy() {
onStop();
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public void onStop() {
mStopped = true;
if (mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
mHandler.removeCallbacks(mUpdateSyncStatus);
stopForeground(Service.STOP_FOREGROUND_REMOVE);
stopSelf();
}
public static void start(Context context) {
if (!isServiceRunning(context)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, BackgroundService.class));
} else {
context.startService(new Intent(context, BackgroundService.class));
}
}
}
public static void stop(Context context) {
context.stopService(new Intent(context, BackgroundService.class));
}
private static boolean isServiceRunning(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (ActivityManager.RunningServiceInfo runningServiceInfo : services) {
if (runningServiceInfo.service.getClassName().equals(BackgroundService.class.getName())) {
return true;
}
}
return false;
}
private native String getSyncStatusText();
private native String getSyncTitle();
}

View file

@ -1,7 +1,7 @@
package mw.gri.android;
import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
import android.system.ErrnoException;
import android.system.Os;
import com.google.androidgamesdk.GameActivity;
@ -9,7 +9,7 @@ import com.google.androidgamesdk.GameActivity;
public class MainActivity extends GameActivity {
static {
System.loadLibrary("grim_android");
System.loadLibrary("grim");
}
@Override
@ -20,9 +20,29 @@ public class MainActivity extends GameActivity {
throw new RuntimeException(e);
}
super.onCreate(savedInstanceState);
BackgroundService.start(getApplicationContext());
}
@Override
protected void onDestroy() {
if (!mManualExit) {
BackgroundService.stop(getApplicationContext());
// Temporary fix to prevent app hanging when closed from recent apps
Process.killProcess(Process.myPid());
}
super.onDestroy();
}
public int[] getDisplayCutouts() {
return Utils.getDisplayCutouts(this);
}
private boolean mManualExit = false;
// Called from native code
public void onExit() {
mManualExit = true;
BackgroundService.stop(getApplicationContext());
finish();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -14,7 +14,7 @@ export CPPFLAGS="-DMDB_USE_ROBUST=0" && export CFLAGS="-DMDB_USE_ROBUST=0" && ca
if [ $? -eq 0 ]
then
yes | cp -f target/aarch64-linux-android/${type}/libgrim_android.so app/src/main/jniLibs/arm64-v8a
yes | cp -f target/aarch64-linux-android/${type}/libgrim.so app/src/main/jniLibs/arm64-v8a
./gradlew clean
./gradlew build
#./gradlew installDebug

View file

@ -22,7 +22,7 @@ use crate::gui::PlatformApp;
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[no_mangle]
unsafe fn android_main(app: AndroidApp) {
fn android_main(app: AndroidApp) {
#[cfg(debug_assertions)]
{
std::env::set_var("RUST_BACKTRACE", "full");

View file

@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use eframe::epaint::{Color32, Stroke};
mod views;
pub use self::views::View;
@ -24,11 +22,13 @@ mod network;
mod network_node;
mod network_tuning;
mod network_metrics;
mod network_mining;
pub use self::network::Network;
pub trait NetworkTab {
fn name(&self) -> &String;
fn ui(&mut self, ui: &mut egui::Ui, node: &mut crate::node::Node);
fn ui(&mut self, ui: &mut egui::Ui);
}

View file

@ -42,8 +42,6 @@ enum Mode {
}
pub struct Network {
node: Node,
current_mode: Mode,
node_view: NetworkNode,
@ -52,9 +50,8 @@ pub struct Network {
impl Default for Network {
fn default() -> Self {
let node = Node::new(ChainTypes::Mainnet, true);
Node::start(ChainTypes::Mainnet);
Self {
node,
current_mode: Mode::Node,
node_view: NetworkNode::default(),
metrics_view: NetworkMetrics::default()
@ -67,7 +64,7 @@ impl Network {
ui: &mut egui::Ui,
frame: &mut eframe::Frame,
nav: &mut Navigator,
cb: &dyn PlatformCallbacks) {
_: &dyn PlatformCallbacks) {
egui::TopBottomPanel::top("network_title")
.resizable(false)
@ -136,13 +133,13 @@ impl Network {
fn draw_tab_content(&mut self, ui: &mut egui::Ui) {
match self.current_mode {
Mode::Node => {
self.node_view.ui(ui, &mut self.node);
self.node_view.ui(ui);
}
Mode::Metrics => {
self.metrics_view.ui(ui, &mut self.node);
self.metrics_view.ui(ui);
}
Mode::Tuning => {
self.node_view.ui(ui, &mut self.node);
self.node_view.ui(ui);
}
Mode::Miner => {}
}
@ -215,23 +212,12 @@ impl Network {
strip.cell(|ui| {
ui.centered_and_justified(|ui| {
// Select sync status text
let sync_status = self.node.state.get_sync_status();
let status_text = if self.node.state.is_restarting() {
t!("server_restarting")
} else {
match sync_status {
None => {
t!("server_down")
}
Some(ss) => {
get_sync_status_text(ss).to_string()
}
}
};
let sync_status = Node::get_sync_status();
let status_text = Node::get_sync_status_text(sync_status);
// Setup text color animation based on sync status
let idle = match sync_status {
None => { !self.node.state.is_starting() }
None => { !Node::is_starting() }
Some(ss) => { ss == SyncStatus::NoSync }
};
let (dark, bright) = (0.3, 1.0);
@ -267,72 +253,3 @@ impl Network {
}
}
fn get_sync_status_text(sync_status: SyncStatus) -> String {
match sync_status {
SyncStatus::Initial => t!("sync_status.initial"),
SyncStatus::NoSync => t!("sync_status.no_sync"),
SyncStatus::AwaitingPeers(_) => t!("sync_status.awaiting_peers"),
SyncStatus::HeaderSync {
sync_head,
highest_height,
..
} => {
if highest_height == 0 {
t!("sync_status.header_sync")
} else {
let percent = sync_head.height * 100 / highest_height;
t!("sync_status.header_sync_percent", "percent" => percent)
}
}
SyncStatus::TxHashsetDownload(stat) => {
if stat.total_size > 0 {
let percent = stat.downloaded_size * 100 / stat.total_size;
t!("sync_status.tx_hashset_download_percent", "percent" => percent)
} else {
t!("sync_status.tx_hashset_download")
}
}
SyncStatus::TxHashsetSetup => {
t!("sync_status.tx_hashset_setup")
}
SyncStatus::TxHashsetRangeProofsValidation {
rproofs,
rproofs_total,
} => {
let r_percent = if rproofs_total > 0 {
(rproofs * 100) / rproofs_total
} else {
0
};
t!("sync_status.tx_hashset_range_proofs_validation", "percent" => r_percent)
}
SyncStatus::TxHashsetKernelsValidation {
kernels,
kernels_total,
} => {
let k_percent = if kernels_total > 0 {
(kernels * 100) / kernels_total
} else {
0
};
t!("sync_status.tx_hashset_kernels_validation", "percent" => k_percent)
}
SyncStatus::TxHashsetSave | SyncStatus::TxHashsetDone => {
t!("sync_status.tx_hashset_save")
}
SyncStatus::BodySync {
current_height,
highest_height,
} => {
if highest_height == 0 {
t!("sync_status.body_sync")
} else {
let percent = current_height * 100 / highest_height;
t!("sync_status.body_sync_percent", "percent" => percent)
}
}
SyncStatus::Shutdown => t!("sync_status.shutdown"),
}
}

View file

@ -18,7 +18,7 @@ use egui::{RichText, ScrollArea, Spinner, Widget};
use grin_servers::DiffBlock;
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
use crate::gui::icons::{AT, CALENDAR_PLUS, COINS, CUBE, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
use crate::gui::views::{NetworkTab, View};
use crate::node::Node;
@ -43,8 +43,8 @@ impl NetworkTab for NetworkMetrics {
&self.title
}
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
let server_stats = node.state.get_stats();
fn ui(&mut self, ui: &mut egui::Ui) {
let server_stats = Node::get_stats();
// Show loading widget if server is not working or difficulty height is zero.
if !server_stats.is_some() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
ui.centered_and_justified(|ui| {
@ -86,7 +86,7 @@ impl NetworkTab for NetworkMetrics {
});
ui.add_space(6.0);
// Show difficulty window info
// Show difficulty adjustment window info
ui.vertical_centered_justified(|ui| {
let title = t!("difficulty_at_window", "size" => stats.diff_stats.window_size);
View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, title));
@ -114,28 +114,32 @@ impl NetworkTab for NetworkMetrics {
});
ui.add_space(6.0);
// Draw difficulty window blocks
// Show difficulty adjustment window blocks
let blocks_size = stats.diff_stats.last_blocks.len();
ScrollArea::vertical().auto_shrink([false; 2]).stick_to_bottom(true).show_rows(
ui,
DIFF_BLOCK_UI_HEIGHT,
blocks_size,
|ui, row_range| {
for index in row_range {
let db = stats.diff_stats.last_blocks.get(index).unwrap();
let rounding = if blocks_size == 1 {
[true, true]
} else if index == 0 {
[true, false]
} else if index == blocks_size - 1 {
[false, true]
} else {
[false, false]
};
draw_diff_block(ui, db, rounding)
}
},
);
ScrollArea::vertical()
.auto_shrink([false; 2])
.stick_to_bottom(true)
.id_source("diff_scroll")
.show_rows(
ui,
DIFF_BLOCK_UI_HEIGHT,
blocks_size,
|ui, row_range| {
for index in row_range {
let db = stats.diff_stats.last_blocks.get(index).unwrap();
let rounding = if blocks_size == 1 {
[true, true]
} else if index == 0 {
[true, false]
} else if index == blocks_size - 1 {
[false, true]
} else {
[false, false]
};
draw_diff_block(ui, db, rounding)
}
},
);
}
}

View file

@ -0,0 +1,13 @@
// 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.

View file

@ -14,7 +14,6 @@
use eframe::epaint::Stroke;
use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget};
use grin_servers::common::stats::TxStats;
use grin_servers::PeerStats;
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
@ -39,8 +38,8 @@ impl NetworkTab for NetworkNode {
&self.title
}
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
let server_stats = node.state.get_stats();
fn ui(&mut self, ui: &mut egui::Ui) {
let server_stats = Node::get_stats();
if !server_stats.is_some() {
ui.centered_and_justified(|ui| {
Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui);
@ -80,9 +79,9 @@ impl NetworkTab for NetworkNode {
[false, false, true, false]);
});
columns[1].vertical_centered(|ui| {
let ts = stats.header_stats.latest_timestamp;
let h_ts = stats.header_stats.latest_timestamp;
View::rounded_box(ui,
format!("{}", ts.format("%d/%m/%Y %H:%M")),
format!("{}", h_ts.format("%d/%m/%Y %H:%M")),
t!("time_utc"),
[false, false, false, true]);
});
@ -116,9 +115,9 @@ impl NetworkTab for NetworkNode {
[false, false, true, false]);
});
columns[1].vertical_centered(|ui| {
let ts = stats.chain_stats.latest_timestamp;
let b_ts = stats.chain_stats.latest_timestamp;
View::rounded_box(ui,
format!("{}", ts.format("%d/%m/%Y %H:%M")),
format!("{}", b_ts.format("%d/%m/%Y %H:%M")),
t!("time_utc"),
[false, false, false, true]);
});
@ -265,9 +264,7 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) {
});
// Add space after last item
if !rounding[0] && rounding[1] {
ui.add_space(5.0);
} else if rounding[0] && rounding[1] {
ui.add_space(2.0);
if rounding[1] {
ui.add_space(3.0);
}
}

View file

@ -15,4 +15,3 @@
mod node;
pub use self::node::Node;
pub use self::node::NodeState;

View file

@ -25,50 +25,20 @@ use grin_config::config;
use grin_core::global;
use grin_core::global::ChainTypes;
use grin_servers::{Server, ServerStats};
use jni::objects::JString;
use jni::sys::jstring;
use lazy_static::lazy_static;
use log::info;
lazy_static! {
static ref NODE_STATE: Arc<Node> = Arc::new(Node::default());
}
pub struct Node {
/// Node state updated from the separate thread
pub(crate) state: Arc<NodeState>,
}
impl Node {
/// Instantiate new node with provided chain type, start server if needed
pub fn new(chain_type: ChainTypes, start: bool) -> Self {
let state = Arc::new(NodeState::new(chain_type));
if start {
start_server_thread(state.clone(), chain_type);
}
Self { state }
}
/// Stop server
pub fn stop(&self) {
self.state.stop_needed.store(true, Ordering::Relaxed);
}
/// Start server with provided chain type
pub fn start(&self, chain_type: ChainTypes) {
if !self.state.is_running() {
start_server_thread(self.state.clone(), chain_type);
}
}
/// Restart server or start when not running
pub fn restart(&mut self) {
if self.state.is_running() {
self.state.restart_needed.store(true, Ordering::Relaxed);
} else {
self.start(*self.state.chain_type);
}
}
}
pub struct NodeState {
/// Data for UI
stats: Arc<RwLock<Option<ServerStats>>>,
/// Chain type of launched server
chain_type: Arc<ChainTypes>,
chain_type: Arc<RwLock<ChainTypes>>,
/// Indicator if server is starting
starting: AtomicBool,
/// Thread flag to stop the server and start it again
@ -77,102 +47,206 @@ pub struct NodeState {
stop_needed: AtomicBool,
}
impl NodeState {
/// Instantiate new node state with provided chain type and server state
pub fn new(chain_type: ChainTypes) -> Self {
impl Default for Node {
fn default() -> Self {
Self {
stats: Arc::new(RwLock::new(None)),
chain_type: Arc::new(chain_type),
chain_type: Arc::new(RwLock::new(ChainTypes::Mainnet)),
starting: AtomicBool::new(false),
restart_needed: AtomicBool::new(false),
stop_needed: AtomicBool::new(false),
starting: AtomicBool::new(false)
}
}
}
impl Node {
/// Stop server
pub fn stop() {
NODE_STATE.stop_needed.store(true, Ordering::Relaxed);
}
/// Start server with provided chain type
pub fn start(chain_type: ChainTypes) {
if !Self::is_running() {
let mut w_chain_type = NODE_STATE.chain_type.write().unwrap();
*w_chain_type = chain_type;
Self::start_server_thread();
}
}
/// Restart server with provided chain type
pub fn restart(chain_type: ChainTypes) {
if Self::is_running() {
let mut w_chain_type = NODE_STATE.chain_type.write().unwrap();
*w_chain_type = chain_type;
NODE_STATE.restart_needed.store(true, Ordering::Relaxed);
} else {
Node::start(chain_type);
}
}
/// Check if server is starting
pub fn is_starting(&self) -> bool {
self.starting.load(Ordering::Relaxed)
pub fn is_starting() -> bool {
NODE_STATE.starting.load(Ordering::Relaxed)
}
/// Check if server is running
pub fn is_running(&self) -> bool {
self.get_stats().is_some() || self.is_starting()
pub fn is_running() -> bool {
Self::get_stats().is_some() || Self::is_starting()
}
/// Check if server is stopping
pub fn is_stopping(&self) -> bool {
self.stop_needed.load(Ordering::Relaxed)
pub fn is_stopping() -> bool {
NODE_STATE.stop_needed.load(Ordering::Relaxed)
}
/// Check if server is restarting
pub fn is_restarting(&self) -> bool {
self.restart_needed.load(Ordering::Relaxed)
pub fn is_restarting() -> bool {
NODE_STATE.restart_needed.load(Ordering::Relaxed)
}
/// Get server stats
pub fn get_stats(&self) -> RwLockReadGuard<'_, Option<ServerStats>> {
self.stats.read().unwrap()
pub fn get_stats() -> RwLockReadGuard<'static, Option<ServerStats>> {
NODE_STATE.stats.read().unwrap()
}
/// Get server sync status, empty when server is not running
pub fn get_sync_status(&self) -> Option<SyncStatus> {
pub fn get_sync_status() -> Option<SyncStatus> {
// return Shutdown status when node is stopping
if self.is_stopping() {
if Self::is_stopping() {
return Some(SyncStatus::Shutdown)
}
// return Initial status when node is starting
if self.is_starting() {
if Self::is_starting() {
return Some(SyncStatus::Initial)
}
let stats = self.get_stats();
let stats = Self::get_stats();
// return sync status when server is running (stats are not empty)
if stats.is_some() {
return Some(stats.as_ref().unwrap().sync_status)
}
None
}
}
/// Start a thread to launch server and update node state with server stats
fn start_server_thread(state: Arc<NodeState>, chain_type: ChainTypes) -> JoinHandle<()> {
thread::spawn(move || {
state.starting.store(true, Ordering::Relaxed);
let mut server = start_server(&chain_type);
let mut first_start = true;
/// Start a thread to launch server and update state with server stats
fn start_server_thread() -> JoinHandle<()> {
thread::spawn(move || {
NODE_STATE.starting.store(true, Ordering::Relaxed);
loop {
if state.is_restarting() {
server.stop();
let mut server = start_server(&NODE_STATE.chain_type.read().unwrap());
let mut first_start = true;
// Create new server with current chain type
server = start_server(&state.chain_type);
loop {
if Self::is_restarting() {
server.stop();
state.restart_needed.store(false, Ordering::Relaxed);
} else if state.is_stopping() {
server.stop();
// Create new server with current chain type
server = start_server(&NODE_STATE.chain_type.read().unwrap());
let mut w_stats = state.stats.write().unwrap();
*w_stats = None;
NODE_STATE.restart_needed.store(false, Ordering::Relaxed);
} else if Self::is_stopping() {
server.stop();
state.stop_needed.store(false, Ordering::Relaxed);
break;
} else {
let stats = server.get_server_stats();
if stats.is_ok() {
let mut w_stats = state.stats.write().unwrap();
*w_stats = Some(stats.as_ref().ok().unwrap().clone());
let mut w_stats = NODE_STATE.stats.write().unwrap();
*w_stats = None;
if first_start {
state.starting.store(false, Ordering::Relaxed);
first_start = false;
NODE_STATE.stop_needed.store(false, Ordering::Relaxed);
break;
} else {
let stats = server.get_server_stats();
if stats.is_ok() {
let mut w_stats = NODE_STATE.stats.write().unwrap();
*w_stats = Some(stats.as_ref().ok().unwrap().clone());
if first_start {
NODE_STATE.starting.store(false, Ordering::Relaxed);
first_start = false;
}
}
}
thread::sleep(Duration::from_millis(300));
}
thread::sleep(Duration::from_millis(300));
})
}
pub fn get_sync_status_text(sync_status: Option<SyncStatus>) -> String {
if Node::is_restarting() {
return t!("server_restarting")
}
})
if sync_status.is_none() {
return t!("server_down")
}
match sync_status.unwrap() {
SyncStatus::Initial => t!("sync_status.initial"),
SyncStatus::NoSync => t!("sync_status.no_sync"),
SyncStatus::AwaitingPeers(_) => t!("sync_status.awaiting_peers"),
SyncStatus::HeaderSync {
sync_head,
highest_height,
..
} => {
if highest_height == 0 {
t!("sync_status.header_sync")
} else {
let percent = sync_head.height * 100 / highest_height;
t!("sync_status.header_sync_percent", "percent" => percent)
}
}
SyncStatus::TxHashsetDownload(stat) => {
if stat.total_size > 0 {
let percent = stat.downloaded_size * 100 / stat.total_size;
t!("sync_status.tx_hashset_download_percent", "percent" => percent)
} else {
t!("sync_status.tx_hashset_download")
}
}
SyncStatus::TxHashsetSetup => {
t!("sync_status.tx_hashset_setup")
}
SyncStatus::TxHashsetRangeProofsValidation {
rproofs,
rproofs_total,
} => {
let r_percent = if rproofs_total > 0 {
(rproofs * 100) / rproofs_total
} else {
0
};
t!("sync_status.tx_hashset_range_proofs_validation", "percent" => r_percent)
}
SyncStatus::TxHashsetKernelsValidation {
kernels,
kernels_total,
} => {
let k_percent = if kernels_total > 0 {
(kernels * 100) / kernels_total
} else {
0
};
t!("sync_status.tx_hashset_kernels_validation", "percent" => k_percent)
}
SyncStatus::TxHashsetSave | SyncStatus::TxHashsetDone => {
t!("sync_status.tx_hashset_save")
}
SyncStatus::BodySync {
current_height,
highest_height,
} => {
if highest_height == 0 {
t!("sync_status.body_sync")
} else {
let percent = current_height * 100 / highest_height;
t!("sync_status.body_sync_percent", "percent" => percent)
}
}
SyncStatus::Shutdown => t!("sync_status.shutdown"),
}
}
}
/// Start server with provided chain type
@ -281,3 +355,31 @@ fn start_server(chain_type: &ChainTypes) -> Server {
server_result.unwrap()
}
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncStatusText(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
_activity: jni::objects::JObject,
) -> jstring {
let sync_status = Node::get_sync_status();
let status_text = Node::get_sync_status_text(sync_status);
let j_text = _env.new_string(status_text);
return j_text.unwrap().into_raw();
}
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle(
_env: jni::JNIEnv,
_class: jni::objects::JObject,
_activity: jni::objects::JObject,
) -> jstring {
let j_text = _env.new_string(t!("integrated_node"));
return j_text.unwrap().into_raw();
}