1- mod utils;
2-
3- use anyhow:: { Context , Result } ;
4- use guidon:: { GitOptions , Guidon , TryNew } ;
5- use itertools:: Itertools ;
6- use serde:: { Deserialize , Serialize } ;
7-
8- use std:: {
9- collections:: BTreeMap ,
10- path:: { Path , PathBuf } ,
11- process:: { Command , ExitStatus } ,
12- } ;
13-
14- use utils:: { find_adb, find_gradle} ;
15-
16- pub const VERSION : & str = env ! ( "CARGO_PKG_VERSION" ) ;
17- pub const DEFAULT_MAIN_ACTIVITY : & str = "MainActivity" ;
18-
19- const DEFAULT_TEMPLATE_REPO : & str = "https://github.com/SyedAhkam/android-cli-template" ;
20- const TEMPLATE_REV : & str = "master" ;
21-
22- const DOTFILE_COMMENT : & str = "// DO NOT MODIFY; Generated by Android CLI for internal usage.\n " ;
23-
24- #[ derive( Debug , Serialize , Deserialize ) ]
25- pub struct DotAndroid {
26- pub project_name : String ,
27- pub package_id : String ,
28- pub gen_at_version : String ,
29- pub main_activity_name : String ,
30- }
31-
32- pub fn copy_template ( dest : & Path , vars : BTreeMap < String , String > ) -> Result < ( ) > {
33- let git_options = GitOptions :: builder ( )
34- . repo ( DEFAULT_TEMPLATE_REPO )
35- . rev ( TEMPLATE_REV )
36- . build ( )
37- . unwrap ( ) ;
38-
39- let mut guidon = Guidon :: try_new ( git_options) ?;
40-
41- guidon. variables ( vars) ;
42- guidon. apply_template ( dest) ?;
43-
44- Ok ( ( ) )
45- }
46-
47- pub fn create_local_properties_file ( root : & Path , sdk_path : & str ) -> Result < ( ) > {
48- let sdk_path = Path :: new ( sdk_path) ;
49- let prop_file_path = PathBuf :: new ( ) . join ( root) . join ( "local.properties" ) ;
50-
51- // It is necessary to escape paths on windows
52- let content = if cfg ! ( windows) {
53- format ! ( "sdk.dir={}" , sdk_path. display( ) ) . replace ( "\\ " , "\\ \\ " )
54- } else {
55- format ! ( "sdk.dir={}" , sdk_path. display( ) )
56- } ;
57-
58- if sdk_path. exists ( ) {
59- std:: fs:: write ( prop_file_path, content) . context ( "Unable to write local.properties file" ) ?;
60- } else {
61- eprintln ! ( "warning: did not create local.properties file because of invalid sdk path" )
62- }
63-
64- Ok ( ( ) )
65- }
66-
67- pub fn invoke_gradle_command ( cmd : & str ) -> Result < ExitStatus > {
68- let gradle_path = find_gradle ( ) . context ( "ERROR: Gradle not found on system" ) ?;
69-
70- let mut run = Command :: new ( gradle_path) ;
71- run. arg ( cmd) ;
72-
73- println ! (
74- "Invoking Gradle: {} {}" ,
75- & run. get_program( ) . to_string_lossy( ) ,
76- & run. get_args( ) . map( |arg| arg. to_string_lossy( ) ) . join( " " )
77- ) ;
78-
79- Ok ( run. status ( ) ?)
80- }
81-
82- pub fn invoke_adb_command ( args : & [ & str ] ) -> Result < ExitStatus > {
83- let adb_path = find_adb ( ) . context ( "ERROR: ADB not found on system" ) ?;
84-
85- let mut run = Command :: new ( adb_path) ;
86- run. args ( args) ;
87-
88- println ! (
89- "Invoking ADB: {} {}" ,
90- & run. get_program( ) . to_string_lossy( ) ,
91- & run. get_args( ) . map( |arg| arg. to_string_lossy( ) ) . join( " " )
92- ) ;
93-
94- Ok ( run. status ( ) ?)
95- }
96-
97- pub fn create_dot_android (
98- dest : & Path ,
99- project_name : String ,
100- package_id : String ,
101- main_activity_name : Option < String > ,
102- ) -> Result < ( ) > {
103- // Construct the structure
104- let dot_android = DotAndroid {
105- package_id,
106- project_name,
107- gen_at_version : VERSION . to_owned ( ) ,
108- main_activity_name : main_activity_name. unwrap_or ( DEFAULT_MAIN_ACTIVITY . to_owned ( ) ) ,
109- } ;
110-
111- // Serialize into Ron
112- let mut ron_contents =
113- ron:: ser:: to_string_pretty ( & dot_android, ron:: ser:: PrettyConfig :: default ( ) ) ?;
114-
115- // Add a comment at top
116- ron_contents. insert_str ( 0 , DOTFILE_COMMENT ) ;
117-
118- // Write to file
119- let path = PathBuf :: from ( dest) . join ( ".android" ) ;
120- std:: fs:: write ( path, ron_contents) . context ( "failed to write .android file" ) ?;
121-
122- Ok ( ( ) )
123- }
124-
125- pub fn get_dot_android ( ) -> Option < DotAndroid > {
126- if let Ok ( contents) = std:: fs:: read_to_string ( ".android" ) {
127- return ron:: from_str :: < DotAndroid > ( & contents) . ok ( ) ;
128- } ;
129-
130- None
131- }
132-
133- pub fn install_apk ( is_release : bool ) -> Result < ExitStatus > {
134- let output_dir = PathBuf :: from ( "app/build/outputs/apk" ) ;
135-
136- let apk_path = match is_release {
137- true => output_dir. join ( "release/app-release.apk" ) ,
138- false => output_dir. join ( "debug/app-debug.apk" ) ,
139- } ;
140-
141- Ok ( invoke_adb_command ( & [ "install" , apk_path. to_str ( ) . unwrap ( ) ] )
142- . context ( "failed to run adb command" ) ?)
143- }
144-
145- pub fn trigger_build ( is_release : bool ) -> Result < ExitStatus > {
146- // Decide gradle subcommand to use
147- let cmd = match is_release {
148- true => "assembleRelease" ,
149- false => "assembleDebug" ,
150- } ;
151-
152- Ok ( invoke_gradle_command ( cmd) . context ( "failed to invoke gradle command" ) ?)
153- }
154-
155- pub fn launch_activity ( package_id : String , activity_name : String ) -> Result < ExitStatus > {
156- Ok ( invoke_adb_command ( & [
157- "shell" ,
158- "am" ,
159- "start" ,
160- "-n" ,
161- & format ! ( "{}/.{}" , package_id, activity_name) ,
162- ] )
163- . context ( "failed to invoke adb command" ) ?)
164- }
165-
166- pub fn query_devices ( ) -> Result < ExitStatus > {
167- Ok ( invoke_adb_command ( & [ "devices" ] ) . context ( "failed to invoke adb command" ) ?)
168- }
1+ mod utils;
2+
3+ use anyhow:: { Context , Result } ;
4+ use guidon:: { GitOptions , Guidon , TryNew } ;
5+ use itertools:: Itertools ;
6+ use serde:: { Deserialize , Serialize } ;
7+
8+ use std:: {
9+ collections:: BTreeMap ,
10+ path:: { Path , PathBuf } ,
11+ process:: { Command , ExitStatus } ,
12+ } ;
13+
14+ use utils:: { find_adb, find_gradle} ;
15+
16+ pub const VERSION : & str = env ! ( "CARGO_PKG_VERSION" ) ;
17+ pub const DEFAULT_MAIN_ACTIVITY : & str = "MainActivity" ;
18+
19+ const DEFAULT_TEMPLATE_REPO : & str = "https://github.com/SyedAhkam/android-cli-template" ;
20+ const TEMPLATE_REV : & str = "master" ;
21+
22+ const DOTFILE_COMMENT : & str = "// DO NOT MODIFY; Generated by Android CLI for internal usage.\n " ;
23+
24+ #[ derive( Debug , Serialize , Deserialize ) ]
25+ pub struct DotAndroid {
26+ pub project_name : String ,
27+ pub package_id : String ,
28+ pub gen_at_version : String ,
29+ pub main_activity_name : String ,
30+ }
31+
32+ pub fn copy_template ( dest : & Path , vars : BTreeMap < String , String > ) -> Result < ( ) > {
33+ let git_options = GitOptions :: builder ( )
34+ . repo ( DEFAULT_TEMPLATE_REPO )
35+ . rev ( TEMPLATE_REV )
36+ . build ( )
37+ . unwrap ( ) ;
38+
39+ let mut guidon = Guidon :: try_new ( git_options) ?;
40+
41+ guidon. variables ( vars) ;
42+ guidon. apply_template ( dest) ?;
43+
44+ Ok ( ( ) )
45+ }
46+
47+ pub fn create_local_properties_file ( root : & Path , sdk_path : & str ) -> Result < ( ) > {
48+ let sdk_path = Path :: new ( sdk_path) ;
49+ let prop_file_path = PathBuf :: new ( ) . join ( root) . join ( "local.properties" ) ;
50+
51+ // It is necessary to escape paths on windows
52+ let content = if cfg ! ( windows) {
53+ format ! ( "sdk.dir={}" , sdk_path. display( ) ) . replace ( "\\ " , "\\ \\ " )
54+ } else {
55+ format ! ( "sdk.dir={}" , sdk_path. display( ) )
56+ } ;
57+
58+ if sdk_path. exists ( ) {
59+ std:: fs:: write ( prop_file_path, content) . context ( "Unable to write local.properties file" ) ?;
60+ } else {
61+ eprintln ! ( "warning: did not create local.properties file because of invalid sdk path" )
62+ }
63+
64+ Ok ( ( ) )
65+ }
66+
67+ pub fn invoke_gradle_command ( cmd : & str ) -> Result < ExitStatus > {
68+ let gradle_path = find_gradle ( ) . context ( "ERROR: Gradle not found on system" ) ?;
69+
70+ let mut run = Command :: new ( gradle_path) ;
71+ run. arg ( cmd) ;
72+
73+ println ! (
74+ "Invoking Gradle: {} {}" ,
75+ & run. get_program( ) . to_string_lossy( ) ,
76+ & run. get_args( ) . map( |arg| arg. to_string_lossy( ) ) . join( " " )
77+ ) ;
78+
79+ Ok ( run. status ( ) ?)
80+ }
81+
82+ pub fn invoke_adb_command ( args : & [ & str ] ) -> Result < ExitStatus > {
83+ let adb_path = find_adb ( ) . context ( "ERROR: ADB not found on system" ) ?;
84+
85+ let mut run = Command :: new ( adb_path) ;
86+ run. args ( args) ;
87+
88+ println ! (
89+ "Invoking ADB: {} {}" ,
90+ & run. get_program( ) . to_string_lossy( ) ,
91+ & run. get_args( ) . map( |arg| arg. to_string_lossy( ) ) . join( " " )
92+ ) ;
93+
94+ Ok ( run. status ( ) ?)
95+ }
96+
97+ pub fn create_dot_android (
98+ dest : & Path ,
99+ project_name : String ,
100+ package_id : String ,
101+ main_activity_name : Option < String > ,
102+ ) -> Result < ( ) > {
103+ // Construct the structure
104+ let dot_android = DotAndroid {
105+ package_id,
106+ project_name,
107+ gen_at_version : VERSION . to_owned ( ) ,
108+ main_activity_name : main_activity_name. unwrap_or ( DEFAULT_MAIN_ACTIVITY . to_owned ( ) ) ,
109+ } ;
110+
111+ // Serialize into Ron
112+ let mut ron_contents =
113+ ron:: ser:: to_string_pretty ( & dot_android, ron:: ser:: PrettyConfig :: default ( ) ) ?;
114+
115+ // Add a comment at top
116+ ron_contents. insert_str ( 0 , DOTFILE_COMMENT ) ;
117+
118+ // Write to file
119+ let path = PathBuf :: from ( dest) . join ( ".android" ) ;
120+ std:: fs:: write ( path, ron_contents) . context ( "failed to write .android file" ) ?;
121+
122+ Ok ( ( ) )
123+ }
124+
125+ pub fn get_dot_android ( ) -> Option < DotAndroid > {
126+ if let Ok ( contents) = std:: fs:: read_to_string ( ".android" ) {
127+ return ron:: from_str :: < DotAndroid > ( & contents) . ok ( ) ;
128+ } ;
129+
130+ None
131+ }
132+
133+ pub fn install_apk ( is_release : bool ) -> Result < ExitStatus > {
134+ let output_dir = PathBuf :: from ( "app/build/outputs/apk" ) ;
135+
136+ let apk_path = match is_release {
137+ true => output_dir. join ( "release/app-release.apk" ) ,
138+ false => output_dir. join ( "debug/app-debug.apk" ) ,
139+ } ;
140+
141+ Ok ( invoke_adb_command ( & [ "install" , apk_path. to_str ( ) . unwrap ( ) ] )
142+ . context ( "failed to run adb command" ) ?)
143+ }
144+
145+ pub fn trigger_build ( is_release : bool ) -> Result < ExitStatus > {
146+ // Decide gradle subcommand to use
147+ let cmd = match is_release {
148+ true => "assembleRelease" ,
149+ false => "assembleDebug" ,
150+ } ;
151+
152+ Ok ( invoke_gradle_command ( cmd) . context ( "failed to invoke gradle command" ) ?)
153+ }
154+
155+ pub fn launch_activity ( package_id : String , activity_name : String ) -> Result < ExitStatus > {
156+ Ok ( invoke_adb_command ( & [
157+ "shell" ,
158+ "am" ,
159+ "start" ,
160+ "-n" ,
161+ & format ! ( "{}/.{}" , package_id, activity_name) ,
162+ ] )
163+ . context ( "failed to invoke adb command" ) ?)
164+ }
165+
166+ pub fn query_devices ( ) -> Result < ExitStatus > {
167+ Ok ( invoke_adb_command ( & [ "devices" ] ) . context ( "failed to invoke adb command" ) ?)
168+ }
169+
170+ pub fn attach_shell ( ) -> Result < ExitStatus > {
171+ Ok ( invoke_adb_command ( & [ "shell" ] ) . context ( "failed to invoke adb command" ) ?)
172+ }
0 commit comments