jni 0.15.2
jni: ^0.15.2 copied to clipboard
A library to access JNI from Dart and Flutter that acts as a support library for package:jnigen.
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:jni/jni.dart';
double randomDouble() {
final math = JClass.forName("java/lang/Math");
final random =
math.staticMethodId("random", "()D").call(math, jdouble.type, []);
math.release();
return random;
}
int uptime() {
return JClass.forName("android/os/SystemClock").use(
(systemClock) => systemClock
.staticMethodId("uptimeMillis", "()J")
.call(systemClock, jlong.type, []),
);
}
String backAndForth() {
final jstring = '🪓'.toJString();
final dartString = jstring.toDartString(releaseOriginal: true);
return dartString;
}
void quit() {
final activity = Jni.androidActivity(PlatformDispatcher.instance.engineId!);
if (activity == null) return;
activity.jClass
.instanceMethodId("finish", "()V")
.call(activity, jvoid.type, []);
activity.release();
}
void showToast(String text) {
final activity = Jni.androidActivity(PlatformDispatcher.instance.engineId!);
if (activity == null) return;
final toasterClass =
JClass.forName('com/github/dart_lang/jni_example/Toaster');
final makeText = toasterClass.staticMethodId(
'makeText',
'(Landroid/app/Activity;Landroid/content/Context;'
'Ljava/lang/CharSequence;I)'
'Lcom/github/dart_lang/jni_example/Toaster;');
final applicationContext = Jni.androidApplicationContext;
final toaster = makeText(toasterClass, JObject.type, [
activity,
applicationContext,
text.toJString(),
0,
]);
final show = toasterClass.instanceMethodId('show', '()V');
show(toaster, jvoid.type, []);
toaster.release();
applicationContext.release();
activity.release();
text.toJString().release();
}
void main() {
WidgetsFlutterBinding.ensureInitialized();
if (!Platform.isAndroid) {
Jni.spawn();
}
final examples = [
Example("Math.random()", () => randomDouble(), runInitially: false),
if (Platform.isAndroid) ...[
Example("Minutes of usage since reboot",
() => (uptime() / (60 * 1000)).floor()),
Example("Back and forth string conversion", () => backAndForth()),
Example("Device name", () {
final buildClass = JClass.forName("android/os/Build");
return buildClass
.staticFieldId("DEVICE", JString.type.signature)
.get(buildClass, JString.type)
.toDartString(releaseOriginal: true);
}),
Example(
"Package name",
() {
final activity =
Jni.androidActivity(PlatformDispatcher.instance.engineId!);
if (activity == null) return "Activity not available";
final packageName = activity.jClass
.instanceMethodId("getPackageName", "()Ljava/lang/String;")
.call(activity, JString.type, []);
activity.release();
return packageName;
},
),
Example(
"Show toast",
() => showToast("Hello from JNI!"),
runInitially: false,
),
Example(
"Quit",
quit,
runInitially: false,
),
]
];
runApp(MyApp(examples));
}
class Example {
String title;
dynamic Function() callback;
bool runInitially;
Example(this.title, this.callback, {this.runInitially = true});
}
class MyApp extends StatelessWidget {
const MyApp(this.examples, {super.key});
final List<Example> examples;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('JNI Examples'),
),
body: ListView.builder(
itemCount: examples.length,
itemBuilder: (context, i) {
final eg = examples[i];
return ExampleCard(eg);
},
),
),
);
}
}
class ExampleCard extends StatefulWidget {
const ExampleCard(this.example, {super.key});
final Example example;
@override
State<ExampleCard> createState() => _ExampleCardState();
}
class _ExampleCardState extends State<ExampleCard> {
Widget _pad(Widget w, double h, double v) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: h, vertical: v), child: w);
}
bool _run = false;
@override
void initState() {
super.initState();
_run = widget.example.runInitially;
}
@override
Widget build(BuildContext context) {
final eg = widget.example;
var result = "";
var hasError = false;
if (_run) {
try {
result = eg.callback().toString();
} on Exception catch (e) {
hasError = true;
result = e.toString();
} on Error catch (e) {
hasError = true;
result = e.toString();
}
}
var resultStyle = const TextStyle(fontFamily: "Monospace");
if (hasError) {
resultStyle = const TextStyle(fontFamily: "Monospace", color: Colors.red);
}
return Card(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(eg.title,
softWrap: true,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
_pad(
Text(result.toString(), softWrap: true, style: resultStyle), 8, 16),
_pad(
ElevatedButton(
child: Text(_run ? "Run again" : "Run"),
onPressed: () => setState(() => _run = true),
),
8,
8),
]),
);
}
}