11use crate :: device:: Device ;
22use crate :: error:: Error ;
3- use crate :: error:: Error :: Uninstall ;
43use crate :: package:: Package ;
54use std:: sync:: Arc ;
65use tokio:: sync:: mpsc;
@@ -19,6 +18,20 @@ impl CommandOutcome {
1918 pub fn error ( & self ) -> Option < & Error > {
2019 self . error . as_ref ( )
2120 }
21+
22+ pub fn from_success ( description : & str ) -> CommandOutcome {
23+ CommandOutcome {
24+ description : String :: from ( description) ,
25+ error : None ,
26+ }
27+ }
28+
29+ pub fn from_error ( description : & str , error : Error ) -> CommandOutcome {
30+ CommandOutcome {
31+ description : String :: from ( description) ,
32+ error : Some ( error) ,
33+ }
34+ }
2235}
2336
2437pub struct DeviceInstallations {
@@ -33,10 +46,7 @@ impl DeviceInstallations {
3346 ) -> Vec < DeviceInstallations > {
3447 let mut requests: Vec < DeviceInstallations > = Vec :: new ( ) ;
3548 for device in devices {
36- let matches: Vec < _ > = packages
37- . iter ( )
38- . filter ( |p| is_package_match ( device, p) )
39- . collect ( ) ;
49+ let matches: Vec < _ > = packages. iter ( ) . filter ( |p| device. supports ( p) ) . collect ( ) ;
4050 if matches. is_empty ( ) {
4151 continue ;
4252 }
@@ -66,86 +76,33 @@ impl DeviceInstallations {
6676 let tx = tx. clone ( ) ;
6777 tokio:: task:: spawn ( async move {
6878 if self . uninstall_first {
69- let uninstall_outcome = perform_uninstall ( & device, & package) . await ;
70- tx. send ( uninstall_outcome)
71- . await
72- . expect ( "Error sending operation outcome." ) ;
79+ match device. has_app_installed ( package. id ( ) ) . await {
80+ Ok ( true ) => {
81+ // App is installed, uninstall it first.
82+ let uninstall_outcome = device. uninstall ( & package) . await ;
83+ tx. send ( uninstall_outcome)
84+ . await
85+ . expect ( "Error sending uninstallation outcome." ) ;
86+ }
87+ Ok ( false ) => ( ) , // Do nothing because the app is not installed.
88+ Err ( e) => {
89+ let error = CommandOutcome :: from_error ( "Uninstall check failed" , e) ;
90+ tx. send ( error)
91+ . await
92+ . expect ( "Error sending uninstallation check error." ) ;
93+ }
94+ }
7395 }
74- let install_outcome = perform_install ( & device, & package) . await ;
96+ let install_outcome = device. install ( & package) . await ;
7597 tx. send ( install_outcome)
7698 . await
77- . expect ( "Error sending operation outcome." ) ;
99+ . expect ( "Error sending installation outcome." ) ;
78100 } ) ;
79101 }
80102 ReceiverStream :: new ( rx)
81103 }
82104}
83105
84- fn is_package_match ( device : & Device , package : & Package ) -> bool {
85- package
86- . platforms ( )
87- . iter ( )
88- . any ( |p| match package. match_file_name ( ) {
89- false => device. platform ( ) == p,
90- true => package
91- . file_name ( )
92- . to_lowercase ( )
93- . contains ( device. platform ( ) ) ,
94- } )
95- }
96-
97- fn parse_installation_error ( error : & [ u8 ] ) -> Error {
98- let error = String :: from_utf8_lossy ( error) ;
99- if error. contains ( "INSTALL_FAILED_UPDATE_INCOMPATIBLE" ) {
100- Error :: PackageSignatureMismatch
101- } else if error. contains ( "INSTALL_FAILED_VERSION_DOWNGRADE" ) {
102- Error :: PackageDowngrade
103- } else {
104- Error :: Installation ( String :: from ( error) )
105- }
106- }
107-
108- async fn perform_uninstall ( device : & Device , package : & Package ) -> CommandOutcome {
109- let description = format ! ( "Uninstallation of {} on {}" , package. id( ) , device. name( ) ) ;
110- let output = tokio:: process:: Command :: new ( "adb" )
111- . args ( [ "-s" , device. id ( ) , "uninstall" , package. id ( ) ] )
112- . output ( ) ;
113- match output. await {
114- Ok ( output) if output. status . success ( ) => CommandOutcome {
115- description,
116- error : None ,
117- } ,
118- Ok ( output) => {
119- let message = String :: from_utf8_lossy ( & output. stderr ) ;
120- CommandOutcome {
121- description,
122- error : Some ( Uninstall ( message. to_string ( ) ) ) ,
123- }
124- }
125- Err ( e) => CommandOutcome {
126- description,
127- error : Some ( Uninstall ( e. to_string ( ) ) ) ,
128- } ,
129- }
130- }
131-
132- async fn perform_install ( device : & Device , package : & Package ) -> CommandOutcome {
133- let path = package. path ( ) ;
134- let output = tokio:: process:: Command :: new ( "adb" )
135- . args ( [ "-s" , device. id ( ) , "install" , path] )
136- . output ( ) ;
137- let error = match output. await {
138- Ok ( output) if output. stderr . is_empty ( ) => None , // Success
139- Ok ( output) => {
140- let error = parse_installation_error ( & output. stderr ) ;
141- Some ( error)
142- }
143- Err ( e) => Some ( Error :: Installation ( e. to_string ( ) ) ) ,
144- } ;
145- let description = format ! ( "Installation of {} on {}" , package. id( ) , device. name( ) ) ;
146- CommandOutcome { description, error }
147- }
148-
149106#[ cfg( test) ]
150107mod tests {
151108 use super :: * ;
@@ -156,7 +113,7 @@ mod tests {
156113 [INSTALL_FAILED_UPDATE_INCOMPATIBLE: Package com.lazysquirrellabs.airhockey signatures \
157114 do not match previously installed version; ignoring!]";
158115 assert_eq ! (
159- parse_installation_error ( error. as_bytes( ) ) ,
116+ Error :: from_installation_error ( error. as_bytes( ) ) ,
160117 Error :: PackageSignatureMismatch
161118 ) ;
162119 }
@@ -166,7 +123,7 @@ mod tests {
166123 let error = "adb: failed to install 5.1/CorpusVR-5.1-5010001-Dashboard.apk: \
167124 Failure [INSTALL_FAILED_VERSION_DOWNGRADE]";
168125 assert_eq ! (
169- parse_installation_error ( error. as_bytes( ) ) ,
126+ Error :: from_installation_error ( error. as_bytes( ) ) ,
170127 Error :: PackageDowngrade
171128 ) ;
172129 }
@@ -176,7 +133,7 @@ mod tests {
176133 // Sometimes ADB throws errors without no content, just a header, like the one below.
177134 let error = "adb: failed to install 5.1/CorpusVR-5.1-5010001-Dashboard.apk:\n " ;
178135 assert_eq ! (
179- parse_installation_error ( error. as_bytes( ) ) ,
136+ Error :: from_installation_error ( error. as_bytes( ) ) ,
180137 Error :: Installation ( String :: from( error) )
181138 ) ;
182139 }
0 commit comments