Open .slatepack file with the app #13
11 changed files with 193 additions and 68 deletions
|
@ -1,15 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
>
|
>
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
<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.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
|
@ -18,7 +20,6 @@
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="Grim"
|
android:label="Grim"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
|
||||||
android:theme="@style/Theme.Main">
|
android:theme="@style/Theme.Main">
|
||||||
|
|
||||||
<receiver android:name=".NotificationActionsReceiver"/>
|
<receiver android:name=".NotificationActionsReceiver"/>
|
||||||
|
@ -44,6 +45,22 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter android:scheme="http" tools:ignore="AppLinkUrlError">
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="text/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter android:scheme="http" tools:ignore="AppLinkUrlError">
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="application/*" />
|
||||||
|
</intent-filter>
|
||||||
|
|
||||||
<meta-data android:name="android.app.lib_name" android:value="grim" />
|
<meta-data android:name="android.app.lib_name" android:value="grim" />
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name=".BackgroundService" android:stopWithTask="true" />
|
<service android:name=".BackgroundService" android:stopWithTask="true" />
|
||||||
|
|
|
@ -152,6 +152,7 @@ public class BackgroundService extends Service {
|
||||||
// Show notification with sync status.
|
// Show notification with sync status.
|
||||||
Intent i = getPackageManager().getLaunchIntentForPackage(this.getPackageName());
|
Intent i = getPackageManager().getLaunchIntentForPackage(this.getPackageName());
|
||||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_IMMUTABLE);
|
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_IMMUTABLE);
|
||||||
|
try {
|
||||||
mNotificationBuilder = new NotificationCompat.Builder(this, TAG)
|
mNotificationBuilder = new NotificationCompat.Builder(this, TAG)
|
||||||
.setContentTitle(this.getSyncTitle())
|
.setContentTitle(this.getSyncTitle())
|
||||||
.setContentText(this.getSyncStatusText())
|
.setContentText(this.getSyncStatusText())
|
||||||
|
@ -159,6 +160,9 @@ public class BackgroundService extends Service {
|
||||||
.setSmallIcon(R.drawable.ic_stat_name)
|
.setSmallIcon(R.drawable.ic_stat_name)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setContentIntent(pendingIntent);
|
.setContentIntent(pendingIntent);
|
||||||
|
} catch (UnsatisfiedLinkError e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Notification notification = mNotificationBuilder.build();
|
Notification notification = mNotificationBuilder.build();
|
||||||
|
|
||||||
// Start service at foreground state to prevent killing by system.
|
// Start service at foreground state to prevent killing by system.
|
||||||
|
|
|
@ -7,9 +7,9 @@ import android.content.*;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.*;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
|
@ -51,8 +51,7 @@ public class MainActivity extends GameActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context ctx, Intent i) {
|
public void onReceive(Context ctx, Intent i) {
|
||||||
if (i.getAction().equals(STOP_APP_ACTION)) {
|
if (i.getAction().equals(STOP_APP_ACTION)) {
|
||||||
onExit();
|
exit();
|
||||||
Process.killProcess(Process.myPid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -67,11 +66,19 @@ public class MainActivity extends GameActivity {
|
||||||
private ExecutorService mCameraExecutor = null;
|
private ExecutorService mCameraExecutor = null;
|
||||||
private boolean mUseBackCamera = true;
|
private boolean mUseBackCamera = true;
|
||||||
|
|
||||||
private ActivityResultLauncher<Intent> mFilePickResultLauncher = null;
|
private ActivityResultLauncher<Intent> mFilePickResult = null;
|
||||||
|
private ActivityResultLauncher<Intent> mOpenFilePermissionsResult = null;
|
||||||
|
|
||||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
// Check if activity was launched to exclude from recent apps on exit.
|
||||||
|
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
|
||||||
|
super.onCreate(null);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Clear cache on start.
|
// Clear cache on start.
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
Utils.deleteDirectoryContent(new File(getExternalCacheDir().getPath()), false);
|
Utils.deleteDirectoryContent(new File(getExternalCacheDir().getPath()), false);
|
||||||
|
@ -91,8 +98,21 @@ public class MainActivity extends GameActivity {
|
||||||
// Register receiver to finish activity from the BackgroundService.
|
// Register receiver to finish activity from the BackgroundService.
|
||||||
registerReceiver(mBroadcastReceiver, new IntentFilter(STOP_APP_ACTION));
|
registerReceiver(mBroadcastReceiver, new IntentFilter(STOP_APP_ACTION));
|
||||||
|
|
||||||
// Register file pick result launcher.
|
// Register associated file opening result.
|
||||||
mFilePickResultLauncher = registerForActivityResult(
|
mOpenFilePermissionsResult = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
if (Environment.isExternalStorageManager()) {
|
||||||
|
onFile();
|
||||||
|
}
|
||||||
|
} else if (result.getResultCode() == RESULT_OK) {
|
||||||
|
onFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Register file pick result.
|
||||||
|
mFilePickResult = registerForActivityResult(
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
result -> {
|
result -> {
|
||||||
int resultCode = result.getResultCode();
|
int resultCode = result.getResultCode();
|
||||||
|
@ -124,7 +144,7 @@ public class MainActivity extends GameActivity {
|
||||||
// Listener for display insets (cutouts) to pass values into native code.
|
// Listener for display insets (cutouts) to pass values into native code.
|
||||||
View content = getWindow().getDecorView().findViewById(android.R.id.content);
|
View content = getWindow().getDecorView().findViewById(android.R.id.content);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(content, (v, insets) -> {
|
ViewCompat.setOnApplyWindowInsetsListener(content, (v, insets) -> {
|
||||||
// Setup cutouts values.
|
// Get display cutouts.
|
||||||
DisplayCutoutCompat dc = insets.getDisplayCutout();
|
DisplayCutoutCompat dc = insets.getDisplayCutout();
|
||||||
int cutoutTop = 0;
|
int cutoutTop = 0;
|
||||||
int cutoutRight = 0;
|
int cutoutRight = 0;
|
||||||
|
@ -140,7 +160,7 @@ public class MainActivity extends GameActivity {
|
||||||
// Get display insets.
|
// Get display insets.
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
|
||||||
// Setup values to pass into native code.
|
// Pass values into native code.
|
||||||
int[] values = new int[]{0, 0, 0, 0};
|
int[] values = new int[]{0, 0, 0, 0};
|
||||||
values[0] = Utils.pxToDp(Integer.max(cutoutTop, systemBars.top), this);
|
values[0] = Utils.pxToDp(Integer.max(cutoutTop, systemBars.top), this);
|
||||||
values[1] = Utils.pxToDp(Integer.max(cutoutRight, systemBars.right), this);
|
values[1] = Utils.pxToDp(Integer.max(cutoutRight, systemBars.right), this);
|
||||||
|
@ -166,7 +186,60 @@ public class MainActivity extends GameActivity {
|
||||||
BackgroundService.start(this);
|
BackgroundService.start(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Check if intent has data on launch.
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
onNewIntent(getIntent());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onNewIntent(Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
String action = intent.getAction();
|
||||||
|
// Check if file was open with the application.
|
||||||
|
if (action != null && action.equals(Intent.ACTION_VIEW)) {
|
||||||
|
Intent i = getIntent();
|
||||||
|
i.setData(intent.getData());
|
||||||
|
setIntent(i);
|
||||||
|
onFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback when associated file was open.
|
||||||
|
private void onFile() {
|
||||||
|
Uri data = getIntent().getData();
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
if (!Environment.isExternalStorageManager()) {
|
||||||
|
Intent i = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||||
|
mOpenFilePermissionsResult.launch(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ParcelFileDescriptor parcelFile = getContentResolver().openFileDescriptor(data, "r");
|
||||||
|
FileReader fileReader = new FileReader(parcelFile.getFileDescriptor());
|
||||||
|
BufferedReader reader = new BufferedReader(fileReader);
|
||||||
|
String line;
|
||||||
|
StringBuilder buff = new StringBuilder();
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
buff.append(line);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
fileReader.close();
|
||||||
|
|
||||||
|
// Provide file content into native code.
|
||||||
|
onData(buff.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass data into native code.
|
||||||
|
public native void onData(String data);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
@ -232,17 +305,17 @@ public class MainActivity extends GameActivity {
|
||||||
// Implemented into native code to handle key code BACK event.
|
// Implemented into native code to handle key code BACK event.
|
||||||
public native void onBack();
|
public native void onBack();
|
||||||
|
|
||||||
// Actions on app exit.
|
// Called from native code to exit app.
|
||||||
private void onExit() {
|
public void exit() {
|
||||||
unregisterReceiver(mBroadcastReceiver);
|
finishAndRemoveTask();
|
||||||
BackgroundService.stop(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
onExit();
|
unregisterReceiver(mBroadcastReceiver);
|
||||||
|
BackgroundService.stop(this);
|
||||||
|
|
||||||
// Kill process after 3 seconds if app was terminated from recent apps to prevent app hanging.
|
// Kill process after 3 secs if app was terminated from recent apps to prevent app hang.
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
onTermination();
|
onTermination();
|
||||||
|
@ -253,9 +326,7 @@ public class MainActivity extends GameActivity {
|
||||||
}
|
}
|
||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
// Destroy an app and kill process.
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
Process.killProcess(Process.myPid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify native code to stop activity (e.g. node) if app was terminated from recent apps.
|
// Notify native code to stop activity (e.g. node) if app was terminated from recent apps.
|
||||||
|
@ -298,18 +369,16 @@ public class MainActivity extends GameActivity {
|
||||||
|
|
||||||
// Called from native code to start camera.
|
// Called from native code to start camera.
|
||||||
public void startCamera() {
|
public void startCamera() {
|
||||||
// Check permissions.
|
|
||||||
String notificationsPermission = Manifest.permission.CAMERA;
|
String notificationsPermission = Manifest.permission.CAMERA;
|
||||||
if (checkSelfPermission(notificationsPermission) != PackageManager.PERMISSION_GRANTED) {
|
if (checkSelfPermission(notificationsPermission) != PackageManager.PERMISSION_GRANTED) {
|
||||||
requestPermissions(new String[] { notificationsPermission }, CAMERA_PERMISSION_CODE);
|
requestPermissions(new String[] { notificationsPermission }, CAMERA_PERMISSION_CODE);
|
||||||
} else {
|
} else {
|
||||||
// Start .
|
|
||||||
if (mCameraProviderFuture == null) {
|
if (mCameraProviderFuture == null) {
|
||||||
mCameraProviderFuture = ProcessCameraProvider.getInstance(this);
|
mCameraProviderFuture = ProcessCameraProvider.getInstance(this);
|
||||||
mCameraProviderFuture.addListener(() -> {
|
mCameraProviderFuture.addListener(() -> {
|
||||||
try {
|
try {
|
||||||
mCameraProvider = mCameraProviderFuture.get();
|
mCameraProvider = mCameraProviderFuture.get();
|
||||||
// Launch camera.
|
// Start camera.
|
||||||
openCamera();
|
openCamera();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
View content = findViewById(android.R.id.content);
|
View content = findViewById(android.R.id.content);
|
||||||
|
@ -402,7 +471,7 @@ public class MainActivity extends GameActivity {
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.setType("*/*");
|
intent.setType("*/*");
|
||||||
try {
|
try {
|
||||||
mFilePickResultLauncher.launch(Intent.createChooser(intent, "Pick file"));
|
mFilePickResult.launch(Intent.createChooser(intent, "Pick file"));
|
||||||
} catch (android.content.ActivityNotFoundException ex) {
|
} catch (android.content.ActivityNotFoundException ex) {
|
||||||
onFilePick("");
|
onFilePick("");
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ impl<Platform: PlatformCallbacks> App<Platform> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provide incoming data to wallets.
|
// Provide incoming data to wallets.
|
||||||
if let Some(data) = crate::consume_passed_data() {
|
if let Some(data) = crate::consume_incoming_data() {
|
||||||
if !data.is_empty() {
|
if !data.is_empty() {
|
||||||
self.content.wallets.on_data(ui, Some(data), &self.platform);
|
self.content.wallets.on_data(ui, Some(data), &self.platform);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,10 @@ impl PlatformCallbacks for Android {
|
||||||
*w_ctx = Some(ctx.clone());
|
*w_ctx = Some(ctx.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn exit(&self) {
|
||||||
|
self.call_java_method("exit", "()V", &[]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn show_keyboard(&self) {
|
fn show_keyboard(&self) {
|
||||||
// Disable NDK soft input show call before fix for egui.
|
// Disable NDK soft input show call before fix for egui.
|
||||||
// self.android_app.show_soft_input(false);
|
// self.android_app.show_soft_input(false);
|
||||||
|
@ -137,20 +141,18 @@ impl PlatformCallbacks for Android {
|
||||||
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error> {
|
fn share_data(&self, name: String, data: Vec<u8>) -> Result<(), std::io::Error> {
|
||||||
// Create file at cache dir.
|
// Create file at cache dir.
|
||||||
let default_cache = OsString::from(dirs::cache_dir().unwrap());
|
let default_cache = OsString::from(dirs::cache_dir().unwrap());
|
||||||
let mut cache = PathBuf::from(env::var_os("XDG_CACHE_HOME").unwrap_or(default_cache));
|
let mut file = PathBuf::from(env::var_os("XDG_CACHE_HOME").unwrap_or(default_cache));
|
||||||
cache.push("images");
|
file.push(name);
|
||||||
std::fs::create_dir_all(cache.to_str().unwrap())?;
|
if file.exists() {
|
||||||
cache.push(name);
|
std::fs::remove_file(file.clone())?;
|
||||||
if cache.exists() {
|
|
||||||
std::fs::remove_file(cache.clone())?;
|
|
||||||
}
|
}
|
||||||
let mut image = File::create_new(cache.clone())?;
|
let mut image = File::create_new(file.clone())?;
|
||||||
image.write_all(data.as_slice())?;
|
image.write_all(data.as_slice())?;
|
||||||
image.sync_all()?;
|
image.sync_all()?;
|
||||||
// Call share modal at system.
|
// Call share modal at system.
|
||||||
let vm = unsafe { jni::JavaVM::from_raw(self.android_app.vm_as_ptr() as _) }.unwrap();
|
let vm = unsafe { jni::JavaVM::from_raw(self.android_app.vm_as_ptr() as _) }.unwrap();
|
||||||
let env = vm.attach_current_thread().unwrap();
|
let env = vm.attach_current_thread().unwrap();
|
||||||
let arg_value = env.new_string(cache.to_str().unwrap()).unwrap();
|
let arg_value = env.new_string(file.to_str().unwrap()).unwrap();
|
||||||
self.call_java_method("shareImage",
|
self.call_java_method("shareImage",
|
||||||
"(Ljava/lang/String;)V",
|
"(Ljava/lang/String;)V",
|
||||||
&[JValue::Object(&JObject::from(arg_value))]).unwrap();
|
&[JValue::Object(&JObject::from(arg_value))]).unwrap();
|
||||||
|
|
|
@ -43,6 +43,14 @@ impl PlatformCallbacks for Desktop {
|
||||||
*w_ctx = Some(ctx.clone());
|
*w_ctx = Some(ctx.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn exit(&self) {
|
||||||
|
let r_ctx = self.ctx.read();
|
||||||
|
if r_ctx.is_some() {
|
||||||
|
let ctx = r_ctx.as_ref().unwrap();
|
||||||
|
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn show_keyboard(&self) {}
|
fn show_keyboard(&self) {}
|
||||||
|
|
||||||
fn hide_keyboard(&self) {}
|
fn hide_keyboard(&self) {}
|
||||||
|
|
|
@ -23,6 +23,7 @@ pub mod platform;
|
||||||
|
|
||||||
pub trait PlatformCallbacks {
|
pub trait PlatformCallbacks {
|
||||||
fn set_context(&mut self, ctx: &egui::Context);
|
fn set_context(&mut self, ctx: &egui::Context);
|
||||||
|
fn exit(&self);
|
||||||
fn show_keyboard(&self);
|
fn show_keyboard(&self);
|
||||||
fn hide_keyboard(&self);
|
fn hide_keyboard(&self);
|
||||||
fn copy_string_to_buffer(&self, data: String);
|
fn copy_string_to_buffer(&self, data: String);
|
||||||
|
|
|
@ -40,8 +40,8 @@ pub struct Content {
|
||||||
/// Central panel [`WalletsContent`] content.
|
/// Central panel [`WalletsContent`] content.
|
||||||
pub wallets: WalletsContent,
|
pub wallets: WalletsContent,
|
||||||
|
|
||||||
/// Check if app exit is allowed on close event of [`eframe::App`] implementation.
|
/// Check if app exit is allowed on Desktop close event.
|
||||||
pub(crate) exit_allowed: bool,
|
pub exit_allowed: bool,
|
||||||
/// Flag to show exit progress at [`Modal`].
|
/// Flag to show exit progress at [`Modal`].
|
||||||
show_exit_progress: bool,
|
show_exit_progress: bool,
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ impl ModalContainer for Content {
|
||||||
modal: &Modal,
|
modal: &Modal,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
match modal.id {
|
match modal.id {
|
||||||
Self::EXIT_CONFIRMATION_MODAL => self.exit_modal_content(ui, modal),
|
Self::EXIT_CONFIRMATION_MODAL => self.exit_modal_content(ui, modal, cb),
|
||||||
Self::SETTINGS_MODAL => self.settings_modal_ui(ui, modal),
|
Self::SETTINGS_MODAL => self.settings_modal_ui(ui, modal),
|
||||||
Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui, modal),
|
Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui, modal),
|
||||||
Self::CRASH_REPORT_MODAL => self.crash_report_modal_ui(ui, modal, cb),
|
Self::CRASH_REPORT_MODAL => self.crash_report_modal_ui(ui, modal, cb),
|
||||||
|
@ -206,11 +206,11 @@ impl Content {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw exit confirmation modal content.
|
/// Draw exit confirmation modal content.
|
||||||
fn exit_modal_content(&mut self, ui: &mut egui::Ui, modal: &Modal) {
|
fn exit_modal_content(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {
|
||||||
if self.show_exit_progress {
|
if self.show_exit_progress {
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
self.exit_allowed = true;
|
self.exit_allowed = true;
|
||||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
cb.exit();
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
}
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
|
@ -244,7 +244,7 @@ impl Content {
|
||||||
View::button_ui(ui, t!("modal_exit.exit"), Colors::white_or_black(false), |ui| {
|
View::button_ui(ui, t!("modal_exit.exit"), Colors::white_or_black(false), |ui| {
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
self.exit_allowed = true;
|
self.exit_allowed = true;
|
||||||
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
|
cb.exit();
|
||||||
modal.close();
|
modal.close();
|
||||||
} else {
|
} else {
|
||||||
Node::stop(true);
|
Node::stop(true);
|
||||||
|
|
|
@ -36,12 +36,10 @@ pub struct WalletsContent {
|
||||||
|
|
||||||
/// Wallet selection [`Modal`] content.
|
/// Wallet selection [`Modal`] content.
|
||||||
wallet_selection_content: Option<WalletsModal>,
|
wallet_selection_content: Option<WalletsModal>,
|
||||||
|
|
||||||
/// Wallet opening [`Modal`] content.
|
/// Wallet opening [`Modal`] content.
|
||||||
open_wallet_content: Option<OpenWalletModal>,
|
open_wallet_content: Option<OpenWalletModal>,
|
||||||
|
|
||||||
/// Wallet connection selection content.
|
/// Wallet connection selection content.
|
||||||
conn_modal_content: Option<WalletConnectionModal>,
|
conn_selection_content: Option<WalletConnectionModal>,
|
||||||
|
|
||||||
/// Selected [`Wallet`] content.
|
/// Selected [`Wallet`] content.
|
||||||
wallet_content: WalletContent,
|
wallet_content: WalletContent,
|
||||||
|
@ -70,7 +68,7 @@ impl Default for WalletsContent {
|
||||||
wallets: WalletList::default(),
|
wallets: WalletList::default(),
|
||||||
wallet_selection_content: None,
|
wallet_selection_content: None,
|
||||||
open_wallet_content: None,
|
open_wallet_content: None,
|
||||||
conn_modal_content: None,
|
conn_selection_content: None,
|
||||||
wallet_content: WalletContent::new(None),
|
wallet_content: WalletContent::new(None),
|
||||||
creation_content: WalletCreation::default(),
|
creation_content: WalletCreation::default(),
|
||||||
show_wallets_at_dual_panel: AppConfig::show_wallets_at_dual_panel(),
|
show_wallets_at_dual_panel: AppConfig::show_wallets_at_dual_panel(),
|
||||||
|
@ -106,7 +104,7 @@ impl ModalContainer for WalletsContent {
|
||||||
self.creation_content.name_pass_modal_ui(ui, modal, cb)
|
self.creation_content.name_pass_modal_ui(ui, modal, cb)
|
||||||
},
|
},
|
||||||
CONNECTION_SELECTION_MODAL => {
|
CONNECTION_SELECTION_MODAL => {
|
||||||
if let Some(content) = self.conn_modal_content.as_mut() {
|
if let Some(content) = self.conn_selection_content.as_mut() {
|
||||||
content.ui(ui, modal, cb, |id| {
|
content.ui(ui, modal, cb, |id| {
|
||||||
// Update wallet connection on select.
|
// Update wallet connection on select.
|
||||||
let list = self.wallets.list();
|
let list = self.wallets.list();
|
||||||
|
@ -303,6 +301,7 @@ impl WalletsContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Show wallet selection with provided optional data.
|
||||||
fn show_wallet_selection_modal(&mut self, data: Option<String>) {
|
fn show_wallet_selection_modal(&mut self, data: Option<String>) {
|
||||||
self.wallet_selection_content = Some(WalletsModal::new(None, data, true));
|
self.wallet_selection_content = Some(WalletsModal::new(None, data, true));
|
||||||
// Show wallet selection modal.
|
// Show wallet selection modal.
|
||||||
|
@ -557,7 +556,7 @@ impl WalletsContent {
|
||||||
/// Show [`Modal`] to select connection for the wallet.
|
/// Show [`Modal`] to select connection for the wallet.
|
||||||
fn show_connection_selector_modal(&mut self, wallet: &Wallet) {
|
fn show_connection_selector_modal(&mut self, wallet: &Wallet) {
|
||||||
let ext_conn = wallet.get_current_ext_conn();
|
let ext_conn = wallet.get_current_ext_conn();
|
||||||
self.conn_modal_content = Some(WalletConnectionModal::new(ext_conn));
|
self.conn_selection_content = Some(WalletConnectionModal::new(ext_conn));
|
||||||
// Show modal.
|
// Show modal.
|
||||||
Modal::new(CONNECTION_SELECTION_MODAL)
|
Modal::new(CONNECTION_SELECTION_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
|
|
36
src/lib.rs
36
src/lib.rs
|
@ -260,15 +260,15 @@ fn setup_i18n() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get data provided from deeplink or opened file.
|
/// Get data from deeplink or opened file.
|
||||||
pub fn consume_passed_data() -> Option<String> {
|
pub fn consume_incoming_data() -> Option<String> {
|
||||||
let has_data = {
|
let has_data = {
|
||||||
let r_data = PASSED_DATA.read();
|
let r_data = INCOMING_DATA.read();
|
||||||
r_data.is_some()
|
r_data.is_some()
|
||||||
};
|
};
|
||||||
if has_data {
|
if has_data {
|
||||||
// Clear data.
|
// Clear data.
|
||||||
let mut w_data = PASSED_DATA.write();
|
let mut w_data = INCOMING_DATA.write();
|
||||||
let data = w_data.clone();
|
let data = w_data.clone();
|
||||||
*w_data = None;
|
*w_data = None;
|
||||||
return data;
|
return data;
|
||||||
|
@ -278,11 +278,35 @@ pub fn consume_passed_data() -> Option<String> {
|
||||||
|
|
||||||
/// Provide data from deeplink or opened file.
|
/// Provide data from deeplink or opened file.
|
||||||
pub fn on_data(data: String) {
|
pub fn on_data(data: String) {
|
||||||
let mut w_data = PASSED_DATA.write();
|
let mut w_data = INCOMING_DATA.write();
|
||||||
*w_data = Some(data);
|
*w_data = Some(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Data provided from deeplink or opened file.
|
/// Data provided from deeplink or opened file.
|
||||||
pub static ref PASSED_DATA: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
pub static ref INCOMING_DATA: Arc<RwLock<Option<String>>> = Arc::new(RwLock::new(None));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Callback from Java code with with passed data.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn Java_mw_gri_android_MainActivity_onData(
|
||||||
|
_env: jni::JNIEnv,
|
||||||
|
_class: jni::objects::JObject,
|
||||||
|
char: jni::sys::jstring
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let j_obj = jni::objects::JString::from_raw(char);
|
||||||
|
if let Ok(j_str) = _env.get_string_unchecked(j_obj.as_ref()) {
|
||||||
|
match j_str.to_str() {
|
||||||
|
Ok(str) => {
|
||||||
|
let mut w_path = INCOMING_DATA.write();
|
||||||
|
*w_path = Some(str.to_string());
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -48,9 +48,10 @@ pub struct Settings {
|
||||||
impl Settings {
|
impl Settings {
|
||||||
/// Main application directory name.
|
/// Main application directory name.
|
||||||
pub const MAIN_DIR_NAME: &'static str = ".grim";
|
pub const MAIN_DIR_NAME: &'static str = ".grim";
|
||||||
|
|
||||||
/// Crash report file name.
|
/// Crash report file name.
|
||||||
pub const CRASH_REPORT_FILE_NAME: &'static str = "crash.log";
|
pub const CRASH_REPORT_FILE_NAME: &'static str = "crash.log";
|
||||||
|
/// Application socket name.
|
||||||
|
pub const SOCKET_NAME: &'static str = "grim.sock";
|
||||||
|
|
||||||
/// Initialize settings with app and node configs.
|
/// Initialize settings with app and node configs.
|
||||||
fn init() -> Self {
|
fn init() -> Self {
|
||||||
|
@ -142,10 +143,10 @@ impl Settings {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get desktop application socket path.
|
/// Get desktop application socket path.
|
||||||
pub fn socket_path() -> String {
|
pub fn socket_path() -> PathBuf {
|
||||||
let mut socket_path = Self::base_path(None);
|
let mut socket_path = Self::base_path(None);
|
||||||
socket_path.push("grim.socket");
|
socket_path.push(Self::SOCKET_NAME);
|
||||||
socket_path.to_str().unwrap().to_string()
|
socket_path
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get configuration file path from provided name and sub-directory if needed.
|
/// Get configuration file path from provided name and sub-directory if needed.
|
||||||
|
|
Loading…
Reference in a new issue