android: images sharing

This commit is contained in:
ardocrat 2024-05-28 00:59:28 +03:00
parent 493d801aad
commit 5689be4579
7 changed files with 74 additions and 1 deletions

View file

@ -19,11 +19,22 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.Main"> android:theme="@style/Theme.Main">
<provider
android:name=".FileProvider"
android:authorities="mw.gri.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
<activity <activity
android:launchMode="singleTask" android:launchMode="singleTask"
android:name=".MainActivity" android:name=".MainActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:exported="true"> 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" />

View file

@ -0,0 +1,7 @@
package mw.gri.android;
public class FileProvider extends androidx.core.content.FileProvider {
public FileProvider() {
super(R.xml.paths);
}
}

View file

@ -4,6 +4,7 @@ import android.Manifest;
import android.content.*; 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.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Process; import android.os.Process;
@ -17,6 +18,7 @@ import androidx.annotation.NonNull;
import androidx.camera.core.*; import androidx.camera.core.*;
import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.DisplayCutoutCompat; import androidx.core.view.DisplayCutoutCompat;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
@ -24,6 +26,7 @@ import androidx.core.view.WindowInsetsCompat;
import com.google.androidgamesdk.GameActivity; import com.google.androidgamesdk.GameActivity;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -62,6 +65,11 @@ public class MainActivity extends GameActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { 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. // Setup environment variables for native code.
try { try {
Os.setenv("HOME", getExternalFilesDir("").getPath(), true); Os.setenv("HOME", getExternalFilesDir("").getPath(), true);
@ -70,6 +78,7 @@ public class MainActivity extends GameActivity {
} catch (ErrnoException e) { } catch (ErrnoException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
super.onCreate(null); super.onCreate(null);
// Register receiver to finish activity from the BackgroundService. // Register receiver to finish activity from the BackgroundService.
@ -334,4 +343,14 @@ public class MainActivity extends GameActivity {
// Pass image from camera into native code. // Pass image from camera into native code.
public native void onCameraImage(byte[] buff, int rotation); 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"));
}
} }

View file

@ -8,6 +8,7 @@ import android.media.Image;
import androidx.camera.core.ImageProxy; import androidx.camera.core.ImageProxy;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
public class Utils { public class Utils {
@ -135,4 +136,14 @@ public class Utils {
rowStart += plane.getRowStride(); 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();
}
} }

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-cache-path name="images" path="images/" />
</paths>

View file

@ -12,6 +12,11 @@
// 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 std::env;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::sync::Arc; use std::sync::Arc;
use parking_lot::RwLock; use parking_lot::RwLock;
@ -116,6 +121,22 @@ 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.
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(()) Ok(())
} }
} }

View file

@ -19,7 +19,7 @@ use std::thread;
use egui::{SizeHint, TextureHandle, TextureOptions}; use egui::{SizeHint, TextureHandle, TextureOptions};
use egui::load::SizedTexture; use egui::load::SizedTexture;
use egui_extras::image::load_svg_bytes_with_size; 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 image::codecs::png::{CompressionType, FilterType, PngEncoder};
use qrcodegen::QrCode; use qrcodegen::QrCode;
use crate::gui::Colors; use crate::gui::Colors;