Skip to content

Commit

Permalink
android: opt-in material design
Browse files Browse the repository at this point in the history
  • Loading branch information
zenith391 committed Jan 21, 2023
1 parent a410469 commit 9b93794
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 48 deletions.
2 changes: 1 addition & 1 deletion android/Sdk.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,7 @@ const zig_targets = struct {
.os_tag = android_os,
.abi = android_abi,
.cpu_model = .baseline,
.cpu_features_add = std.Target.aarch64.featureSet(&.{.v8a}),
.cpu_features_add = std.Target.aarch64.featureSet(&.{.v8a, .reserve_x18}),
};

const arm = std.zig.CrossTarget{
Expand Down
Binary file modified android/classes.dex
Binary file not shown.
1 change: 1 addition & 0 deletions build_capy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub fn install(step: *std.build.LibExeObjStep, options: CapyBuildOptions) !void
.linux, .freebsd => {
if (step.target.toTarget().isAndroid()) {
// TODO: automatically download the SDK and NDK and build tools?
// TODO: download Material components by parsing Maven?
const sdk = AndroidSdk.init(step.builder, null, .{});
const mode = step.build_mode;

Expand Down
105 changes: 58 additions & 47 deletions src/backends/android/backend.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var hasInit: bool = false;
var theApp: *backendExport.AndroidApp = undefined;

const NativeActivity = android.NativeActivity;
const USE_MATERIAL = false;

pub fn init() BackendError!void {
if (!hasInit) {
Expand Down Expand Up @@ -135,7 +136,7 @@ pub fn Events(comptime T: type) type {
// As long as we treat the Long as an unsigned number on our side, this supports all possible
// 64-bit addresses.
const Long = jni.findClass("java/lang/Long") catch return error.InitializationError;
const dataAddress = Long.newObject("(J)V", .{ @ptrToInt(data) }) catch return error.InitializationError;
const dataAddress = Long.newObject("(J)V", .{@ptrToInt(data)}) catch return error.InitializationError;

const View = jni.findClass("android/view/View") catch return error.InitializationError;
View.callVoidMethod(widget, "setTag", "(ILjava/lang/Object;)V", .{ EVENT_USER_DATA_KEY, dataAddress }) catch return error.InitializationError;
Expand Down Expand Up @@ -297,13 +298,15 @@ pub const Button = struct {

pub usingnamespace Events(Button);

const CLASS = if (USE_MATERIAL) "com/google/android/material/button/MaterialButton" else "android/widget/Button";

pub fn create() BackendError!Button {
var view: PeerType = undefined;
theApp.runOnUiThread(struct {
fn callback(view_ptr: *PeerType) void {
std.log.info("Creating android.widget.Button", .{});
std.log.info("Creating " ++ CLASS, .{});
const jni = theApp.getJni();
const AndroidButton = jni.findClass("android/widget/Button") catch unreachable;
const AndroidButton = jni.findClass(CLASS) catch unreachable;
const peer = (AndroidButton.newObject("(Landroid/content/Context;)V", .{theApp.activity.clazz}) catch unreachable).?;
Button.setupEvents(peer) catch unreachable;
view_ptr.* = jni.invokeJniNoException(.NewGlobalRef, .{peer}).?;
Expand Down Expand Up @@ -337,7 +340,7 @@ pub const Label = struct {
std.log.info("Creating android.widget.TextView", .{});
const jni = theApp.getJni();
const TextView = jni.findClass("android/widget/TextView") catch unreachable;
const peer = (TextView.newObject("(Landroid/content/Context;)V", .{ theApp.activity.clazz }) catch unreachable).?;
const peer = (TextView.newObject("(Landroid/content/Context;)V", .{theApp.activity.clazz}) catch unreachable).?;
Label.setupEvents(peer) catch unreachable;
view_ptr.* = jni.invokeJniNoException(.NewGlobalRef, .{peer}).?;
}
Expand All @@ -355,7 +358,7 @@ pub const Label = struct {

const jni = theApp.getJni();
const TextView = jni.findClass("android/widget/TextView") catch unreachable;
TextView.callVoidMethod(self.peer, "setText", "(Ljava/lang/CharSequence;)V", .{ jni.newString(self.nullTerminated.?) catch unreachable }) catch unreachable;
TextView.callVoidMethod(self.peer, "setText", "(Ljava/lang/CharSequence;)V", .{jni.newString(self.nullTerminated.?) catch unreachable}) catch unreachable;
}
}.callback, .{ self_ptr, text_ptr }) catch unreachable;
}
Expand All @@ -381,7 +384,7 @@ pub const TextField = struct {
const peer = (EditText.newObject("(Landroid/content/Context;)V", .{theApp.activity.clazz}) catch unreachable).?;
TextField.setupEvents(peer) catch unreachable;
view_ptr.* = jni.invokeJniNoException(.NewGlobalRef, .{peer}).?;

const textChanged = (getEventListener("android/text/TextWatcher", &onChangedText, view_ptr.*) catch unreachable).?;
EditText.callVoidMethod(view_ptr.*, "addTextChangedListener", "(Landroid/text/TextWatcher;)V", .{textChanged}) catch unreachable;
}
Expand All @@ -408,7 +411,7 @@ pub const TextField = struct {
const EditText = jni.findClass("android/widget/EditText") catch unreachable;
const text = EditText.callObjectMethod(self.peer, "getText", "()Landroid/text/Editable;", .{}) catch unreachable;
const string = jni.callObjectMethod(text, "toString", "()Ljava/lang/String;", .{}) catch unreachable;
const length = @intCast(usize, jni.invokeJniNoException(.GetStringUTFLength, .{ string }));
const length = @intCast(usize, jni.invokeJniNoException(.GetStringUTFLength, .{string}));
const chars = jni.invokeJniNoException(.GetStringUTFChars, .{ string, null });
// TODO: call ReleaseStringUTFChars
return chars[0..length];
Expand Down Expand Up @@ -466,12 +469,14 @@ pub const Canvas = struct {
};

pub fn setColorByte(self: *DrawContext, color: lib.Color) void {
self.paintClass.callVoidMethod(self.paint, "setARGB", "(IIII)V", .{
@as(android.jint, color.alpha),
@as(android.jint, color.red),
@as(android.jint, color.green),
@as(android.jint, color.blue),
}) catch unreachable;
_ = self;
_ = color;
// self.paintClass.callVoidMethod(self.paint, "setARGB", "(IIII)V", .{
// @as(android.jint, color.alpha),
// @as(android.jint, color.red),
// @as(android.jint, color.green),
// @as(android.jint, color.blue),
// }) catch unreachable;
}

pub fn setColor(self: *DrawContext, r: f32, g: f32, b: f32) void {
Expand All @@ -491,30 +496,40 @@ pub const Canvas = struct {
pub fn rectangle(self: *DrawContext, x: i32, y: i32, w: u32, h: u32) void {
const PaintStyle = self.jni.findClass("android/graphics/Paint$Style") catch unreachable;
const FILL = PaintStyle.getStaticObjectField("FILL", "Landroid/graphics/Paint$Style;") catch unreachable;
self.paintClass.callVoidMethod(self.paint, "setStyle", "(Landroid/graphics/Paint$Style;)V", .{
FILL
}) catch unreachable;
const color = self.paintClass.callIntMethod(self.paint, "getColor", "()I", .{}) catch unreachable;
const red = (color & 0xFF0000) >> 16;
const green = (color & 0x00FF00) >> 8;
const blue = (color & 0x0000FF);

_ = self.class.callBooleanMethod(self.canvas, "clipRect", "(IIII)Z", .{
x, y,
@intCast(i32, w), @intCast(i32, h),
}) catch unreachable;
self.paintClass.callVoidMethod(self.paint, "setStyle", "(Landroid/graphics/Paint$Style;)V", .{FILL}) catch unreachable;
_ = x;
_ = y;
_ = w;
_ = h;

self.class.callVoidMethod(self.canvas, "drawRGB", "(III)V", .{
@as(android.jint, red),
@as(android.jint, green),
@as(android.jint, blue),
}) catch unreachable;
// self.class.callVoidMethod(self.canvas, "drawPaint", "(Landroid/graphics/Paint;)V", .{
// self.paint,
// }) catch unreachable;
// const color = self.paintClass.callIntMethod(self.paint, "getColor", "()I", .{}) catch unreachable;
// const red = (color & 0xFF0000) >> 16;
// const green = (color & 0x00FF00) >> 8;
// const blue = (color & 0x0000FF);

// _ = self.class.callBooleanMethod(self.canvas, "clipRect", "(IIII)Z", .{
// x, y,
// @intCast(i32, w), @intCast(i32, h),
// }) catch unreachable;

// self.class.callVoidMethod(self.canvas, "drawRGB", "(III)V", .{
// @as(android.jint, red),
// @as(android.jint, green),
// @as(android.jint, blue),
// }) catch unreachable;

self.class.callVoidMethod(self.canvas, "drawRect", "(FFFFLandroid/graphics/Paint;)V", .{
@intToFloat(f32, x+100),
@intToFloat(f32, y+100),
@intToFloat(f32, x+@intCast(i32, w)),
@intToFloat(f32, y+@intCast(i32, h)),
//@intToFloat(f32, x+100),
//@intToFloat(f32, y+100),
//@intToFloat(f32, x+@intCast(i32, w)),
//@intToFloat(f32, y+@intCast(i32, h)),
@as(android.jfloat, 0.0),
@as(android.jfloat, 0.0),
@as(android.jfloat, 1000.0),
@as(android.jfloat, 1000.0),
self.paint,
}) catch unreachable;
}
Expand Down Expand Up @@ -550,15 +565,13 @@ pub const Canvas = struct {
pub fn ellipse(self: *DrawContext, x: i32, y: i32, w: u32, h: u32) void {
const PaintStyle = self.jni.findClass("android/graphics/Paint$Style") catch unreachable;
const FILL = PaintStyle.getStaticObjectField("FILL", "Landroid/graphics/Paint$Style;") catch unreachable;
self.paintClass.callVoidMethod(self.paint, "setStyle", "(Landroid/graphics/Paint$Style;)V", .{
FILL
}) catch unreachable;
self.paintClass.callVoidMethod(self.paint, "setStyle", "(Landroid/graphics/Paint$Style;)V", .{FILL}) catch unreachable;

self.class.callVoidMethod(self.canvas, "drawRect", "(FFFFLandroid/graphics/Paint;)V", .{
self.class.callVoidMethod(self.canvas, "drawOval", "(FFFFLandroid/graphics/Paint;)V", .{
@intToFloat(f32, x),
@intToFloat(f32, y),
@intToFloat(f32, x+@intCast(i32, w)),
@intToFloat(f32, y+@intCast(i32, h)),
@intToFloat(f32, x + @intCast(i32, w)),
@intToFloat(f32, y + @intCast(i32, h)),
self.paint,
}) catch unreachable;
}
Expand Down Expand Up @@ -589,13 +602,13 @@ pub const Canvas = struct {
const class = jni.findClass("android/graphics/Canvas") catch unreachable;
const paintClass = jni.findClass("android/graphics/Paint") catch unreachable;
const paint = paintClass.newObject("()V", .{}) catch unreachable;
var ctx = Canvas.DrawContext { .canvas = canvas, .jni = jni, .class = class, .paintClass = paintClass, .paint = paint };
if (eventData.user.drawHandler) |handler| {
handler(&ctx, eventData.userdata);
}
var ctx = Canvas.DrawContext{ .canvas = canvas, .jni = jni, .class = class, .paintClass = paintClass, .paint = paint };
if (eventData.class.drawHandler) |handler| {
handler(&ctx, eventData.classUserdata);
}
if (eventData.user.drawHandler) |handler| {
handler(&ctx, eventData.userdata);
}
return null;
}

Expand All @@ -614,7 +627,7 @@ pub const Canvas = struct {
const ClassLoader = jni.findClass("java/lang/ClassLoader") catch unreachable;
const strClassName = jni.newString("CanvasView") catch unreachable;
defer jni.invokeJniNoException(.DeleteLocalRef, .{strClassName});
const CanvasViewClass = android.JNI.Class { .jni = jni, .class = ClassLoader.callObjectMethod(cls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", .{strClassName}) catch unreachable };
const CanvasViewClass = android.JNI.Class{ .jni = jni, .class = ClassLoader.callObjectMethod(cls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;", .{strClassName}) catch unreachable };
const methods = [_]android.JNINativeMethod{
.{
.name = "onDraw0",
Expand Down Expand Up @@ -811,8 +824,6 @@ pub const backendExport = struct {
if (std.Thread.getCurrentId() == self.uiThreadId) {
@call(.auto, func, args);
return;
} else {
std.log.info("request from thread {d}", .{ std.Thread.getCurrentId() });
}

self.uiThreadMutex.lock();
Expand Down

0 comments on commit 9b93794

Please sign in to comment.