Android-ElectricSheep-Automator
view release on metacpan or search on metacpan
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# Inputs parameters:
# 'package' : required package name as a SCALAR (for an exact search)
# or a regex (qr//) object for regex search including case-insensitive.
# 'activity' : optional activity name to start additionally to the app/package name.
# If not present, we will try to find the MAIN activities of the package via
# AppProperties. There could be several MAIN activities and there are heuristics
# to pick one. See AppProperties::enquire().
# The spec must yield exactly 1 match, it will complain if more than 1 matches found.
# 'force-reload-apps-list' => 0,1 : optionally call find_installed_apps() if > 0
# but restricted only to the packages match 'package' NOT ALL.
# 'lazy' => 0,1 : pass this lazy value to the find_installed_apps()
# and be lazy (i.e. without enquiring on each app's specifics
# and creating an AppProperties object) if ==1
# or not be lazy if ==0 ...
# ... (which means an AppProperties object is created for each found package)
# Default is force-reload-apps-list=>0
# THIS APPLIES ONLY TO THE MATCHED 'package'
# On success it returns a hash of {appname => appproperties} of the opened app
# (which will be created if not existing). It may return {} if no match
# was found for the specified 'package' name.
# On failure it returns undef
# it needs that connect_device() to have been called prior to this call
sub open_app {
my ($self, $params) = @_;
$params //= {};
my $parent = ( caller(1) )[3] || "N/A";
my $whoami = ( caller(0) )[3];
my $log = $self->log();
my $verbosity = $self->verbosity();
if( ! $self->is_device_connected() ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, you need to connect a device to the desktop and ALSO explicitly call ".'connect_device()'." before calling this."); return undef }
my ($package);
if( ! exists($params->{'package'}) || ! defined($package=$params->{'package'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'package' is required."); return undef }
if( (ref($package)ne'') && (ref($package)ne'Regexp') ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, the type of input parameter 'package' must be a scalar string (the package name) or a Regexp object (compiled regex via ".'qr//'...
# optional activity, else we will see if we find one
my $activity = (exists($params->{'activity'}) && defined($params->{'activity'})) ? $params->{'activity'} : undef;
my $force_reload = (exists($params->{'force-reload-apps-list'}) && defined($params->{'force-reload-apps-list'})) ? $params->{'force-reload-apps-list'} : 0;
my $lazy = (exists($params->{'lazy'}) && defined($params->{'lazy'})) ? $params->{'lazy'} : 1;
my $apps = $self->apps();
if( (0 == scalar(keys %$apps))
|| ($force_reload>0)
){
my $fpars = {
'packages' => $package,
'force-reload-apps-list' => $force_reload,
'lazy' => $lazy
};
if( ! defined($apps=$self->find_installed_apps($fpars)) ){ $log->error(perl2dump($fpars)."${whoami} (via $parent), line ".__LINE__." : error, failed to load list of installed apps, call to ".'find_installed_apps()'." has failed with above parameter...
if( 0 == scalar(keys %$apps) ){
$log->error("${whoami} (via $parent), line ".__LINE__." : error, there are no installed apps, even after enquiring them. The target device has no apps installed. Weird.");
return undef
}
}
# by now we are sure we have the list of installed apps updated
# but it is likely that there is no AppProperties object for each
# app in the list, but we need it, so make a search and if
# AppProperties is undef, then we need to call find_installed_apps() again.
my $searchres = $self->search_app({
'package' => $package,
'force-reload-apps-list' => 0,
});
if( ! defined $searchres ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'search_app()'." has failed for this search term (package) : ${package}"); return undef }
my $num_searchres = scalar keys %$searchres;
if( $num_searchres == 0 ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, no app was found for this search term (package) : ${package}"); return undef }
elsif( $num_searchres > 1 ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, more than one app was found for this search term (package) : ${package} . Apps found: '".join("', '", sort keys %$searchres)."'."); return undef }
# only 1 tupple in the hash, get it:
my ($found_app_name, $found_app_properties) = %$searchres;
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : app to open has been matched to '${found_app_name}'.") }
if( ! defined $found_app_properties ){
# the app is there in the list but it does not have AppProperties yet.
# So get just this one package and non-lazily because we need the AppProperties object:
my $fpars = {
'force-reload-apps-list' => 0,
'lazy' => 1, # << lazy for all other packages except our 'package'
'packages' => $found_app_name,
};
if( ! defined $self->find_installed_apps($fpars) ){ $log->error(perl2dump($fpars)."${whoami} (via $parent), line ".__LINE__." : error, failed to load list of installed apps, call to ".'find_installed_apps()'." has failed with above parameters."); r...
$apps = $self->apps();
$found_app_properties = $apps->{$found_app_name};
}
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : opening app '".$found_app_properties->get('packageName')."' ...") }
my ($MainActivity, $fact, @cmd);
if( ! defined($MainActivity=$found_app_properties->get('MainActivity'))
|| ! exists($MainActivity->{'name-fully-qualified'})
|| ! defined($fact=$MainActivity->{'name-fully-qualified'})
){
$log->warn("${whoami} (via $parent), line ".__LINE__." : error, above app (package '${package}') does not contain a 'MainActivity' entry. Launching without it ...");
$fact = $found_app_properties->get('packageName');
@cmd = ('am', 'start', $fact);
} else {
@cmd = ('am', 'start', '-n', $fact);
}
# open it
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : sending command to adb: @cmd") }
my $res = $self->adb->shell(@cmd);
if( ! defined $res ){ $log->error(join(" ", @cmd)."\n${whoami} (via $parent), line ".__LINE__." : error, above shell command has failed, got undefined result, most likely shell command did not run at all, this should not be happening."); return unde...
if( $res->[0] != 0 ){ $log->error(join(" ", @cmd)."\n${whoami} (via $parent), line ".__LINE__." : error, above shell command has failed, with:\nSTDOUT:\n".$res->[1]."\n\nSTDERR:\n".$res->[2]."\nEND."); return undef }
# we are returning a hash of name=>appproperties
# but because we allow 1 match only, this hash will only contain 1 item
# but it will be easier to allow more apps in the future if
# we return a hash here
return { $found_app_name => $found_app_properties };
}
# Inputs parameters:
# 'package' => required package name as a SCALAR (for an exact search)
# or a regex (qr//) object for regex search including case-insensitive.
# The spec must yield exactly 1 match, it will complain if more than 1 matches found.
# 'force-reload-apps-list' => 0,1 : optionally call find_installed_apps() if > 0
# but restricted only to the packages match 'package' NOT ALL.
# 'lazy' => 0,1 : pass this lazy value to the find_installed_apps()
# and be lazy (i.e. without enquiring on each app's specifics
# and creating an AppProperties object) if ==1
# or not be lazy if ==0 ...
# ... (which means an AppProperties object is created for each found package)
# Default is force-reload-apps-list=>0
# THIS APPLIES ONLY TO THE MATCHED 'package'
# On success it returns the a hash of {appname => appproperties} of the closed app
# (which will be created if not existing). It may return {} if no match.
# On failure it returns undef
# it needs that connect_device() to have been called prior to this call
sub close_app {
my ($self, $params) = @_;
$params //= {};
my $parent = ( caller(1) )[3] || "N/A";
my $whoami = ( caller(0) )[3];
my $log = $self->log();
my $verbosity = $self->verbosity();
if( ! $self->is_device_connected() ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, you need to connect a device to the desktop and ALSO explicitly call ".'connect_device()'." before calling this."); return undef }
my ($package);
if( ! exists($params->{'package'}) || ! defined($package=$params->{'package'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'package' is required."); return undef }
if( (ref($package)ne'') && (ref($package)ne'Regexp') ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, the type of input parameter 'package' must be a scalar string (the package name) or a Regexp object (compiled regex via ".'qr//'...
# optional activity, else we will see if we find one
my $activity = (exists($params->{'activity'}) && defined($params->{'activity'})) ? $params->{'activity'} : undef;
my $force_reload = (exists($params->{'force-reload-apps-list'}) && defined($params->{'force-reload-apps-list'})) ? $params->{'force-reload-apps-list'} : 0;
my $lazy = (exists($params->{'lazy'}) && defined($params->{'lazy'})) ? $params->{'lazy'} : 1;
my $apps = $self->apps();
if( (0 == scalar(keys %$apps))
|| ($force_reload>0)
){
my $fpars = {
'packages' => $package,
'force-reload-apps-list' => $force_reload,
'lazy' => $lazy
};
if( ! defined($apps=$self->find_installed_apps($fpars)) ){ $log->error(perl2dump($fpars)."${whoami} (via $parent), line ".__LINE__." : error, failed to load list of installed apps, call to ".'find_installed_apps()'." has failed with above parameter...
if( 0 == scalar(keys %$apps) ){
$log->error("${whoami} (via $parent), line ".__LINE__." : error, there are no installed apps, even after enquiring them. The target device has no apps installed. Weird.");
return undef
}
}
# by now we are sure we have the list of installed apps updated
# but it is likely that there is no AppProperties object for each
# app in the list, but we need it, so make a search and if
# AppProperties is undef, then we need to call find_installed_apps() again.
my $searchres = $self->search_app({
'package' => $package,
'force-reload-apps-list' => 0,
});
if( ! defined $searchres ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'search_app()'." has failed for this search term (package) : ${package}"); return undef }
my $num_searchres = scalar keys %$searchres;
if( $num_searchres == 0 ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, no app was found for this search term (package) : ${package}"); return {} }
elsif( $num_searchres > 1 ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, more than one app was found for this search term (package) : ${package} . Apps found: '".join("', '", sort keys %$searchres)."'."); return undef }
# only 1 tupple in the hash, get it:
my ($found_app_name, $found_app_properties) = %$searchres;
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : app to open has been matched to '${found_app_name}'.") }
if( ! defined $found_app_properties ){
# the app is there in the list but it does not have AppProperties yet.
# So get just this one package and non-lazily because we need the AppProperties object:
my $fpars = {
'force-reload-apps-list' => 0,
'lazy' => 1, # << lazy for all other packages except our 'package'
'packages' => $found_app_name,
};
if( ! defined $self->find_installed_apps($fpars) ){ $log->error(perl2dump($fpars)."${whoami} (via $parent), line ".__LINE__." : error, failed to load list of installed apps, call to ".'find_installed_apps()'." has failed with above parameters."); r...
$apps = $self->apps();
$found_app_properties = $apps->{$found_app_name};
}
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : closing app '".$found_app_properties->get('packageName')."' ...") }
# adb shell am force-stop com.my.app
my @cmd = ('am', 'force-stop', $found_app_properties->get('packageName'));
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : sending command to adb: @cmd") }
my $res = $self->adb->shell(@cmd);
if( ! defined $res ){ $log->error(join(" ", @cmd)."\n${whoami} (via $parent), line ".__LINE__." : error, above shell command has failed, got undefined result, most likely shell command did not run at all, this should not be happening."); return unde...
if( $res->[0] != 0 ){ $log->error(join(" ", @cmd)."\n${whoami} (via $parent), line ".__LINE__." : error, above shell command has failed, with:\nSTDOUT:\n".$res->[1]."\n\nSTDERR:\n".$res->[2]."\nEND."); return undef }
# we are returning a hash of name=>appproperties
# but because we allow 1 match only, this hash will only contain 1 item
# but it will be easier to allow more apps in the future if
# we return a hash here
return { $found_app_name => $found_app_properties };
}
# returns pid (as a non-negative integer) of the specified app by its exact name
# or -1 if nothing was matched in the process table.
# on error it returns undef
# Note: if you do not know the exact app name e.g. com.viber.voip
# use pgrep()
sub pidof {
my ($self, $params) = @_;
$params //= {};
my $parent = ( caller(1) )[3] || "N/A";
my $whoami = ( caller(0) )[3];
my $log = $self->log();
my $verbosity = $self->verbosity();
if( ! $self->is_device_connected() ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, you need to connect a device to the desktop and ALSO explicitly call ".'connect_device()'." before calling this."); return undef }
( run in 0.451 second using v1.01-cache-2.11-cpan-df04353d9ac )