-
Notifications
You must be signed in to change notification settings - Fork 43
/
android.rs
127 lines (116 loc) · 4.29 KB
/
android.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::{Browser, BrowserOptions, Error, ErrorKind, Result};
use jni::objects::JValue;
use std::process::{Command, Stdio};
/// Deal with opening of browsers on Android. Only [Browser::Default] is supported, and
/// in options, only [BrowserOptions::dry_run] is honoured.
#[inline]
pub fn open_browser_internal(browser: Browser, url: &str, options: &BrowserOptions) -> Result<()> {
match browser {
Browser::Default => open_browser_default(url, options),
_ => Err(Error::new(
ErrorKind::NotFound,
"only default browser supported",
)),
}
}
/// Open the default browser
#[inline]
fn open_browser_default(url: &str, options: &BrowserOptions) -> Result<()> {
// always return true for a dry run
if options.dry_run {
return Ok(());
}
// first we try to see if we're in a termux env, because if we are, then
// the android context may not have been initialized, and it'll panic
if try_for_termux(url, options).is_ok() {
return Ok(());
}
// Create a VM for executing Java calls
let ctx = ndk_context::android_context();
let vm = unsafe { jni::JavaVM::from_raw(ctx.vm() as _) }.map_err(|_| -> Error {
Error::new(
ErrorKind::NotFound,
"Expected to find JVM via ndk_context crate",
)
})?;
let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
let env = vm.attach_current_thread().map_err(|_| -> Error {
Error::new(ErrorKind::Other, "Failed to attach current thread")
})?;
// Create ACTION_VIEW object
let intent_class = env
.find_class("android/content/Intent")
.map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "Failed to find Intent class") })?;
let action_view = env
.get_static_field(intent_class, "ACTION_VIEW", "Ljava/lang/String;")
.map_err(|_| -> Error {
Error::new(ErrorKind::NotFound, "Failed to get intent.ACTION_VIEW")
})?;
// Create Uri object
let uri_class = env
.find_class("android/net/Uri")
.map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "Failed to find Uri class") })?;
let url = env
.new_string(url)
.map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to create JNI string") })?;
let uri = env
.call_static_method(
uri_class,
"parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
&[JValue::Object(*url)],
)
.map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to parse JNI Uri") })?;
// Create new ACTION_VIEW intent with the uri
let intent = env
.alloc_object(intent_class)
.map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to allocate intent") })?;
env.call_method(
intent,
"<init>",
"(Ljava/lang/String;Landroid/net/Uri;)V",
&[action_view, uri],
)
.map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to initialize intent") })?;
// Start the intent activity.
env.call_method(
activity,
"startActivity",
"(Landroid/content/Intent;)V",
&[JValue::Object(intent)],
)
.map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to start activity") })?;
Ok(())
}
/// Attemps to open a browser assuming a termux environment
///
/// See [issue #53](https://github.com/amodm/webbrowser-rs/issues/53)
#[inline]
fn try_for_termux(url: &str, options: &BrowserOptions) -> Result<()> {
use std::env;
if env::var("TERMUX_VERSION").is_ok() {
// return true on dry-run given that termux-open command is guaranteed to be present
if options.dry_run {
return Ok(());
}
let mut cmd = Command::new("termux-open");
cmd.arg(url);
if options.suppress_output {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
}
cmd.status().and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"command present but exited unsuccessfully",
))
}
})
} else {
Err(Error::new(ErrorKind::Other, "Not a termux environment"))
}
}