1use allwright_surface_mobile::{ConnectOptions, LaunchOptions, MobilePlatform};
2use allwright_surface_mobile_android::{connect, dump_source, launch_app};
3use std::env;
4use std::error::Error;
5use std::fs;
6use std::path::PathBuf;
7
8fn main() -> Result<(), Box<dyn Error>> {
9 let args = parse_args(env::args().skip(1).collect())?;
10
11 let session = connect(&ConnectOptions {
12 platform: MobilePlatform::Android,
13 device: args.device,
14 adb_endpoint: args.adb_endpoint,
15 preserve_app_state: false,
16 timeout_ms: Some(10_000),
17 })?;
18
19 let page = launch_app(
20 &session.browser_session,
21 &LaunchOptions {
22 apk_path: Some(args.apk_path),
23 app_id: args.app_id,
24 launch_activity: args.activity,
25 stop_before_launch: args.stop_before_launch,
26 timeout_ms: Some(30_000),
27 },
28 )?;
29
30 let source = dump_source(&session.browser_session, &page.page_session)?;
31 fs::write(&args.output, source.source)?;
32
33 println!("{}", args.output.display());
34 Ok(())
35}
36
37#[derive(Debug)]
38struct SourceArgs {
39 apk_path: String,
40 output: PathBuf,
41 app_id: Option<String>,
42 activity: Option<String>,
43 device: Option<String>,
44 adb_endpoint: Option<String>,
45 stop_before_launch: bool,
46}
47
48fn parse_args(args: Vec<String>) -> Result<SourceArgs, Box<dyn Error>> {
49 let mut apk_path = String::new();
50 let mut output = None;
51 let mut app_id = None;
52 let mut activity = None;
53 let mut device = None;
54 let mut adb_endpoint = None;
55 let mut stop_before_launch = false;
56
57 let mut index = 0usize;
58 while index < args.len() {
59 match args[index].as_str() {
60 "--apk" => {
61 index += 1;
62 apk_path = required_value(&args, index, "--apk")?;
63 }
64 "--out" => {
65 index += 1;
66 output = Some(PathBuf::from(required_value(&args, index, "--out")?));
67 }
68 "--app-id" => {
69 index += 1;
70 app_id = Some(required_value(&args, index, "--app-id")?);
71 }
72 "--activity" => {
73 index += 1;
74 activity = Some(required_value(&args, index, "--activity")?);
75 }
76 "--device" => {
77 index += 1;
78 device = Some(required_value(&args, index, "--device")?);
79 }
80 "--adb-endpoint" => {
81 index += 1;
82 adb_endpoint = Some(required_value(&args, index, "--adb-endpoint")?);
83 }
84 "--stop-before-launch" => stop_before_launch = true,
85 "--help" | "-h" => {
86 print_usage();
87 std::process::exit(0);
88 }
89 other => {
90 return Err(format!("unknown argument `{other}`").into());
91 }
92 }
93 index += 1;
94 }
95
96 if apk_path.trim().is_empty() {
97 return Err("missing required `--apk <path>` argument".into());
98 }
99
100 Ok(SourceArgs {
101 apk_path,
102 output: output.unwrap_or_else(|| PathBuf::from("tmp/mobile-android-source.xml")),
103 app_id,
104 activity,
105 device,
106 adb_endpoint,
107 stop_before_launch,
108 })
109}
110
111fn required_value(args: &[String], index: usize, flag: &str) -> Result<String, Box<dyn Error>> {
112 args.get(index)
113 .cloned()
114 .ok_or_else(|| format!("missing value for `{flag}`").into())
115}
116
117fn print_usage() {
118 eprintln!(
119 "Usage: cargo run -p allwright-surface-mobile-android --example source -- \
120 --apk /path/to/app.apk \
121 --out tmp/mobile-android-source.xml \
122 [--app-id dev.allwright.sample] \
123 [--activity .MainActivity] \
124 [--device emulator-5554] \
125 [--adb-endpoint 192.168.1.10:5555] \
126 [--stop-before-launch]"
127 );
128}