Files
ai-app/app/src/android/bench_jni.rs
T

180 lines
5.2 KiB
Rust

use android_view::jni::{
JNIEnv, JavaVM,
objects::{GlobalRef, JObject, JValue},
};
/// `android.os.BatteryManager.BATTERY_PROPERTY_CURRENT_NOW` -- not exposed
/// as a constant anywhere reachable without the Android SDK jar, so named
/// here with its source rather than left as a bare `2`.
const BATTERY_PROPERTY_CURRENT_NOW: i32 = 2;
pub struct PlatformHandle {
vm: JavaVM,
view: GlobalRef,
}
impl PlatformHandle {
pub fn new(vm: JavaVM, view: GlobalRef) -> Self {
Self { vm, view }
}
fn context<'e>(&self, env: &mut JNIEnv<'e>) -> Option<JObject<'e>> {
env.call_method(
self.view.as_obj(),
"getContext",
"()Landroid/content/Context;",
&[],
)
.ok()?
.l()
.ok()
}
fn system_service<'e>(
&self,
env: &mut JNIEnv<'e>,
context: &JObject<'e>,
name: &str,
) -> Option<JObject<'e>> {
let jname = env.new_string(name).ok()?;
env.call_method(
context,
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
&[JValue::Object(jname.as_ref())],
)
.ok()?
.l()
.ok()
}
pub fn battery_current_ua(&self) -> Option<i32> {
let mut guard = self.vm.attach_current_thread().ok()?;
let env: &mut JNIEnv = &mut guard;
let context = self.context(env)?;
let battery_manager = self.system_service(env, &context, "batterymanager")?;
let value = env
.call_method(
&battery_manager,
"getIntProperty",
"(I)I",
&[JValue::Int(BATTERY_PROPERTY_CURRENT_NOW)],
)
.ok()?
.i()
.ok()?;
if value == 0 || value == i32::MIN {
None
} else {
Some(value)
}
}
/// Puts `text` on the system clipboard through `ClipboardManager` --
/// `true` only if the whole JNI chain (service lookup, `ClipData`,
/// `setPrimaryClip`) succeeded.
pub fn copy_to_clipboard(&self, label: &str, text: &str) -> bool {
self.try_copy_to_clipboard(label, text).is_some()
}
fn try_copy_to_clipboard(&self, label: &str, text: &str) -> Option<()> {
let mut guard = self.vm.attach_current_thread().ok()?;
let env: &mut JNIEnv = &mut guard;
let context = self.context(env)?;
let clipboard = self.system_service(env, &context, "clipboard")?;
let jlabel = env.new_string(label).ok()?;
let jtext = env.new_string(text).ok()?;
let clip = env
.call_static_method(
"android/content/ClipData",
"newPlainText",
"(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;",
&[
JValue::Object(jlabel.as_ref()),
JValue::Object(jtext.as_ref()),
],
)
.ok()?
.l()
.ok()?;
env.call_method(
&clipboard,
"setPrimaryClip",
"(Landroid/content/ClipData;)V",
&[JValue::Object(&clip)],
)
.ok()?;
Some(())
}
pub fn refresh_rate_hz(&self) -> Option<f32> {
let mut guard = self.vm.attach_current_thread().ok()?;
let env: &mut JNIEnv = &mut guard;
let display = env
.call_method(
self.view.as_obj(),
"getDisplay",
"()Landroid/view/Display;",
&[],
)
.ok()?
.l()
.ok()?;
if display.is_null() {
return None;
}
let rate = env
.call_method(&display, "getRefreshRate", "()F", &[])
.ok()?
.f()
.ok()?;
if rate > 0.0 { Some(rate) } else { None }
}
pub fn show_ime(&self) -> bool {
self.try_toggle_ime(true).unwrap_or(false)
}
pub fn hide_ime(&self) -> bool {
self.try_toggle_ime(false).unwrap_or(false)
}
fn try_toggle_ime(&self, show: bool) -> Option<bool> {
let mut guard = self.vm.attach_current_thread().ok()?;
let env: &mut JNIEnv = &mut guard;
let context = self.context(env)?;
let imm = self.system_service(env, &context, "input_method")?;
if show {
env.call_method(
&imm,
"showSoftInput",
"(Landroid/view/View;I)Z",
&[JValue::Object(self.view.as_obj()), JValue::Int(0)],
)
.ok()?
.z()
.ok()
} else {
let token = env
.call_method(
self.view.as_obj(),
"getWindowToken",
"()Landroid/os/IBinder;",
&[],
)
.ok()?
.l()
.ok()?;
env.call_method(
&imm,
"hideSoftInputFromWindow",
"(Landroid/os/IBinder;I)Z",
&[JValue::Object(&token), JValue::Int(0)],
)
.ok()?
.z()
.ok()
}
}
}