view release on metacpan or search on metacpan
{
"abstract" : "Do Androids Dream of Electric Sheep? Smartphone control from your desktop.",
"author" : [
"Andreas Hadjiprocopis <bliako@cpan.org>"
],
"dynamic_config" : 1,
"generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010",
"license" : [
"artistic_2"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
---
abstract: 'Do Androids Dream of Electric Sheep? Smartphone control from your desktop.'
author:
- 'Andreas Hadjiprocopis <bliako@cpan.org>'
build_requires:
Data::Roundtrip: '0.30'
ExtUtils::MakeMaker: '0'
FindBin: '0'
Mojo::Log: '0'
Test::More: '0'
Test::More::UTF8: '0'
Test::TempDir::Tiny: '0'
NAME
Android::ElectricSheep::Automator - Do Androids Dream of Electric
Sheep? Smartphone control from your desktop.
VERSION
Version 0.06
WARNING
Current distribution is extremely alpha. API may change.
SYNOPSIS
The present package fascilitates the control of a USB-debugging-enabled
Android device, e.g. a real smartphone, or an emulated (virtual)
Android device, from your desktop computer using Perl. It's basically a
thickishly-thin wrapper to the omnipotent Android Debug Bridge (adb)
program.
Note that absolutely nothing is installed on the connected device,
neither any of its settings will be modified by this package. See "WILL
ANYTHING BE INSTALLED ON THE DEVICE?".
use Android::ElectricSheep::Automator;
my $mother = Android::ElectricSheep::Automator->new({
# optional as there is a default, but you may have
# problems with the location of the adb executable
'configfile' => $configfile,
'verbosity' => 1,
# we already have a device connected and ready to control
'device-is-connected' => 1,
});
# find the devices connected to desktop and set one.
my @devices = $mother->adb->devices;
$mother->connect_device({'serial' => $devices->[0]->serial})
or die;
# no device needs to be specified if just one:
$mother->connect_device() if scalar(@devices)==0;
# Go Home
$mother->home_screen() or die;
# swipe up/down/left/right
and can be supplied to the constructor instead of the configuration
file.
If no configuration is specified, then a default configuration will
be used. In this case please specify adb-path-to-executable to point
to the location of adb. Most likely the default path will not work
for you.
adb-path-to-executable
optionally specify the path to the adb executable in your desktop
system. This will override the setting 'adb'->'path-to-executable'
in the configuration, if it was provided. Use this option if you are
not providing any configuration and so the default configuration will
be used. But it will most likely fail because of this path not being
correct for your system. So, if you are going to omit providing a
configuration and the default configuration will be used do specify
the adb path via this option (but you don't have to and your mileage
may vary).
device-serial or device-object
device-is-connected
optionally set it to 1 in order to communicate with the device and
get some information about it like screen size, resolution,
orientation, etc. And also allow use of functionality which needs
communicating with a device like "swipe($params)",
"home_screen($params)", "open_app($params)", etc. After
instantiation, you can use the method "connect_device($params)" and
"disconnect_device()" for conveying this information to the module.
Also note that if there are more than one devices connected to the
desktop, make sure you specify which one with the device parameter.
Default value is 0.
logger
optionally specify a logger object to be used (instead of creating a
fresh one). This object must implement info(), warn(), error(). For
example Mojo::Log.
logfile
ARRAY_REF : my $ar = [1,2,3]; my $ar = \@ahash; my @anarray = @$ar;
HASH_REF : my $hr = {1=1, 2=>2}; my $hr = \%ahash; my %ahash = %$hr;>
In this module parameters to functions are passed as a HASH_REF.
Functions return back objects, ARRAY_REF or HASH_REF.
devices()
Lists all Android devices connected to your desktop and returns these
as an ARRAY_REF which can be empty.
It returns undef on failure.
connect_device($params)
Specifies the current Android device to control. Its use is required
only if you have more than one devices connected. $params is a
HASH_REF which should contain exactly one of the following:
Android::ElectricSheep::Automator::DeviceProperties object containing
this information, for example screen size, resolution, serial number,
etc.
It returns Android::ElectricSheep::Automator::DeviceProperties object
on success or undef on failure.
connect_device()
It signals to our object that there is now a device connected to the
desktop and its enquiry and subsequent control can commence. If this
is not called and neither device-is-connected => 1 is specified as a
parameter to the constructor, then the functionality will be limited
and access to functions like swipe(), open_app(), etc. will be
blocked until the caller signals that a device is now connected to
the desktop.
Using "connect_device($params)" to specify which device to target in
the case of multiple devices connected to the desktop will also call
this method.
This method will try to enquire the connected device about some of
its properties, like screen size, resolution, orientation, serial
number etc. This information will subsequently be available via
$self->device_properties()>.
It returns 0 on success, 1 on failure.
disconnect_device()
Signals to our object that it should consider that there is currently
no device connected to the desktop (irrespective of that is true or
not) which will block access to "swipe()", "open_app()", etc.
device_properties()
It returns the currently connected device properties as a
Android::ElectricSheep::Automator::DeviceProperties object or undef
if there is no connected device. The returned object is constructed
during a call to "find_current_device_properties()" which is called
via "connect_device($params)" and will persist for the duration of
the connection. However, after a call to "disconnect_device()" this
--configfile config/myapp.conf --device Pixel_2_API_30_x86_>>
TESTING
The normal tests under t/, initiated with make test, are quite limited
in scope because they do not assume a connected device. That is, they
do not check any functions which require interaction with a connected
device.
The live tests under xt/live, initiated with make livetest, require an
Android device connected to your desktop on which you installed this
package and on which you are doing the testing. This suffices to be an
emulator. It can also be a real Android phone but testing with your
smartphone is not a good idea, please do not do this, unless it is some
phone which you do not store important data.
So, prior to make livetest make sure you have an android emulator up
and running with, for example, emulator -avd Pixel_2_API_30_x86_ . See
section "Android Emulators" for how to install, list and run them
buggers.
and this messes up with the focus. Other times, the OS will detect that
some app is taking too long to launch and pops up a notification about
"something is not responding, shall I close it". This steals the focus
and sometimes it causes the tests to fail.
PREREQUISITES
Android Studio
This is not a prerequisite but it is highly recommended to install
(from https://developer.android.com/studio) on your desktop computer
because it contains all the executables you will need, saved in a well
documented file system hierarchy, which can then be accessed from the
command line.
Additionally, Android Studio offers possibly the easiest way to create
Android Virtual Devices (AVD) which emulate an Android phone of various
specifications. I mention this because one can install apps on an AVD
and control them from your desktop as long as you are able to receive
sms verification codes from a real phone. This is great for
experimenting without pluggin in your real smartphone on your desktop.
The bottom line is that by installing Android Studio, you have all the
executables you need for running things from the command line and,
additionally, you have the easiest way for creating Android Virtual
Devices, which emulate Android devices: phones, tablets, automotive
displays. Once you have this set up, you will not need to open Android
Studio ever again unless you want to update your kit. All the
functionality will be accessible from the command line.
ADB
Android Debug Bridge (ADB) is the program which communicates with your
smartphone or an Android Virtual Device from your desktop (Linux, osx
and the unnamed 0$).
If you do not want to install Android Studio, the adb executable is
included in the package called "Android SDK Platform Tools" available
from the Android official site, here:
https://developer.android.com/tools/releases/platform-tools#downloads
You will need the adb executable to be on your path or specify its
fullpath in the configuration file supplied to
Android::ElectricSheep::Automator's constructor.
Enter your phone pin and you are in developer mode.
You can exit Developer Mode by going to Settings->System->Developer and
turn it off. It is highly advised to turn off Developer Mode for
everyday use of your phone. Do not connect your smartphone to public
WIFI networks with Developer Mode ON.
Do not leave home with Developer Mode ON.
Once you have enabled "USB Debugging", you have two options for making
your device visible to your desktop and, consequently, to ADB and to
this module:
connect your android device via a USB cable to your desktop computer. I
am not sure if you also need to tap on the USB charging options and
allow "Transfer Files".
connect your device to the same WIFI network as your desktop computer.
Then follow instructions from, e.g., here
https://developer.android.com. This requires a newer Android version.
Android Emulators
It is possible to do most things your smartphone does with an Android
Virtual Device. You can install apps on the the virtual device which
you can register by supplying your real smartphone number.
List all virtual devices currently available in your desktop computer,
with emulator -list-avds which outputs something like:
Pixel_2_API_27_x86_
Pixel_2_API_30_x86_
Start a virtual device with emulator -avd Pixel_2_API_30_x86_
And hey, you have an android phone running on your desktop in its own
space, able to access the network but not the telephone network (no SIM
card).
It is possible to create a virtual device from the command line. But
perhaps it is easier if you download Android Studio from:
https://developer.android.com/studio and follow the setup there using
the GUI. You will need to do this just once for creating the device,
you can then uninstall Android Studio.
Android Studio will download all the required files and will create
# NAME
Android::ElectricSheep::Automator - Do Androids Dream of Electric Sheep? Smartphone control from your desktop.
# VERSION
Version 0.06
# WARNING
Current distribution is extremely alpha. API may change.
# SYNOPSIS
The present package fascilitates the control
of a USB-debugging-enabled
Android device, e.g. a real smartphone,
or an emulated (virtual) Android device,
from your desktop computer using Perl.
It's basically a thickishly-thin wrapper
to the omnipotent Android Debug Bridge (adb)
program.
**Note that absolutely nothing is
installed on the connected device,
neither any of its settings will be modified by this package**.
See ["WILL ANYTHING BE INSTALLED ON THE DEVICE?"](#will-anything-be-installed-on-the-device).
use Android::ElectricSheep::Automator;
my $mother = Android::ElectricSheep::Automator->new({
# optional as there is a default, but you may have
# problems with the location of the adb executable
'configfile' => $configfile,
'verbosity' => 1,
# we already have a device connected and ready to control
'device-is-connected' => 1,
});
# find the devices connected to desktop and set one.
my @devices = $mother->adb->devices;
$mother->connect_device({'serial' => $devices->[0]->serial})
or die;
# no device needs to be specified if just one:
$mother->connect_device() if scalar(@devices)==0;
# Go Home
$mother->home_screen() or die;
# swipe up/down/left/right
If no configuration is specified, then a default
configuration will be used. In this case please
specify **`adb-path-to-executable`** to point
to the location of `adb`. Most likely
the default path will not work for you.
- **`adb-path-to-executable`**
optionally specify the path to the `adb` executable in
your desktop system. This will override the setting
` 'adb'->'path-to-executable' ` in the configuration,
if it was provided. Use this option if you are not
providing any configuration and so the default configuration
will be used. But it will most likely fail because of this
path not being correct for your system. So, if you are going
to omit providing a configuration and the default configuration
will be used do specify the `adb` path via this option (but you
don't have to and your mileage may vary).
- **`device-serial`** or **`device-object`**
screen size, resolution, orientation, etc.
And also allow use of
functionality which needs communicating with a device
like ["swipe($params)"](#swipe-params), ["home\_screen($params)"](#home_screen-params),
["open\_app($params)"](#open_app-params), etc.
After instantiation, you can use the
method ["connect\_device($params)"](#connect_device-params) and
["disconnect\_device()"](#disconnect_device) for conveying
this information to the module.
Also note that if there are
more than one devices connected to the desktop, make sure
you specify which one with the `device` parameter.
Default value is 0.
- **`logger`**
optionally specify a logger object
to be used (instead of creating a fresh one). This object
must implement `info()`, `warn()`, `error()`. For
example [Mojo::Log](https://metacpan.org/pod/Mojo%3A%3ALog).
Note:
- **`ARRAY_REF`** : `my $ar = [1,2,3]; my $ar = \@ahash; my @anarray = @$ar;`
- **`HASH_REF`** : `my $hr = {1=`1, 2=>2}; my $hr = \\%ahash; my %ahash = %$hr;>
- In this module parameters to functions are passed as a HASH\_REF.
Functions return back objects, ARRAY\_REF or HASH\_REF.
- devices()
Lists all Android devices connected to your
desktop and returns these as an ARRAY\_REF which can be empty.
It returns `undef` on failure.
- connect\_device($params)
Specifies the current Android device to control. Its use is
required only if you have more than one devices connected.
`$params` is a HASH\_REF which should contain exactly
one of the following:
and returns back an [Android::ElectricSheep::Automator::DeviceProperties](https://metacpan.org/pod/Android%3A%3AElectricSheep%3A%3AAutomator%3A%3ADeviceProperties)
object containing this information, for example screen size,
resolution, serial number, etc.
It returns [Android::ElectricSheep::Automator::DeviceProperties](https://metacpan.org/pod/Android%3A%3AElectricSheep%3A%3AAutomator%3A%3ADeviceProperties)
object on success or `undef` on failure.
- connect\_device()
It signals to our object that there is now
a device connected to the desktop and its
enquiry and subsequent control can commence.
If this is not called and neither `device-is-connected => 1`
is specified as a parameter to the constructor, then
the functionality will be limited and access
to functions like `swipe()`, `open_app()`, etc.
will be blocked until the caller signals that
a device is now connected to the desktop.
Using ["connect\_device($params)"](#connect_device-params) to specify which device
to target in the case of multiple devices
connected to the desktop will also call this
method.
This method will try to enquire the connected device
about some of its properties, like screen size,
resolution, orientation, serial number etc.
This information will subsequently be available
via `$self->`device\_properties()>.
It returns `0` on success, `1` on failure.
- disconnect\_device()
Signals to our object that it should consider
that there is currently no device connected to
the desktop (irrespective of that is true or not)
which will block access to ["swipe()"](#swipe), ["open\_app()"](#open_app), etc.
- device\_properties()
It returns the currently connected device properties
as a [Android::ElectricSheep::Automator::DeviceProperties](https://metacpan.org/pod/Android%3A%3AElectricSheep%3A%3AAutomator%3A%3ADeviceProperties)
object or `undef` if there is no connected device.
The returned object is constructed during a call
to ["find\_current\_device\_properties()"](#find_current_device_properties)
which is called via ["connect\_device($params)"](#connect_device-params) and will persist
# TESTING
The normal tests under `t/`, initiated with `make test`,
are quite limited in scope because they do not assume
a connected device. That is, they do not check any
functions which require interaction with a connected
device.
The _live tests_ under `xt/live`, initiated with
`make livetest`, require
an Android device connected to your desktop on which
you installed this package and on which you are doing the testing.
This suffices to be an emulator. It can also be a real Android
phone but testing
with your smartphone is not a good idea, please do not do this,
unless it is some phone which you do not store important data.
So, prior to `make livetest` make sure you have an android
emulator up and running with, for example,
`emulator -avd Pixel_2_API_30_x86_` . See section
["Android Emulators"](#android-emulators) for how to install, list and run them
This steals the focus and sometimes it causes
the tests to fail.
# PREREQUISITES
## Android Studio
This is not a prerequisite but it is
highly recommended to install
(from [https://developer.android.com/studio](https://developer.android.com/studio))
on your desktop computer because it contains
all the executables you will need,
saved in a well documented file system hierarchy,
which can then be accessed from the command line.
Additionally, Android Studio offers possibly the
easiest way to create Android Virtual Devices (AVD) which emulate
an Android phone of various specifications.
I mention this because one can install apps
on an AVD and control them from your desktop
as long as you are able to receive sms verification
codes from a real phone. This is great for
experimenting without pluggin in your real
smartphone on your desktop.
The bottom line is that by installing Android Studio,
you have all the executables you need for running things
from the command line and, additionally, you have
the easiest way for creating Android
Virtual Devices, which emulate Android devices: phones,
tablets, automotive displays. Once you have this set up, you
will not need to open Android Studio ever again unless you
want to update your kit. All the functionality
will be accessible from the command line.
## ADB
Android Debug Bridge (ADB) is the program
which communicates with your smartphone or
an Android Virtual Device from
your desktop (Linux, osx and the unnamed `0$`).
If you do not want to install Android Studio, the `adb` executable
is included in the package called
"Android SDK Platform Tools" available from
the Android official site, here:
[https://developer.android.com/tools/releases/platform-tools#downloads](https://developer.android.com/tools/releases/platform-tools#downloads)
You will need the `adb` executable to be on your path
or specify its fullpath in the configuration file
supplied to [Android::ElectricSheep::Automator](https://metacpan.org/pod/Android%3A%3AElectricSheep%3A%3AAutomator)'s constructor.
`Settings->System->Developer` and turn it off.
It is highly advised to turn off Developer Mode
for everyday use of your phone.
**Do not connect your smartphone
to public WIFI networks with Developer Mode ON**.
**Do not leave home with Developer Mode ON**.
Once you have enabled "USB Debugging", you have
two options for making your device visible to
your desktop and, consequently, to ADB and to this module:
- connect your android device via a USB cable
to your desktop computer. I am not sure if you also
need to tap on the USB charging options and allow
"Transfer Files".
- connect your device to the same WIFI network
as your desktop computer. Then follow instructions
from, e.g., here [https://developer.android.com](https://developer.android.com).
This requires a newer Android version.
## Android Emulators
It is possible to do most things your
smartphone does with an Android Virtual Device.
You can install apps on the the virtual device which
you can register by supplying your real smartphone
number.
List all virtual devices currently available
in your desktop computer, with `emulator -list-avds`
which outputs something like:
Pixel_2_API_27_x86_
Pixel_2_API_30_x86_
Start a virtual device with `emulator -avd Pixel_2_API_30_x86_`
And hey, you have an android phone running on your
desktop in its own space, able to access the network
but not the telephone network (no SIM card).
It is possible to create a virtual device
from the command line.
But perhaps it is easier if you download Android Studio
from: [https://developer.android.com/studio](https://developer.android.com/studio) and follow
the setup there using the GUI. You will need to do this just
once for creating the device, you can then uninstall Android Studio.
Android Studio will download all the
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
</* log to file if you uncomment this */>
</* "filename" : "..." */>
}
</* config for our plugins (each can go to separate file also) */>
}
EODC
# NOTE: by default, it assumes that no device is connected
# and so it does not enquire about screen size etc on startup
# In order to tell it that a device (just one)
# is connected to the desktop and that we should connect to
# it,
# use param 'device-is-connected' => 1
# if there are more than one devices and you want to connect to
# one of them, then
# use param 'device-serial' => <serial-of-device-to-connect>
# or
# use param 'device-object' => <device object>
# (of type Android::ElectricSheep::Automator::ADB::Device)
# or after instantiation with $obj->connect_device(...);
# and similarly for disconnect_device()
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
else { $self->cleanup($self->confighash->{'debug'}->{'cleanup'}) }
my $verbosity = $self->verbosity;
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : done, success (verbosity is set to ".$self->verbosity." and cleanup to ".$self->cleanup.").") }
return $self;
}
# This signals our object that there is at least one device connected
# to the desktop which ADB can access and so can we.
# set the device by specifying one of
# 'serial' : the device's serial
# 'device-object' : a Android::ADB::Device object
# as returned by any item of $self->adb->devices()
# However, if there is ONLY ONE device connected to the desktop, then
# you do not need to specify a device, use this method without arguments
#
# It returns the device object (Android::ADB::Device) on success
# or undef on failure
sub connect_device {
my ($self, $params) = @_;
$params //= {};
my $parent = ( caller(1) )[3] || "N/A";
my $whoami = ( caller(0) )[3];
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
for (@$devs){
if( $_->serial eq $m ){ $what_device = $_; last }
}
if( ! defined $what_device ){ $log->error(devices_toString($devs)."\n${whoami} (via $parent), line ".__LINE__." : error, there is no device with specified serial '$m', above are all the connected devices."); return undef }
} elsif( exists($params->{'device-object'}) && defined($m=$params->{'device-object'})
&& (ref($params->{'device-object'})eq'Android::ElectricSheep::Automator::ADB::Device')
){
$what_device = $m
} else {
# no params means we assume there is exactly 1 device connected to the desktop
my $devs = $self->devices();
if( scalar(@$devs) == 1 ){
$what_device = $devs->[0];
} else { $log->error("${whoami} (via $parent), line ".__LINE__." : error, expecting exactly one device connected to the desktop but found ".scalar(@$devs)." instead. In the case of more than one devices connected to the desktop then specify which o...
}
# this can die
my $res = eval { $self->adb->set_device($what_device) };
if( $@ || ! defined $res ){ $log->error(device_toString($what_device)."\n${whoami} (via $parent), line ".__LINE__." : error, call to ".'adb->set_device()'." has failed for above device."); return undef }
# and get the device properties of the set device
# that method will also set $self->{'device-properties'} to the returned object
my $device_properties = $self->find_current_device_properties();
if( ! defined $device_properties ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'find_current_device_properties()'." has failed."); return 1; }
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub dump_current_screen_ui {
my ($self, $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 ".'device_connected()'." before calling this."); return undef }
my $filename = exists($params->{'filename'}) && defined($params->{'filename'}) ? $params->{'filename'} : undef;
my $FH;
if( ! defined $filename ){
($FH, $filename) = tempfile(CLEANUP=>$self->cleanup);
close $FH;
}
# WARNING, you need to wake up the phone before dumping !!!!
my $devicefile = File::Spec->catfile('/', 'data', 'local', 'tmp', $$.'.xml');
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub swipe {
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 1 }
my $w = $self->device_properties->get('w');
my $h = $self->device_properties->get('h');
my @fullspec;
for ('x1', 'y1', 'x2', 'y2', 'dt'){
if( ! exists($params->{$_}) || ! defined($params->{$_}) ){ last }
push @fullspec, $params->{$_};
}
my @cmd;
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# This will append to apps() the result.
sub find_installed_apps {
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 }
# optionally caller can specify a list of app names to enquire
# either as exact package name (string), a Regexp object (qr//)
# an ARRAY of package names or a HASH of package names:
my $packages = exists($params->{'packages'}) && defined($params->{'packages'}) ? $params->{'packages'} : undef;
my $rr = ref $packages;
if( ($rr ne '')&&($rr ne 'Regexp')&&($rr ne 'ARRAY')&&($rr ne 'HASH') ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, the type of input parameter 'packages' must be one of scalar string, Regexp, ARRAY or HASH and not '$rr'."); re...
# NOTE: that all those 'packages', if any,
# will be non-lazily even if 'lazy' is 1
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# 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;
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# 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;
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# 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 }
if( ! exists($params->{'name'}) || ! defined($params->{'name'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, missing parameter 'name' is required, it must be the exact app name, if you do not have the exact name then use 'pgre...
my @cmd = ('pidof', $params->{'name'});
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 not found it exits with 1
if( $res->[0] == 1 ){ return -1 } # nothing matched
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# on error it returns undef
sub pgrep {
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 }
if( ! exists($params->{'name'}) || ! defined($params->{'name'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, missing parameter 'name' is required, it must be the exact app name, if you do not have the exact name then use 'pgre...
my $dont_show_command_name = (exists($params->{'dont-show-command-name'}) && defined($params->{'dont-show-command-name'}) && ($params->{'dont-show-command-name'}>0)) ? 1 : 0;
# -f will search the full command name
# -l will include the command name which is the default
my @cmdparams = ('-f');
push(@cmdparams, '-l') unless $dont_show_command_name;
my @cmd = ('pgrep', @cmdparams, $params->{'name'});
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub tap {
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 (@position, $m);
if( exists($params->{'position'}) && defined($m=$params->{'position'}) ){
@position = ($m->[0], $m->[1]);
} elsif( exists($params->{'bounds'}) && defined($m=$params->{'bounds'}) ){
@position = ( int(($m->[1]->[0] + $m->[0]->[0])/2), int(($m->[1]->[1] + $m->[0]->[1])/2) );
} else { $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'position' (as ['x','y']) or 'bounds' (as [lefttopX,lefttopY],[bottomrightX,bottomrighY]) was not specified."); return 1 }
my @cmd = ('input', 'tap', @position);
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : sending command to adb: @cmd") }
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub input_text {
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 (@position, $m);
if( exists($params->{'position'}) && defined($m=$params->{'position'}) ){
@position = ($m->[0], $m->[1]);
} elsif( exists($params->{'bounds'}) && defined($m=$params->{'bounds'}) ){
@position = ( int(($m->[1]->[0] + $m->[0]->[0])/2), int(($m->[1]->[1] + $m->[0]->[1])/2) );
} else { $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'position' (as ['x','y']) or 'bounds' (as [lefttopX,lefttopY],[bottomrightX,bottomrighY]) was not specified."); return 1 }
# optional text, else we send just '' (but we clicked on it)
my $text = (exists($params->{'text'}) && defined($params->{'text'})) ? $params->{'text'} : '';
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub clear_input_field {
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 (@position, $m);
if( exists($params->{'position'}) && defined($m=$params->{'position'}) ){
@position = ($m->[0], $m->[1]);
} elsif( exists($params->{'bounds'}) && defined($m=$params->{'bounds'}) ){
@position = ( int(($m->[1]->[0] + $m->[0]->[0])/2), int(($m->[1]->[1] + $m->[0]->[1])/2) );
} else { $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'position' (as ['x','y']) or 'bounds' (as [lefttopX,lefttopY],[bottomrightX,bottomrighY]) was not specified."); return 1 }
# first tap on the text edit widget at the specified coordinates to get focus
if( $self->tap($params) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to tap on the position of the recipient of the text input"); return 1 }
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# I wish they would load ps info from a string rather than running their own `ps`
sub list_running_processes {
my ($self, $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 ".'device_connected()'." before calling this."); return undef }
my $filename = exists($params->{'filename'}) && defined($params->{'filename'}) ? $params->{'filename'} : undef;
my $extrafields = exists($params->{'extra-fields'}) && defined($params->{'extra-fields'}) ? $params->{'extra-fields'} : [];
my ($FH, $tmpfilename) = tempfile(CLEANUP=>$self->cleanup);
close $FH;
# WARNING, you need to wake up the phone before dumping !!!!
my $devicefile = File::Spec->catfile('/', 'data', 'local', 'tmp', $$.'.csv');
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub geofix {
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 }
for ('latitude', 'longitude'){
if( ! exists $params->{$_} ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter '$_' was not specified (as [x,y])."); return 1 }
}
my @cmd = ('emu', 'geo', 'fix', $params->{'longitude'}, $params->{'latitude'});
if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : sending command to adb: @cmd") }
my $res = $self->adb->run(@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 1 }
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 1 }
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub dump_current_location {
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 @cmd = ('dumpsys', 'location');
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 1 }
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 1 }
my $content = $res->[1];
# these are the GPS providers in order of preference:
my $gps;
for my $prov ('gps provider', 'fused provider', 'passive provider', 'network provider'){
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub list_physical_displays {
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 @cmd = ('dumpsys', 'SurfaceFlinger', '--display-id');
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 1 }
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 1 }
my $content = $res->[1];
my %ids;
while( $content =~ /^(Display\s+(.+?)\s+.+?)$/gsm ){
$ids{$2} = $1
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub dump_current_screen_shot {
my ($self, $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 ".'device_connected()'." before calling this."); return undef }
my $filename = exists($params->{'filename'}) && defined($params->{'filename'}) ? $params->{'filename'} : undef;
my $FH;
if( ! defined $filename ){
($FH, $filename) = tempfile(CLEANUP=>$self->cleanup);
close $FH;
}
# optional display-id (TODO: confirm that this display id is valid with
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub dump_current_screen_video {
my ($self, $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 ".'device_connected()'." before calling this."); return 1 }
my $filename = exists($params->{'filename'}) && defined($params->{'filename'}) ? $params->{'filename'} : undef;
if( ! defined $filename ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'filename' is not specified, an output filename must be specified."); return 1 }
my @options;
# optional duration or default of 10 seconds. (Android default is 180 which is stupidly huge for us)
if( exists($params->{'time-limit'}) && defined($params->{'time-limit'}) ){
push @options, '--time-limit', $params->{'time-limit'}
} else { push @options, '--time-limit', '10' }
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub wake_up {
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 @cmd = qw/input keyevent KEYCODE_WAKEUP/;
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 }
return 0; # success
}
# goes to the home screen
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub home_screen {
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 }
$self->wake_up();
my @cmd = qw/am start -a android.intent.action.MAIN -c android.intent.category.HOME/;
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 }
return 0; # success
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub next_screen {
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 }
if( $self->swipe({'direction' => 'right', dt => 100}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'swipe()'." has failed."); return undef }
return 0; # success
}
# It swipes left basically
# it returns 0 on success, 1 on failure
# it needs that connect_device() to have been called prior to this call
sub previous_screen {
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 }
if( $self->swipe({'direction' => 'left', dt => 100}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'swipe()'." has failed."); return undef }
return 0; # success
}
# it returns 0 on success, 1 on failure
# it needs that connect_device() to have been called prior to this call
# the left-triangle button (see http://developer.android.com/reference/android/view/KeyEvent.html)
sub navigation_menu_back_button {
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 @cmd = ('input', 'keyevent', 'KEYCODE_BACK');
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 }
return 0; # success
}
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# the round button, it goes to home (see http://developer.android.com/reference/android/view/KeyEvent.html)
sub navigation_menu_home_button {
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 @cmd = ('input', 'keyevent', 'KEYCODE_HOME');
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 }
return 0; # success
}
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# (see http://developer.android.com/reference/android/view/KeyEvent.html)
sub navigation_menu_overview_button {
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 @cmd = ('input', 'keyevent', 'KEYCODE_APP_SWITCH');
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 }
return 0; # success
}
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub find_all_apps_roundabout_way {
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 }
# go to home, swipe up and all apps will be revealed
# then dump the UI
# then swipe down
if( $self->home_screen() ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'home_screen()'." has failed."); return undef }
usleep(300);
if( $self->swipe({'direction'=>'up'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'swipe()'." has failed."); return undef }
usleep(300);
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
# it needs that connect_device() to have been called prior to this call
sub open_app_roundabout_way {
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 $apps = $self->apps_roundabout_way();
if( (! defined($apps))
|| (exists($params->{'force-reload-apps-list'}) && defined($params->{'force-reload-apps-list'}) && ($params->{'force-reload-apps-list'}>0))
){
if( ! defined $self->find_all_apps_roundabout_way() ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to load list of installed apps, call to ".'find_all_apps_roundabout_way()'." has failed."); return undef }
$apps = $self->apps_roundabout_way();
}
my ($position, $appname, $bounds, $name);
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
? $params->{'adb-path-to-executable'}
: $self->confighash->{'adb'}->{'path-to-executable'}
;
if( ! -x $pathtoadb ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, specified adb executable '${pathtoadb}' is not an executable or does not exist."); return 1; }
if( ! defined ($self->{'_private'}->{'Android::ADB'}=Android::ElectricSheep::Automator::ADB->new(
path => $pathtoadb,
verbosity => $self->verbosity
)) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'Android::ElectricSheep::Automator::ADB->new()'." has failed (path to executable was specified as '$pathtoadb')."); return 1; }
}
# does caller have a device connected to the desktop and wants us to
# target it?
# if just one device, we don't need serial etc:
my $device_params;
if( exists($params->{'device-is-connected'}) && defined($params->{'device-is-connected'}) && ($params->{'device-is-connected'}>0) ){
# just one device, we don't need serial of the device
$device_params = {};
} elsif( exists($params->{'device-serial'}) && defined($params->{'device-serial'}) ){
$device_params = {'serial' => $params->{'device-serial'}};
} elsif( exists($params->{'device-object'}) && defined($params->{'device-object'}) ){
$device_params = {'device-object' => $params->{'device-object'}};
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
return $sl;
}
# only pod below
=pod
=encoding utf8
=head1 NAME
Android::ElectricSheep::Automator - Do Androids Dream of Electric Sheep? Smartphone control from your desktop.
=head1 VERSION
Version 0.06
=head1 WARNING
Current distribution is extremely alpha. API may change.
=head1 SYNOPSIS
The present package fascilitates the control
of a USB-debugging-enabled
Android device, e.g. a real smartphone,
or an emulated (virtual) Android device,
from your desktop computer using Perl.
It's basically a thickishly-thin wrapper
to the omnipotent Android Debug Bridge (adb)
program.
B<Note that absolutely nothing is
installed on the connected device,
neither any of its settings will be modified by this package>.
See L</WILL ANYTHING BE INSTALLED ON THE DEVICE?>.
use Android::ElectricSheep::Automator;
my $mother = Android::ElectricSheep::Automator->new({
# optional as there is a default, but you may have
# problems with the location of the adb executable
'configfile' => $configfile,
'verbosity' => 1,
# we already have a device connected and ready to control
'device-is-connected' => 1,
});
# find the devices connected to desktop and set one.
my @devices = $mother->adb->devices;
$mother->connect_device({'serial' => $devices->[0]->serial})
or die;
# no device needs to be specified if just one:
$mother->connect_device() if scalar(@devices)==0;
# Go Home
$mother->home_screen() or die;
# swipe up/down/left/right
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
If no configuration is specified, then a default
configuration will be used. In this case please
specify B<C<adb-path-to-executable>> to point
to the location of C<adb>. Most likely
the default path will not work for you.
=item B<C<adb-path-to-executable>>
optionally specify the path to the C<adb> executable in
your desktop system. This will override the setting
C< 'adb'-E<gt>'path-to-executable' > in the configuration,
if it was provided. Use this option if you are not
providing any configuration and so the default configuration
will be used. But it will most likely fail because of this
path not being correct for your system. So, if you are going
to omit providing a configuration and the default configuration
will be used do specify the C<adb> path via this option (but you
don't have to and your mileage may vary).
=item B<C<device-serial>> or B<C<device-object>>
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
screen size, resolution, orientation, etc.
And also allow use of
functionality which needs communicating with a device
like L</swipe($params)>, L</home_screen($params)>,
L</open_app($params)>, etc.
After instantiation, you can use the
method L</connect_device($params)> and
L</disconnect_device()> for conveying
this information to the module.
Also note that if there are
more than one devices connected to the desktop, make sure
you specify which one with the C<device> parameter.
Default value is 0.
=item B<C<logger>>
optionally specify a logger object
to be used (instead of creating a fresh one). This object
must implement C<info()>, C<warn()>, C<error()>. For
example L<Mojo::Log>.
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
=item In this module parameters to functions are passed as a HASH_REF.
Functions return back objects, ARRAY_REF or HASH_REF.
=back
=over 1
=item devices()
Lists all Android devices connected to your
desktop and returns these as an ARRAY_REF which can be empty.
It returns C<undef> on failure.
=item connect_device($params)
Specifies the current Android device to control. Its use is
required only if you have more than one devices connected.
C<$params> is a HASH_REF which should contain exactly
one of the following:
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
and returns back an L<Android::ElectricSheep::Automator::DeviceProperties>
object containing this information, for example screen size,
resolution, serial number, etc.
It returns L<Android::ElectricSheep::Automator::DeviceProperties>
object on success or C<undef> on failure.
=item connect_device()
It signals to our object that there is now
a device connected to the desktop and its
enquiry and subsequent control can commence.
If this is not called and neither C<device-is-connected =E<gt> 1>
is specified as a parameter to the constructor, then
the functionality will be limited and access
to functions like C<swipe()>, C<open_app()>, etc.
will be blocked until the caller signals that
a device is now connected to the desktop.
Using L</connect_device($params)> to specify which device
to target in the case of multiple devices
connected to the desktop will also call this
method.
This method will try to enquire the connected device
about some of its properties, like screen size,
resolution, orientation, serial number etc.
This information will subsequently be available
via C<$self-E<gt>>device_properties()>.
It returns C<0> on success, C<1> on failure.
=item disconnect_device()
Signals to our object that it should consider
that there is currently no device connected to
the desktop (irrespective of that is true or not)
which will block access to L</swipe()>, L</open_app()>, etc.
=item device_properties()
It returns the currently connected device properties
as a L<Android::ElectricSheep::Automator::DeviceProperties>
object or C<undef> if there is no connected device.
The returned object is constructed during a call
to L</find_current_device_properties()>
which is called via L</connect_device($params)> and will persist
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
=head1 TESTING
The normal tests under C<t/>, initiated with C<make test>,
are quite limited in scope because they do not assume
a connected device. That is, they do not check any
functions which require interaction with a connected
device.
The I<live tests> under C<xt/live>, initiated with
C<make livetest>, require
an Android device connected to your desktop on which
you installed this package and on which you are doing the testing.
This suffices to be an emulator. It can also be a real Android
phone but testing
with your smartphone is not a good idea, please do not do this,
unless it is some phone which you do not store important data.
So, prior to C<make livetest> make sure you have an android
emulator up and running with, for example,
C<emulator -avd Pixel_2_API_30_x86_> . See section
L<Android Emulators> for how to install, list and run them
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
This steals the focus and sometimes it causes
the tests to fail.
=head1 PREREQUISITES
=head2 Android Studio
This is not a prerequisite but it is
highly recommended to install
(from L<https://developer.android.com/studio>)
on your desktop computer because it contains
all the executables you will need,
saved in a well documented file system hierarchy,
which can then be accessed from the command line.
Additionally, Android Studio offers possibly the
easiest way to create Android Virtual Devices (AVD) which emulate
an Android phone of various specifications.
I mention this because one can install apps
on an AVD and control them from your desktop
as long as you are able to receive sms verification
codes from a real phone. This is great for
experimenting without pluggin in your real
smartphone on your desktop.
The bottom line is that by installing Android Studio,
you have all the executables you need for running things
from the command line and, additionally, you have
the easiest way for creating Android
Virtual Devices, which emulate Android devices: phones,
tablets, automotive displays. Once you have this set up, you
will not need to open Android Studio ever again unless you
want to update your kit. All the functionality
will be accessible from the command line.
=head2 ADB
Android Debug Bridge (ADB) is the program
which communicates with your smartphone or
an Android Virtual Device from
your desktop (Linux, osx and the unnamed C<0$>).
If you do not want to install Android Studio, the C<adb> executable
is included in the package called
"Android SDK Platform Tools" available from
the Android official site, here:
L<https://developer.android.com/tools/releases/platform-tools#downloads>
You will need the C<adb> executable to be on your path
or specify its fullpath in the configuration file
supplied to L<Android::ElectricSheep::Automator>'s constructor.
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
C<Settings-E<gt>System-E<gt>Developer> and turn it off.
It is highly advised to turn off Developer Mode
for everyday use of your phone.
B<Do not connect your smartphone
to public WIFI networks with Developer Mode ON>.
B<Do not leave home with Developer Mode ON>.
Once you have enabled "USB Debugging", you have
two options for making your device visible to
your desktop and, consequently, to ADB and to this module:
=over 4
=item connect your android device via a USB cable
to your desktop computer. I am not sure if you also
need to tap on the USB charging options and allow
"Transfer Files".
=item connect your device to the same WIFI network
as your desktop computer. Then follow instructions
from, e.g., here L<https://developer.android.com>.
This requires a newer Android version.
=back
=head2 Android Emulators
It is possible to do most things your
smartphone does with an Android Virtual Device.
You can install apps on the the virtual device which
you can register by supplying your real smartphone
number.
List all virtual devices currently available
in your desktop computer, with C<emulator -list-avds>
which outputs something like:
Pixel_2_API_27_x86_
Pixel_2_API_30_x86_
Start a virtual device with C<emulator -avd Pixel_2_API_30_x86_>
And hey, you have an android phone running on your
desktop in its own space, able to access the network
but not the telephone network (no SIM card).
It is possible to create a virtual device
from the command line.
But perhaps it is easier if you download Android Studio
from: L<https://developer.android.com/studio> and follow
the setup there using the GUI. You will need to do this just
once for creating the device, you can then uninstall Android Studio.
Android Studio will download all the
lib/Android/ElectricSheep/Automator/Plugins/Apps/Viber.pm view on Meta::CPAN
return {};
}
# only pod below
=pod
=encoding utf8
=head1 NAME
Android::ElectricSheep::Automator::Plugins::Apps::Viber - Control the Viber app from your desktop via the ElectricSheep Automator
=head1 VERSION
Version 0.06
=head1 WARNING
Current distribution is extremely alpha. API may change.
=head1 SYNOPSIS
An L<Android::ElectricSheep::Automator> plugin which
interacts with the Viber app from the desktop.
use Android::ElectricSheep::Automator::Plugins::Apps::Viber;
my $viber = Android::ElectricSheep::Automator::Plugins::Apps::Viber->new({
'configfile' => $configfile,
'verbosity' => 1,
# we already have a device connected and ready to control
'device-is-connected' => 1,
});
xt/live/110-adb-connect-device.t view on Meta::CPAN
ok(-f $configfile, "config file exists ($configfile).") or BAIL_OUT;
my $mother = Android::ElectricSheep::Automator->new({
'configfile' => $configfile,
'verbosity' => $VERBOSITY,
});
ok(defined($mother), 'Android::ElectricSheep::Automator->new()'." : called and got defined result.") or BAIL_OUT;
# this mother should not be connected
is($mother->is_device_connected(), 0, 'Android::ElectricSheep::Automator->new()'." : called and device is not connected as expected.") or BAIL_OUT;
# get all devices connected to the desktop
my @devices = $mother->adb->devices;
# and make sure we have at least one device connected to the desktop
ok(scalar(@devices)>0, "there are ".@devices." connected devices on the desktop") or BAIL_OUT("At least one device must be connected to the desktop.");
diag "Found connected android device(s) : ".Android::ElectricSheep::Automator::devices_toString(\@devices);
if( 1 == scalar @devices ){
# now create a new mother with just the one connected device
$mother = Android::ElectricSheep::Automator->new({
'configfile' => $configfile,
'verbosity' => $VERBOSITY,
'device-is-connected' => 1,
});
ok(defined($mother), 'Android::ElectricSheep::Automator->new()'." : called and got defined result.") or BAIL_OUT;