diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index e92b2a4..04e6d7b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,12 +3,14 @@
>
+
+
+
+
25) {
+ startStopIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID);
+ }
+ if (canStart) {
+ startStopIntent.setAction(ACTION_START_NODE);
+ PendingIntent i = PendingIntent
+ .getBroadcast(BackgroundService.this, 1, startStopIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+ mNotificationBuilder.addAction(R.drawable.ic_start, getStartText(), i);
+ } else if (canStop) {
+ startStopIntent.setAction(ACTION_STOP_NODE);
+ PendingIntent i = PendingIntent
+ .getBroadcast(BackgroundService.this, 1, startStopIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+ mNotificationBuilder.addAction(R.drawable.ic_stop, getStopText(), i);
+ }
+
+ // Set up a button to exit from the app.
+ if (canStart || canStop) {
+ Intent exitIntent = new Intent(BackgroundService.this, NotificationActionsReceiver.class);
+ if (Build.VERSION.SDK_INT > 25) {
+ exitIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID);
+ }
+ exitIntent.setAction(ACTION_EXIT);
+ PendingIntent i = PendingIntent
+ .getBroadcast(BackgroundService.this, 1, exitIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT);
+ mNotificationBuilder.addAction(R.drawable.ic_close, getExitText(), i);
+ }
+ }
+
+ // Update notification.
+ if (textChanged || buttonsChanged) {
+ NotificationManager manager = getSystemService(NotificationManager.class);
+ manager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
+ }
+
+ // Repeat notification update.
+ mHandler.postDelayed(this, 1000);
}
}
};
+ @SuppressLint({"WakelockTimeout", "UnspecifiedRegisterReceiverFlag"})
@Override
public void onCreate() {
if (mStopped) {
@@ -78,15 +155,20 @@ public class BackgroundService extends Service {
mNotificationBuilder = new NotificationCompat.Builder(this, TAG)
.setContentTitle(this.getSyncTitle())
.setContentText(this.getSyncStatusText())
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(this.getSyncStatusText()))
.setSmallIcon(R.drawable.ic_stat_name)
+ .setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(pendingIntent);
Notification notification = mNotificationBuilder.build();
// Start service at foreground state to prevent killing by system.
- startForeground(SYNC_STATUS_NOTIFICATION_ID, notification);
+ startForeground(NOTIFICATION_ID, notification);
// Update sync status at notification.
mHandler.post(mUpdateSyncStatus);
+
+ // Register receiver to refresh notifications by intent.
+ registerReceiver(mReceiver, new IntentFilter(ACTION_REFRESH));
}
@Override
@@ -117,22 +199,26 @@ public class BackgroundService extends Service {
// Stop updating the notification.
mHandler.removeCallbacks(mUpdateSyncStatus);
+ unregisterReceiver(mReceiver);
+ clearNotification();
// Remove service from foreground state.
stopForeground(Service.STOP_FOREGROUND_REMOVE);
- // Remove notification.
+ // Release wake lock to allow CPU to sleep at background.
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release();
+ mWakeLock = null;
+ }
+ }
+
+ // Remove notification.
+ private void clearNotification() {
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.deleteNotificationChannel(TAG);
}
- notificationManager.cancel(SYNC_STATUS_NOTIFICATION_ID);
-
- // Release wake lock to allow CPU to sleep at background.
- if (mWakeLock.isHeld()) {
- mWakeLock.release();
- mWakeLock = null;
- }
+ notificationManager.cancel(NOTIFICATION_ID);
}
// Start the service.
@@ -165,10 +251,24 @@ public class BackgroundService extends Service {
return false;
}
- // Get sync status text for notification from native code.
+ // Get sync status text for notification.
private native String getSyncStatusText();
- // Get sync title text for notification from native code.
+ // Get sync title text for notification.
private native String getSyncTitle();
- // Check if app from the app is needed after node stop from native code.
+
+ // Get start text for notification.
+ private native String getStartText();
+ // Get stop text for notification.
+ private native String getStopText();
+
+ // Check if start node is possible.
+ private native boolean canStartNode();
+ // Check if stop node is possible.
+ private native boolean canStopNode();
+
+ // Get exit text for notification.
+ private native String getExitText();
+
+ // Check if app from the app is needed after node stop.
private native boolean exitAppAfterNodeStop();
}
diff --git a/android/app/src/main/java/mw/gri/android/NotificationActionsReceiver.java b/android/app/src/main/java/mw/gri/android/NotificationActionsReceiver.java
new file mode 100644
index 0000000..2336591
--- /dev/null
+++ b/android/app/src/main/java/mw/gri/android/NotificationActionsReceiver.java
@@ -0,0 +1,35 @@
+package mw.gri.android;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class NotificationActionsReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent i) {
+ String a = i.getAction();
+ if (a.equals(BackgroundService.ACTION_START_NODE)) {
+ startNode();
+ context.sendBroadcast(new Intent(BackgroundService.ACTION_REFRESH));
+ } else if (a.equals(BackgroundService.ACTION_STOP_NODE)) {
+ stopNode();
+ context.sendBroadcast(new Intent(BackgroundService.ACTION_REFRESH));
+ } else {
+ if (isNodeRunning()) {
+ stopNodeToExit();
+ context.sendBroadcast(new Intent(BackgroundService.ACTION_REFRESH));
+ } else {
+ context.sendBroadcast(new Intent(MainActivity.STOP_APP_ACTION));
+ }
+ }
+ }
+
+ // Start integrated node.
+ native void startNode();
+ // Stop integrated node.
+ native void stopNode();
+ // Stop node and exit from the app.
+ native void stopNodeToExit();
+ // Check if node is running.
+ native boolean isNodeRunning();
+}
diff --git a/android/app/src/main/res/drawable-anydpi/ic_close.xml b/android/app/src/main/res/drawable-anydpi/ic_close.xml
new file mode 100644
index 0000000..b333f53
--- /dev/null
+++ b/android/app/src/main/res/drawable-anydpi/ic_close.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable-anydpi/ic_start.xml b/android/app/src/main/res/drawable-anydpi/ic_start.xml
new file mode 100644
index 0000000..a00c0ed
--- /dev/null
+++ b/android/app/src/main/res/drawable-anydpi/ic_start.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable-anydpi/ic_stop.xml b/android/app/src/main/res/drawable-anydpi/ic_stop.xml
new file mode 100644
index 0000000..957c847
--- /dev/null
+++ b/android/app/src/main/res/drawable-anydpi/ic_stop.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/android/app/src/main/res/drawable-hdpi/ic_close.png b/android/app/src/main/res/drawable-hdpi/ic_close.png
new file mode 100644
index 0000000..136ac44
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_close.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/ic_start.png b/android/app/src/main/res/drawable-hdpi/ic_start.png
new file mode 100644
index 0000000..e61e66a
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_start.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/ic_stop.png b/android/app/src/main/res/drawable-hdpi/ic_stop.png
new file mode 100644
index 0000000..44c823e
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_stop.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_close.png b/android/app/src/main/res/drawable-mdpi/ic_close.png
new file mode 100644
index 0000000..a6551fb
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_close.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_start.png b/android/app/src/main/res/drawable-mdpi/ic_start.png
new file mode 100644
index 0000000..9611b5f
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_start.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_stop.png b/android/app/src/main/res/drawable-mdpi/ic_stop.png
new file mode 100644
index 0000000..f744450
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_stop.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_close.png b/android/app/src/main/res/drawable-xhdpi/ic_close.png
new file mode 100644
index 0000000..433762c
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_close.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_start.png b/android/app/src/main/res/drawable-xhdpi/ic_start.png
new file mode 100644
index 0000000..6cf694b
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_start.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_stop.png b/android/app/src/main/res/drawable-xhdpi/ic_stop.png
new file mode 100644
index 0000000..b1de0c5
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_stop.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_close.png b/android/app/src/main/res/drawable-xxhdpi/ic_close.png
new file mode 100644
index 0000000..a403ca3
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_close.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_start.png b/android/app/src/main/res/drawable-xxhdpi/ic_start.png
new file mode 100644
index 0000000..57227f5
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_start.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_stop.png b/android/app/src/main/res/drawable-xxhdpi/ic_stop.png
new file mode 100644
index 0000000..56a8d47
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_stop.png differ
diff --git a/src/node/node.rs b/src/node/node.rs
index e7cfaa8..04d85cf 100644
--- a/src/node/node.rs
+++ b/src/node/node.rs
@@ -658,6 +658,128 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle(
return j_text.unwrap().into_raw();
}
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Get start text for Android notification in Java string format.
+pub extern "C" fn Java_mw_gri_android_BackgroundService_getStartText(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) -> jni::sys::jstring {
+ let j_text = _env.new_string(t!("network_settings.enable"));
+ return j_text.unwrap().into_raw();
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Get stop text for Android notification in Java string format.
+pub extern "C" fn Java_mw_gri_android_BackgroundService_getStopText(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) -> jni::sys::jstring {
+ let j_text = _env.new_string(t!("network_settings.disable"));
+ return j_text.unwrap().into_raw();
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Get exit text for Android notification in Java string format.
+pub extern "C" fn Java_mw_gri_android_BackgroundService_getExitText(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) -> jni::sys::jstring {
+ let j_text = _env.new_string(t!("modal_exit.exit"));
+ return j_text.unwrap().into_raw();
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Check if node launch is possible.
+pub extern "C" fn Java_mw_gri_android_BackgroundService_canStartNode(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) -> jni::sys::jboolean {
+ let loading = Node::is_stopping() || Node::is_restarting() || Node::is_starting();
+ return (!loading && !Node::is_running()) as jni::sys::jboolean;
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Check if node stop is possible.
+pub extern "C" fn Java_mw_gri_android_BackgroundService_canStopNode(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) -> jni::sys::jboolean {
+ let loading = Node::is_stopping() || Node::is_restarting() || Node::is_starting();
+ return (!loading && Node::is_running()) as jni::sys::jboolean;
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Check if node stop is possible.
+pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_isNodeRunning(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) -> jni::sys::jboolean {
+ return Node::is_running() as jni::sys::jboolean;
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Start node from Android Java code.
+pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_startNode(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) {
+ Node::start();
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Stop node from Android Java code.
+pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_stopNode(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) {
+ Node::stop(false);
+}
+
+#[allow(dead_code)]
+#[cfg(target_os = "android")]
+#[allow(non_snake_case)]
+#[no_mangle]
+/// Stop node from Android Java code.
+pub extern "C" fn Java_mw_gri_android_NotificationActionsReceiver_stopNodeToExit(
+ _env: jni::JNIEnv,
+ _class: jni::objects::JObject,
+ _activity: jni::objects::JObject,
+) {
+ Node::stop(true);
+}
+
#[allow(dead_code)]
#[cfg(target_os = "android")]
#[allow(non_snake_case)]