From 5689be4579cc593ad7a23e4c29c1afa66ce0a1a8 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 28 May 2024 00:59:28 +0300 Subject: [PATCH] android: images sharing --- android/app/src/main/AndroidManifest.xml | 11 ++++++++++ .../java/mw/gri/android/FileProvider.java | 7 +++++++ .../java/mw/gri/android/MainActivity.java | 19 +++++++++++++++++ .../src/main/java/mw/gri/android/Utils.java | 11 ++++++++++ android/app/src/main/res/xml/paths.xml | 4 ++++ src/gui/platform/android/mod.rs | 21 +++++++++++++++++++ src/gui/views/qr.rs | 2 +- 7 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 android/app/src/main/java/mw/gri/android/FileProvider.java create mode 100644 android/app/src/main/res/xml/paths.xml diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6fc69ab..e92b2a4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -19,11 +19,22 @@ android:supportsRtl="true" android:theme="@style/Theme.Main"> + + + + + diff --git a/android/app/src/main/java/mw/gri/android/FileProvider.java b/android/app/src/main/java/mw/gri/android/FileProvider.java new file mode 100644 index 0000000..c7bd8b0 --- /dev/null +++ b/android/app/src/main/java/mw/gri/android/FileProvider.java @@ -0,0 +1,7 @@ +package mw.gri.android; + +public class FileProvider extends androidx.core.content.FileProvider { + public FileProvider() { + super(R.xml.paths); + } +} diff --git a/android/app/src/main/java/mw/gri/android/MainActivity.java b/android/app/src/main/java/mw/gri/android/MainActivity.java index 7d6d384..f57d3e2 100644 --- a/android/app/src/main/java/mw/gri/android/MainActivity.java +++ b/android/app/src/main/java/mw/gri/android/MainActivity.java @@ -4,6 +4,7 @@ import android.Manifest; import android.content.*; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Process; @@ -17,6 +18,7 @@ import androidx.annotation.NonNull; import androidx.camera.core.*; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; import androidx.core.graphics.Insets; import androidx.core.view.DisplayCutoutCompat; import androidx.core.view.ViewCompat; @@ -24,6 +26,7 @@ import androidx.core.view.WindowInsetsCompat; import com.google.androidgamesdk.GameActivity; import com.google.common.util.concurrent.ListenableFuture; +import java.io.File; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -62,6 +65,11 @@ public class MainActivity extends GameActivity { @Override protected void onCreate(Bundle savedInstanceState) { + // Clear cache on start. + if (savedInstanceState == null) { + Utils.deleteDirectoryContent(new File(getExternalCacheDir().getPath()), false); + } + // Setup environment variables for native code. try { Os.setenv("HOME", getExternalFilesDir("").getPath(), true); @@ -70,6 +78,7 @@ public class MainActivity extends GameActivity { } catch (ErrnoException e) { throw new RuntimeException(e); } + super.onCreate(null); // Register receiver to finish activity from the BackgroundService. @@ -334,4 +343,14 @@ public class MainActivity extends GameActivity { // Pass image from camera into native code. public native void onCameraImage(byte[] buff, int rotation); + + // Called from native code to share image from provided path. + public void shareImage(String path) { + File file = new File(path); + Uri uri = FileProvider.getUriForFile(this, "mw.gri.android.fileprovider", file); + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, uri); + intent.setType("image/*"); + startActivity(Intent.createChooser(intent, "Share image")); + } } \ No newline at end of file diff --git a/android/app/src/main/java/mw/gri/android/Utils.java b/android/app/src/main/java/mw/gri/android/Utils.java index 3723c7d..47d1d56 100644 --- a/android/app/src/main/java/mw/gri/android/Utils.java +++ b/android/app/src/main/java/mw/gri/android/Utils.java @@ -8,6 +8,7 @@ import android.media.Image; import androidx.camera.core.ImageProxy; import java.io.ByteArrayOutputStream; +import java.io.File; import java.nio.ByteBuffer; public class Utils { @@ -135,4 +136,14 @@ public class Utils { rowStart += plane.getRowStride(); } } + + public static boolean deleteDirectoryContent(File directoryToBeDeleted, boolean deleteDirectory) { + File[] allContents = directoryToBeDeleted.listFiles(); + if (allContents != null) { + for (File file : allContents) { + deleteDirectoryContent(file, true); + } + } + return directoryToBeDeleted.delete(); + } } diff --git a/android/app/src/main/res/xml/paths.xml b/android/app/src/main/res/xml/paths.xml new file mode 100644 index 0000000..db42735 --- /dev/null +++ b/android/app/src/main/res/xml/paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/gui/platform/android/mod.rs b/src/gui/platform/android/mod.rs index 0159302..c2d25c1 100644 --- a/src/gui/platform/android/mod.rs +++ b/src/gui/platform/android/mod.rs @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::env; +use std::ffi::OsString; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; use lazy_static::lazy_static; use std::sync::Arc; use parking_lot::RwLock; @@ -116,6 +121,22 @@ impl PlatformCallbacks for Android { } fn share_data(&self, name: String, data: Vec) -> Result<(), std::io::Error> { + // Create file at cache dir. + let default_cache = OsString::from(dirs::cache_dir().unwrap()); + let mut cache = PathBuf::from(env::var_os("XDG_CACHE_HOME").unwrap_or(default_cache)); + cache.push("images"); + std::fs::create_dir_all(cache.to_str().unwrap())?; + cache.push(name); + let mut image = File::create_new(cache.clone()).unwrap(); + image.write_all(data.as_slice()).unwrap(); + image.sync_all().unwrap(); + // Call share modal at system. + let vm = unsafe { jni::JavaVM::from_raw(self.android_app.vm_as_ptr() as _) }.unwrap(); + let env = vm.attach_current_thread().unwrap(); + let arg_value = env.new_string(cache.to_str().unwrap()).unwrap(); + self.call_java_method("shareImage", + "(Ljava/lang/String;)V", + &[JValue::Object(&JObject::from(arg_value))]).unwrap(); Ok(()) } } diff --git a/src/gui/views/qr.rs b/src/gui/views/qr.rs index cbd443b..08d2e69 100644 --- a/src/gui/views/qr.rs +++ b/src/gui/views/qr.rs @@ -19,7 +19,7 @@ use std::thread; use egui::{SizeHint, TextureHandle, TextureOptions}; use egui::load::SizedTexture; use egui_extras::image::load_svg_bytes_with_size; -use image::{EncodableLayout, ExtendedColorType, ImageEncoder}; +use image::{ExtendedColorType, ImageEncoder}; use image::codecs::png::{CompressionType, FilterType, PngEncoder}; use qrcodegen::QrCode; use crate::gui::Colors;