android: show sync progress at notification, prevent app to sleep, temp fix of hanging after closing from recent apps
This commit is contained in:
parent
f05d930f60
commit
22c5b945c6
20 changed files with 426 additions and 234 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1736,6 +1736,7 @@ dependencies = [
|
||||||
"grin_servers",
|
"grin_servers",
|
||||||
"grin_util",
|
"grin_util",
|
||||||
"jni",
|
"jni",
|
||||||
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
|
|
|
@ -46,6 +46,7 @@ once_cell = "1.10.0"
|
||||||
rust-i18n = "1.1.4"
|
rust-i18n = "1.1.4"
|
||||||
sys-locale = "0.3.0"
|
sys-locale = "0.3.0"
|
||||||
chrono = "0.4.23"
|
chrono = "0.4.23"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
winit = { git = "https://github.com/rib/winit", branch = "android-activity" }
|
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" ] }
|
winit = { version = "0.27.2", features = [ "android-game-activity" ] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name="grim_android"
|
name="grim"
|
||||||
crate_type=["cdylib"]
|
crate_type=["cdylib"]
|
|
@ -9,7 +9,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "mw.gri.android"
|
applicationId "mw.gri.android"
|
||||||
minSdk 24
|
minSdk 24
|
||||||
targetSdk 31
|
targetSdk 33
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "0.1.0"
|
versionName "0.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,28 +2,35 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<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.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:hardwareAccelerated="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:largeHeap="true"
|
||||||
android:label="Grim"
|
android:allowBackup="true"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:label="Grim"
|
||||||
android:theme="@style/Theme.Main">
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.Main">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:launchMode="singleTask"
|
||||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode|keyboard"
|
android:name=".MainActivity"
|
||||||
android:exported="true">
|
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode|keyboard"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</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>
|
</activity>
|
||||||
|
<service android:name=".BackgroundService" android:stopWithTask="true" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
131
app/src/main/java/mw/gri/android/BackgroundService.java
Normal file
131
app/src/main/java/mw/gri/android/BackgroundService.java
Normal 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();
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package mw.gri.android;
|
package mw.gri.android;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Process;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import com.google.androidgamesdk.GameActivity;
|
import com.google.androidgamesdk.GameActivity;
|
||||||
|
@ -9,7 +9,7 @@ import com.google.androidgamesdk.GameActivity;
|
||||||
public class MainActivity extends GameActivity {
|
public class MainActivity extends GameActivity {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("grim_android");
|
System.loadLibrary("grim");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,9 +20,29 @@ public class MainActivity extends GameActivity {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
super.onCreate(savedInstanceState);
|
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() {
|
public int[] getDisplayCutouts() {
|
||||||
return Utils.getDisplayCutouts(this);
|
return Utils.getDisplayCutouts(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean mManualExit = false;
|
||||||
|
|
||||||
|
// Called from native code
|
||||||
|
public void onExit() {
|
||||||
|
mManualExit = true;
|
||||||
|
BackgroundService.stop(getApplicationContext());
|
||||||
|
finish();
|
||||||
|
}
|
||||||
}
|
}
|
BIN
app/src/main/res/drawable-hdpi/ic_stat_name.png
Normal file
BIN
app/src/main/res/drawable-hdpi/ic_stat_name.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 984 B |
BIN
app/src/main/res/drawable-mdpi/ic_stat_name.png
Normal file
BIN
app/src/main/res/drawable-mdpi/ic_stat_name.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 615 B |
BIN
app/src/main/res/drawable-xhdpi/ic_stat_name.png
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_stat_name.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_name.png
Normal file
BIN
app/src/main/res/drawable-xxhdpi/ic_stat_name.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_stat_name.png
Normal file
BIN
app/src/main/res/drawable-xxxhdpi/ic_stat_name.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -14,7 +14,7 @@ export CPPFLAGS="-DMDB_USE_ROBUST=0" && export CFLAGS="-DMDB_USE_ROBUST=0" && ca
|
||||||
|
|
||||||
if [ $? -eq 0 ]
|
if [ $? -eq 0 ]
|
||||||
then
|
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 clean
|
||||||
./gradlew build
|
./gradlew build
|
||||||
#./gradlew installDebug
|
#./gradlew installDebug
|
||||||
|
|
|
@ -22,7 +22,7 @@ use crate::gui::PlatformApp;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe fn android_main(app: AndroidApp) {
|
fn android_main(app: AndroidApp) {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
{
|
{
|
||||||
std::env::set_var("RUST_BACKTRACE", "full");
|
std::env::set_var("RUST_BACKTRACE", "full");
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use eframe::epaint::{Color32, Stroke};
|
|
||||||
|
|
||||||
mod views;
|
mod views;
|
||||||
pub use self::views::View;
|
pub use self::views::View;
|
||||||
|
|
||||||
|
@ -24,11 +22,13 @@ mod network;
|
||||||
mod network_node;
|
mod network_node;
|
||||||
mod network_tuning;
|
mod network_tuning;
|
||||||
mod network_metrics;
|
mod network_metrics;
|
||||||
|
mod network_mining;
|
||||||
|
|
||||||
pub use self::network::Network;
|
pub use self::network::Network;
|
||||||
|
|
||||||
pub trait NetworkTab {
|
pub trait NetworkTab {
|
||||||
fn name(&self) -> &String;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,6 @@ enum Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Network {
|
pub struct Network {
|
||||||
node: Node,
|
|
||||||
|
|
||||||
current_mode: Mode,
|
current_mode: Mode,
|
||||||
|
|
||||||
node_view: NetworkNode,
|
node_view: NetworkNode,
|
||||||
|
@ -52,9 +50,8 @@ pub struct Network {
|
||||||
|
|
||||||
impl Default for Network {
|
impl Default for Network {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let node = Node::new(ChainTypes::Mainnet, true);
|
Node::start(ChainTypes::Mainnet);
|
||||||
Self {
|
Self {
|
||||||
node,
|
|
||||||
current_mode: Mode::Node,
|
current_mode: Mode::Node,
|
||||||
node_view: NetworkNode::default(),
|
node_view: NetworkNode::default(),
|
||||||
metrics_view: NetworkMetrics::default()
|
metrics_view: NetworkMetrics::default()
|
||||||
|
@ -67,7 +64,7 @@ impl Network {
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
frame: &mut eframe::Frame,
|
frame: &mut eframe::Frame,
|
||||||
nav: &mut Navigator,
|
nav: &mut Navigator,
|
||||||
cb: &dyn PlatformCallbacks) {
|
_: &dyn PlatformCallbacks) {
|
||||||
|
|
||||||
egui::TopBottomPanel::top("network_title")
|
egui::TopBottomPanel::top("network_title")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
|
@ -136,13 +133,13 @@ impl Network {
|
||||||
fn draw_tab_content(&mut self, ui: &mut egui::Ui) {
|
fn draw_tab_content(&mut self, ui: &mut egui::Ui) {
|
||||||
match self.current_mode {
|
match self.current_mode {
|
||||||
Mode::Node => {
|
Mode::Node => {
|
||||||
self.node_view.ui(ui, &mut self.node);
|
self.node_view.ui(ui);
|
||||||
}
|
}
|
||||||
Mode::Metrics => {
|
Mode::Metrics => {
|
||||||
self.metrics_view.ui(ui, &mut self.node);
|
self.metrics_view.ui(ui);
|
||||||
}
|
}
|
||||||
Mode::Tuning => {
|
Mode::Tuning => {
|
||||||
self.node_view.ui(ui, &mut self.node);
|
self.node_view.ui(ui);
|
||||||
}
|
}
|
||||||
Mode::Miner => {}
|
Mode::Miner => {}
|
||||||
}
|
}
|
||||||
|
@ -215,23 +212,12 @@ impl Network {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
// Select sync status text
|
// Select sync status text
|
||||||
let sync_status = self.node.state.get_sync_status();
|
let sync_status = Node::get_sync_status();
|
||||||
let status_text = if self.node.state.is_restarting() {
|
let status_text = Node::get_sync_status_text(sync_status);
|
||||||
t!("server_restarting")
|
|
||||||
} else {
|
|
||||||
match sync_status {
|
|
||||||
None => {
|
|
||||||
t!("server_down")
|
|
||||||
}
|
|
||||||
Some(ss) => {
|
|
||||||
get_sync_status_text(ss).to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup text color animation based on sync status
|
// Setup text color animation based on sync status
|
||||||
let idle = match sync_status {
|
let idle = match sync_status {
|
||||||
None => { !self.node.state.is_starting() }
|
None => { !Node::is_starting() }
|
||||||
Some(ss) => { ss == SyncStatus::NoSync }
|
Some(ss) => { ss == SyncStatus::NoSync }
|
||||||
};
|
};
|
||||||
let (dark, bright) = (0.3, 1.0);
|
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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use egui::{RichText, ScrollArea, Spinner, Widget};
|
||||||
use grin_servers::DiffBlock;
|
use grin_servers::DiffBlock;
|
||||||
|
|
||||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
|
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::gui::views::{NetworkTab, View};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
|
@ -43,8 +43,8 @@ impl NetworkTab for NetworkMetrics {
|
||||||
&self.title
|
&self.title
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
let server_stats = node.state.get_stats();
|
let server_stats = Node::get_stats();
|
||||||
// Show loading widget if server is not working or difficulty height is zero.
|
// 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 {
|
if !server_stats.is_some() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
|
@ -86,7 +86,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Show difficulty window info
|
// Show difficulty adjustment window info
|
||||||
ui.vertical_centered_justified(|ui| {
|
ui.vertical_centered_justified(|ui| {
|
||||||
let title = t!("difficulty_at_window", "size" => stats.diff_stats.window_size);
|
let title = t!("difficulty_at_window", "size" => stats.diff_stats.window_size);
|
||||||
View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, title));
|
View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, title));
|
||||||
|
@ -114,28 +114,32 @@ impl NetworkTab for NetworkMetrics {
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Draw difficulty window blocks
|
// Show difficulty adjustment window blocks
|
||||||
let blocks_size = stats.diff_stats.last_blocks.len();
|
let blocks_size = stats.diff_stats.last_blocks.len();
|
||||||
ScrollArea::vertical().auto_shrink([false; 2]).stick_to_bottom(true).show_rows(
|
ScrollArea::vertical()
|
||||||
ui,
|
.auto_shrink([false; 2])
|
||||||
DIFF_BLOCK_UI_HEIGHT,
|
.stick_to_bottom(true)
|
||||||
blocks_size,
|
.id_source("diff_scroll")
|
||||||
|ui, row_range| {
|
.show_rows(
|
||||||
for index in row_range {
|
ui,
|
||||||
let db = stats.diff_stats.last_blocks.get(index).unwrap();
|
DIFF_BLOCK_UI_HEIGHT,
|
||||||
let rounding = if blocks_size == 1 {
|
blocks_size,
|
||||||
[true, true]
|
|ui, row_range| {
|
||||||
} else if index == 0 {
|
for index in row_range {
|
||||||
[true, false]
|
let db = stats.diff_stats.last_blocks.get(index).unwrap();
|
||||||
} else if index == blocks_size - 1 {
|
let rounding = if blocks_size == 1 {
|
||||||
[false, true]
|
[true, true]
|
||||||
} else {
|
} else if index == 0 {
|
||||||
[false, false]
|
[true, false]
|
||||||
};
|
} else if index == blocks_size - 1 {
|
||||||
draw_diff_block(ui, db, rounding)
|
[false, true]
|
||||||
}
|
} else {
|
||||||
},
|
[false, false]
|
||||||
);
|
};
|
||||||
|
draw_diff_block(ui, db, rounding)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/gui/views/network_mining.rs
Normal file
13
src/gui/views/network_mining.rs
Normal 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.
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
use eframe::epaint::Stroke;
|
use eframe::epaint::Stroke;
|
||||||
use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget};
|
use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget};
|
||||||
use grin_servers::common::stats::TxStats;
|
|
||||||
use grin_servers::PeerStats;
|
use grin_servers::PeerStats;
|
||||||
|
|
||||||
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
|
use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT};
|
||||||
|
@ -39,8 +38,8 @@ impl NetworkTab for NetworkNode {
|
||||||
&self.title
|
&self.title
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui, node: &mut Node) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
let server_stats = node.state.get_stats();
|
let server_stats = Node::get_stats();
|
||||||
if !server_stats.is_some() {
|
if !server_stats.is_some() {
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui);
|
Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui);
|
||||||
|
@ -80,9 +79,9 @@ impl NetworkTab for NetworkNode {
|
||||||
[false, false, true, false]);
|
[false, false, true, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
let ts = stats.header_stats.latest_timestamp;
|
let h_ts = stats.header_stats.latest_timestamp;
|
||||||
View::rounded_box(ui,
|
View::rounded_box(ui,
|
||||||
format!("{}", ts.format("%d/%m/%Y %H:%M")),
|
format!("{}", h_ts.format("%d/%m/%Y %H:%M")),
|
||||||
t!("time_utc"),
|
t!("time_utc"),
|
||||||
[false, false, false, true]);
|
[false, false, false, true]);
|
||||||
});
|
});
|
||||||
|
@ -116,9 +115,9 @@ impl NetworkTab for NetworkNode {
|
||||||
[false, false, true, false]);
|
[false, false, true, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
let ts = stats.chain_stats.latest_timestamp;
|
let b_ts = stats.chain_stats.latest_timestamp;
|
||||||
View::rounded_box(ui,
|
View::rounded_box(ui,
|
||||||
format!("{}", ts.format("%d/%m/%Y %H:%M")),
|
format!("{}", b_ts.format("%d/%m/%Y %H:%M")),
|
||||||
t!("time_utc"),
|
t!("time_utc"),
|
||||||
[false, false, false, true]);
|
[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
|
// Add space after last item
|
||||||
if !rounding[0] && rounding[1] {
|
if rounding[1] {
|
||||||
ui.add_space(5.0);
|
ui.add_space(3.0);
|
||||||
} else if rounding[0] && rounding[1] {
|
|
||||||
ui.add_space(2.0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,5 +14,4 @@
|
||||||
|
|
||||||
mod node;
|
mod node;
|
||||||
|
|
||||||
pub use self::node::Node;
|
pub use self::node::Node;
|
||||||
pub use self::node::NodeState;
|
|
274
src/node/node.rs
274
src/node/node.rs
|
@ -25,50 +25,20 @@ use grin_config::config;
|
||||||
use grin_core::global;
|
use grin_core::global;
|
||||||
use grin_core::global::ChainTypes;
|
use grin_core::global::ChainTypes;
|
||||||
use grin_servers::{Server, ServerStats};
|
use grin_servers::{Server, ServerStats};
|
||||||
|
use jni::objects::JString;
|
||||||
|
use jni::sys::jstring;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref NODE_STATE: Arc<Node> = Arc::new(Node::default());
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Node {
|
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
|
/// Data for UI
|
||||||
stats: Arc<RwLock<Option<ServerStats>>>,
|
stats: Arc<RwLock<Option<ServerStats>>>,
|
||||||
/// Chain type of launched server
|
/// Chain type of launched server
|
||||||
chain_type: Arc<ChainTypes>,
|
chain_type: Arc<RwLock<ChainTypes>>,
|
||||||
/// Indicator if server is starting
|
/// Indicator if server is starting
|
||||||
starting: AtomicBool,
|
starting: AtomicBool,
|
||||||
/// Thread flag to stop the server and start it again
|
/// Thread flag to stop the server and start it again
|
||||||
|
@ -77,102 +47,206 @@ pub struct NodeState {
|
||||||
stop_needed: AtomicBool,
|
stop_needed: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeState {
|
impl Default for Node {
|
||||||
/// Instantiate new node state with provided chain type and server state
|
fn default() -> Self {
|
||||||
pub fn new(chain_type: ChainTypes) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
stats: Arc::new(RwLock::new(None)),
|
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),
|
restart_needed: AtomicBool::new(false),
|
||||||
stop_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
|
/// Check if server is starting
|
||||||
pub fn is_starting(&self) -> bool {
|
pub fn is_starting() -> bool {
|
||||||
self.starting.load(Ordering::Relaxed)
|
NODE_STATE.starting.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if server is running
|
/// Check if server is running
|
||||||
pub fn is_running(&self) -> bool {
|
pub fn is_running() -> bool {
|
||||||
self.get_stats().is_some() || self.is_starting()
|
Self::get_stats().is_some() || Self::is_starting()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if server is stopping
|
/// Check if server is stopping
|
||||||
pub fn is_stopping(&self) -> bool {
|
pub fn is_stopping() -> bool {
|
||||||
self.stop_needed.load(Ordering::Relaxed)
|
NODE_STATE.stop_needed.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if server is restarting
|
/// Check if server is restarting
|
||||||
pub fn is_restarting(&self) -> bool {
|
pub fn is_restarting() -> bool {
|
||||||
self.restart_needed.load(Ordering::Relaxed)
|
NODE_STATE.restart_needed.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get server stats
|
/// Get server stats
|
||||||
pub fn get_stats(&self) -> RwLockReadGuard<'_, Option<ServerStats>> {
|
pub fn get_stats() -> RwLockReadGuard<'static, Option<ServerStats>> {
|
||||||
self.stats.read().unwrap()
|
NODE_STATE.stats.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get server sync status, empty when server is not running
|
/// 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
|
// return Shutdown status when node is stopping
|
||||||
if self.is_stopping() {
|
if Self::is_stopping() {
|
||||||
return Some(SyncStatus::Shutdown)
|
return Some(SyncStatus::Shutdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return Initial status when node is starting
|
// return Initial status when node is starting
|
||||||
if self.is_starting() {
|
if Self::is_starting() {
|
||||||
return Some(SyncStatus::Initial)
|
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)
|
// return sync status when server is running (stats are not empty)
|
||||||
if stats.is_some() {
|
if stats.is_some() {
|
||||||
return Some(stats.as_ref().unwrap().sync_status)
|
return Some(stats.as_ref().unwrap().sync_status)
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Start a thread to launch server and update node state with server stats
|
/// Start a thread to launch server and update state with server stats
|
||||||
fn start_server_thread(state: Arc<NodeState>, chain_type: ChainTypes) -> JoinHandle<()> {
|
fn start_server_thread() -> JoinHandle<()> {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
state.starting.store(true, Ordering::Relaxed);
|
NODE_STATE.starting.store(true, Ordering::Relaxed);
|
||||||
let mut server = start_server(&chain_type);
|
|
||||||
let mut first_start = true;
|
|
||||||
|
|
||||||
loop {
|
let mut server = start_server(&NODE_STATE.chain_type.read().unwrap());
|
||||||
if state.is_restarting() {
|
let mut first_start = true;
|
||||||
server.stop();
|
|
||||||
|
|
||||||
// Create new server with current chain type
|
loop {
|
||||||
server = start_server(&state.chain_type);
|
if Self::is_restarting() {
|
||||||
|
server.stop();
|
||||||
|
|
||||||
state.restart_needed.store(false, Ordering::Relaxed);
|
// Create new server with current chain type
|
||||||
} else if state.is_stopping() {
|
server = start_server(&NODE_STATE.chain_type.read().unwrap());
|
||||||
server.stop();
|
|
||||||
|
|
||||||
let mut w_stats = state.stats.write().unwrap();
|
NODE_STATE.restart_needed.store(false, Ordering::Relaxed);
|
||||||
*w_stats = None;
|
} else if Self::is_stopping() {
|
||||||
|
server.stop();
|
||||||
|
|
||||||
state.stop_needed.store(false, Ordering::Relaxed);
|
let mut w_stats = NODE_STATE.stats.write().unwrap();
|
||||||
break;
|
*w_stats = None;
|
||||||
} 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());
|
|
||||||
|
|
||||||
if first_start {
|
NODE_STATE.stop_needed.store(false, Ordering::Relaxed);
|
||||||
state.starting.store(false, Ordering::Relaxed);
|
break;
|
||||||
first_start = false;
|
} 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
|
/// Start server with provided chain type
|
||||||
|
@ -280,4 +354,32 @@ fn start_server(chain_type: &ChainTypes) -> Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
server_result.unwrap()
|
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();
|
||||||
}
|
}
|
Loading…
Reference in a new issue