Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
475 changes: 251 additions & 224 deletions cmd/fyne/internal/mobile/dex.go

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions driver/sensor/camera.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package sensor

import (
"image"
)

// A camera device is a device that has hardware support for taking photos.
//
// Since: 2.7
type CameraDevice interface {
// CapturePhoto opens a native photo capture view that allows the user to optionally
// take a photograph. It returns the image, a boolean to indicate if an image was
// taken, and any errors.
//
// Since: 2.7
CapturePhoto() (image.Image, bool, error)
}
196 changes: 115 additions & 81 deletions internal/driver/mobile/app/GoNativeActivity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.golang.app;

import java.util.Base64;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import android.app.Activity;
import android.app.NativeActivity;
import android.content.Context;
Expand All @@ -8,9 +11,12 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
Expand All @@ -29,39 +35,41 @@
import android.widget.TextView.OnEditorActionListener;

public class GoNativeActivity extends NativeActivity {
private static GoNativeActivity goNativeActivity;
private static final int FILE_OPEN_CODE = 1;
private static final int FILE_SAVE_CODE = 2;
private static GoNativeActivity goNativeActivity;
private static final int FILE_OPEN_CODE = 1;
private static final int FILE_SAVE_CODE = 2;
private static final int CAMERA_OPEN_CODE = 3;

private static final int DEFAULT_INPUT_TYPE = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
private static final int DEFAULT_INPUT_TYPE = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;

private static final int DEFAULT_KEYBOARD_CODE = 0;
private static final int SINGLELINE_KEYBOARD_CODE = 1;
private static final int NUMBER_KEYBOARD_CODE = 2;
private static final int PASSWORD_KEYBOARD_CODE = 3;
private static final int DEFAULT_KEYBOARD_CODE = 0;
private static final int SINGLELINE_KEYBOARD_CODE = 1;
private static final int NUMBER_KEYBOARD_CODE = 2;
private static final int PASSWORD_KEYBOARD_CODE = 3;

private native void filePickerReturned(String str);
private native void capturePhotoReturned(String str);
private native void insetsChanged(int top, int bottom, int left, int right);
private native void keyboardTyped(String str);
private native void keyboardDelete();
private native void backPressed();
private native void setDarkMode(boolean dark);

private EditText mTextEdit;
private boolean ignoreKey = false;
private boolean keyboardUp = false;
private EditText mTextEdit;
private boolean ignoreKey = false;
private boolean keyboardUp = false;

public GoNativeActivity() {
super();
goNativeActivity = this;
}
public GoNativeActivity() {
super();
goNativeActivity = this;
}

String getTmpdir() {
return getCacheDir().getAbsolutePath();
}
String getTmpdir() {
return getCacheDir().getAbsolutePath();
}

void updateLayout() {
try {
void updateLayout() {
try {
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
if (insets == null) {
return;
Expand All @@ -70,7 +78,7 @@ void updateLayout() {
insetsChanged(insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetBottom(),
insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetRight());
} catch (java.lang.NoSuchMethodError e) {
Rect insets = new Rect();
Rect insets = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(insets);

View view = findViewById(android.R.id.content).getRootView();
Expand Down Expand Up @@ -179,6 +187,15 @@ void doShowFileOpen(String mimes) {
startActivityForResult(Intent.createChooser(intent, "Open File"), FILE_OPEN_CODE);
}

static void showCameraOpen() {
goNativeActivity.doShowCameraOpen();
}

void doShowCameraOpen() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, CAMERA_OPEN_CODE);
}

static void showFileSave(String mimes, String filename) {
goNativeActivity.doShowFileSave(mimes, filename);
}
Expand All @@ -195,58 +212,58 @@ void doShowFileSave(String mimes, String filename) {
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(Intent.createChooser(intent, "Save File"), FILE_SAVE_CODE);
}
static int getRune(int deviceId, int keyCode, int metaState) {
try {
int rune = KeyCharacterMap.load(deviceId).get(keyCode, metaState);
if (rune == 0) {
return -1;
}
return rune;
} catch (KeyCharacterMap.UnavailableException e) {
return -1;
} catch (Exception e) {
Log.e("Fyne", "exception reading KeyCharacterMap", e);
return -1;
}
}
static int getRune(int deviceId, int keyCode, int metaState) {
try {
int rune = KeyCharacterMap.load(deviceId).get(keyCode, metaState);
if (rune == 0) {
return -1;
}
return rune;
} catch (KeyCharacterMap.UnavailableException e) {
return -1;
} catch (Exception e) {
Log.e("Fyne", "exception reading KeyCharacterMap", e);
return -1;
}
}

private void load() {
// Interestingly, NativeActivity uses a different method
// to find native code to execute, avoiding
// System.loadLibrary. The result is Java methods
// implemented in C with JNIEXPORT (and JNI_OnLoad) are not
// available unless an explicit call to System.loadLibrary
// is done. So we do it here, borrowing the name of the
// library from the same AndroidManifest.xml metadata used
// by NativeActivity.
try {
ActivityInfo ai = getPackageManager().getActivityInfo(
getIntent().getComponent(), PackageManager.GET_META_DATA);
if (ai.metaData == null) {
Log.e("Fyne", "loadLibrary: no manifest metadata found");
return;
}
String libName = ai.metaData.getString("android.app.lib_name");
System.loadLibrary(libName);
} catch (Exception e) {
Log.e("Fyne", "loadLibrary failed", e);
}
}
private void load() {
// Interestingly, NativeActivity uses a different method
// to find native code to execute, avoiding
// System.loadLibrary. The result is Java methods
// implemented in C with JNIEXPORT (and JNI_OnLoad) are not
// available unless an explicit call to System.loadLibrary
// is done. So we do it here, borrowing the name of the
// library from the same AndroidManifest.xml metadata used
// by NativeActivity.
try {
ActivityInfo ai = getPackageManager().getActivityInfo(
getIntent().getComponent(), PackageManager.GET_META_DATA);
if (ai.metaData == null) {
Log.e("Fyne", "loadLibrary: no manifest metadata found");
return;
}
String libName = ai.metaData.getString("android.app.lib_name");
System.loadLibrary(libName);
} catch (Exception e) {
Log.e("Fyne", "loadLibrary failed", e);
}
}

@Override
public void onCreate(Bundle savedInstanceState) {
load();
super.onCreate(savedInstanceState);
setupEntry();
updateTheme(getResources().getConfiguration());

@Override
public void onCreate(Bundle savedInstanceState) {
load();
super.onCreate(savedInstanceState);
setupEntry();
updateTheme(getResources().getConfiguration());

View view = findViewById(android.R.id.content).getRootView();
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
public void onLayoutChange (View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
GoNativeActivity.this.updateLayout();
}
});
View view = findViewById(android.R.id.content).getRootView();
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
public void onLayoutChange (View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
GoNativeActivity.this.updateLayout();
}
});
}

private void setupEntry() {
Expand Down Expand Up @@ -304,23 +321,40 @@ public void afterTextChanged(Editable s) {
});
}
});
}
}

@Override
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// unhandled request
if (requestCode != FILE_OPEN_CODE && requestCode != FILE_SAVE_CODE) {
if (requestCode != FILE_OPEN_CODE && requestCode != FILE_SAVE_CODE && requestCode != CAMERA_OPEN_CODE) {
return;
}

// dialog was cancelled
if (resultCode != Activity.RESULT_OK) {
filePickerReturned("");
return;
}
if (requestCode == CAMERA_OPEN_CODE) {
if (resultCode != Activity.RESULT_OK) {
// dialog was cancelled
capturePhotoReturned("");
return;
}

Bitmap photo = (Bitmap)data.getExtras().get("data");

ByteArrayOutputStream out = new ByteArrayOutputStream();
photo.compress(CompressFormat.JPEG, 90, out);

Uri uri = data.getData();
filePickerReturned(uri.toString());
String dataAsString = Base64.getEncoder().encodeToString(out.toByteArray());

capturePhotoReturned(dataAsString);
} else {
if (resultCode != Activity.RESULT_OK) {
// dialog was cancelled
filePickerReturned("");
return;
}

Uri uri = data.getData();
filePickerReturned(uri.toString());
}
}

@Override
Expand Down
16 changes: 16 additions & 0 deletions internal/driver/mobile/app/android.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ static jmethodID show_keyboard_method;
static jmethodID hide_keyboard_method;
static jmethodID show_file_open_method;
static jmethodID show_file_save_method;
static jmethodID show_camera_open_method;
static jmethodID finish_method;

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
Expand Down Expand Up @@ -95,6 +96,7 @@ void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_
show_keyboard_method = find_static_method(env, current_class, "showKeyboard", "(I)V");
hide_keyboard_method = find_static_method(env, current_class, "hideKeyboard", "()V");
show_file_open_method = find_static_method(env, current_class, "showFileOpen", "(Ljava/lang/String;)V");
show_camera_open_method = find_static_method(env, current_class, "showCameraOpen", "()V");
show_file_save_method = find_static_method(env, current_class, "showFileSave", "(Ljava/lang/String;Ljava/lang/String;)V");
finish_method = find_method(env, current_class, "finishActivity", "()V");

Expand Down Expand Up @@ -280,11 +282,25 @@ void showFileSave(JNIEnv* env, char* mimes, char* filename) {
);
}

void showCameraOpen(JNIEnv* env) {
jstring f = (*env)->NewStringUTF(env, " ");
(*env)->CallStaticVoidMethod(
env,
current_class,
show_camera_open_method
);
}

void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str) {
const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
filePickerReturned((char*)cstr);
}

void Java_org_golang_app_GoNativeActivity_capturePhotoReturned(JNIEnv *env, jclass clazz, jstring str) {
const char* cstr = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
capturePhotoReturned((char*)cstr);
}

void Java_org_golang_app_GoNativeActivity_insetsChanged(JNIEnv *env, jclass clazz, int top, int bottom, int left, int right) {
insetsChanged(top, bottom, left, right);
}
Expand Down
28 changes: 28 additions & 0 deletions internal/driver/mobile/app/android.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ void showKeyboard(JNIEnv* env, int keyboardType);
void hideKeyboard(JNIEnv* env);
void showFileOpen(JNIEnv* env, char* mimes);
void showFileSave(JNIEnv* env, char* mimes, char* filename);
void showCameraOpen(JNIEnv* env);
void finish(JNIEnv* env, jobject ctx);

void Java_org_golang_app_GoNativeActivity_filePickerReturned(JNIEnv *env, jclass clazz, jstring str);
void Java_org_golang_app_GoNativeActivity_capturePhotoReturned(JNIEnv *env, jclass clazz, jstring str);
*/
import "C"

Expand Down Expand Up @@ -350,6 +352,18 @@ func filePickerReturned(str *C.char) {
fileCallback = nil
}

var capturePhotoCallback func(string)

//export capturePhotoReturned
func capturePhotoReturned(str *C.char) {
if capturePhotoCallback == nil {
return
}

capturePhotoCallback(C.GoString(str))
capturePhotoCallback = nil
}

//export insetsChanged
func insetsChanged(top, bottom, left, right int) {
currentSize.InsetTopPx = top
Expand Down Expand Up @@ -425,6 +439,20 @@ func driverShowFileSavePicker(callback func(string, func()), filter *FileFilter,
}
}

func NativeCapturePhoto(callback func(string)) {
capturePhotoCallback = callback

save := func(vm, jniEnv, ctx uintptr) error {
env := (*C.JNIEnv)(unsafe.Pointer(jniEnv)) // not a Go heap pointer
C.showCameraOpen(env)
return nil
}

if err := mobileinit.RunOnJVM(save); err != nil {
log.Fatalf("app: %v", err)
}
}

var mainUserFn func(App)

var DisplayMetrics struct {
Expand Down
Loading
Loading