Android-ElectricSheep-Automator
view release on metacpan or search on metacpan
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
</* cleanup temp files on exit */>
"cleanup" : 1
},
"logger" : {
</* 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()
# NOTE: without connecting to a device you can not use
# methods which require a connected device, e.g. open_app(), swipe() etc.
sub new {
my $class = ref($_[0]) || $_[0]; # aka proto
my $params = $_[1] // {};
my $parent = ( caller(1) )[3] || "N/A";
my $whoami = ( caller(0) )[3];
my $self = {
'_private' => {
'confighash' => undef,
'configfile' => '', # this should never be undef
'Android::ADB' => undef,
'debug' => {
'verbosity' => 0,
'cleanup' => 1,
},
'log' => {
'logger-object' => undef,
'logfile' => undef
},
},
# object of type Android::ElectricSheep::Automator::DeviceProperties
# if this is undef, then it means caller did not call connect_device()
# when caller calls disconnect_device(), this becomes undef again
# this is a cheap way to not proceed to device-needed subs, e.g. swipe()
# of course we could make an adb query with e.g. adb get-state
'device-properties' => undef,
# object of type Android::ElectricSheep::Automator::ADB::Device
# which is created when we call connect_device()
'device-object' => undef,
# a hash of installed apps by package name (e.g. android.google.calendar)
# the value will be an AppProperties object if it was enquired or undef
# if it wasn't. As the addition of apps is done in a lazy way, when
# needed, unless specified otherwise. In any event open_app() will add an
# AppProperties object if missing to the specified package.
'apps' => {},
# legacy, no worries.
'apps-roundabout-way' => undef,
};
bless $self => $class;
# this will read configuration and create confighash,
# make logger, verbosity,
# instantiate any objects we need here etc.
if( $self->init($params) ){ print STDERR __PACKAGE__."${whoami} (via $parent), line ".__LINE__." : error, call to init() has failed.\n"; return undef }
# Now we have a logger
my $log = $self->log();
# do module-specific init
if( $self->init_module_specific($params) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to init_module_specific() has failed."); return undef }
# optional params, defaults exist above or in the configfile
if( exists($params->{'verbosity'}) && defined($params->{'verbosity'}) ){ $self->verbosity($params->{'verbosity'}) } # later we will call verbosity()
if( exists($params->{'cleanup'}) && defined($params->{'cleanup'}) ){ $self->cleanup($params->{'cleanup'}) }
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];
my $log = $self->log();
my $verbosity = $self->verbosity;
my ($what_device, $m);
if( exists($params->{'serial'}) && defined($m=$params->{'serial'}) ){
my $devs = $self->devices();
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
=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 identification is required for the method call
# if there is only one connected device:
$mother->connect_device() if scalar(@devices)==0;
# Go Home
$mother->home_screen() or die;
# swipe up/down/left/right
$mother->swipe({'direction'=>up}) or die;
# dt is the time to swipe in millis,
# the shorter the faster the swipe
$mother->swipe({'direction'=>left, 'dt'=>100}) or die;
# tap
$mother->tap({'position'=>[100,200]});
# uses swipe() to move in screens (horizontally):
$mother->next_screen() or die;
$mother->previous_screen() or die;
# bottom navigation:
# the "triangle" back button
$mother->navigation_menu_back_button() or die;
# the "circle" home button
$mother->navigation_menu_home_button() or die;
# the "square" overview button
$mother->navigation_menu_overview_button() or die;
# open/close apps
$mother->open_app({'package'=>qr/calendar$/i}) or die;
$mother->close_app({'package'=>qr/calendar$/i}) or die;
# push pull files
$mother->adb->pull($deviceFile, $localFile);
$mother->adb->push($localFile, $deviceFileOrDir);
# guess what!
my $xmlstr = $mother->dump_current_screen_ui();
# Pull the apk(s) for an app from device and save locally
my $res = $mother->pull_app_apk_from_device({
package => 'com.google.android.calendar'
# or qr/calendar/i
'output-dir' => '/tmp/apks-of-calendar-app',
});
print $res->{'com.google.android.calendar'}->[0]->['local-path'};
# Install apk(s) for an app onto the device
$mother->install_app({
'apk-filename' => ['/tmp/apks/base.apk', '/tmp/apks/config.apk'],
# or just a string scalar '/tmp/apks/1.apk'
# optional params to the adb install command
'install-parameters' => ['-r', '-g']
});
=head1 CONSTRUCTOR
=head2 B<C<new($params)>>
Creates a new C<Android::ElectricSheep::Automator> object. C<$params>
is a hash reference used to pass initialization options which may
or should include the following:
=over 4
=item * B<C<confighash>> or B<C<configfile>>
the configuration
file holds
configuration parameters. Its format is "enhanced" JSON
(see L<Config::JSON::Enhanced>) which is basically JSON
which allows comments between C< E<lt>/* > and C< */E<gt> >.
Here is an example configuration file to get you started:
{
"adb" : {
"path-to-executable" : "/usr/local/android-sdk/platform-tools/adb"
},
"debug" : {
"verbosity" : 0,
</* cleanup temp files on exit */>
"cleanup" : 1
},
"logger" : {
</* log to file if you uncomment this, else console */>
"filename" : "my.log"
}
}
All sections in the configuration are mandatory.
Setting C<"adb"> to the wrong path will yield problems.
C<confighash> is a hash of configuration options with
structure as above 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 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
lib/Android/ElectricSheep/Automator.pm view on Meta::CPAN
electric-sheep-find-installed-apps.pl --configfile config/myapp.conf --device Pixel_2_API_30_x86_ --output myapps.json --fast
=head2 B<C<electric-sheep-open-app.pl>>
Open an app by its exact name or a keyword matching it (uniquely):
electric-sheep-open-app.pl --configfile config/myapp.conf --name com.android.settings
electric-sheep-open-app.pl --configfile config/myapp.conf --keyword 'clock'
Note that it constructs a regular expression from escaped user input.
=head2 B<C<electric-sheep-close-app.pl>>
Close an app by its exact name or a keyword matching it (uniquely):
electric-sheep-close-app.pl --configfile config/myapp.conf --name com.android.settings
electric-sheep-close-app.pl --configfile config/myapp.conf --keyword 'clock'
Note that it constructs a regular expression from escaped user input.
=head2 B<C<electric-sheep-dump-ui.pl>>
Dump the current screen UI as XML to STDOUT or to a file:
electric-sheep-dump-ui.pl --configfile config/myapp.conf --output ui.xml
Note that it constructs a regular expression from escaped user input.
=head2 B<C<electric-sheep-dump-current-location.pl>>
Dump the GPS / geo-location position for the device from its various providers, if enabled.
electric-sheep-dump-current-location.pl --configfile config/myapp.conf --output geolocation.json
=head2 B<C<electric-sheep-emulator-geofix.pl>>
Set the GPS / geo-location position to the specified coordinates.
electric-sheep-dump-ui.pl --configfile config/myapp.conf --latitude 12.3 --longitude 45.6
=head2 B<C<electric-sheep-dump-screen-shot.pl>>
Take a screenshot of the device (current screen) and save to a PNG file.
electric-sheep-dump-screen-shot.pl --configfile config/myapp.conf --output screenshot.png
=head2 B<C<electric-sheep-dump-screen-video.pl>>
Record a video of the device's current screen and save to an MP4 file.
electric-sheep-dump-screen-video.pl --configfile config/myapp.conf --output video.mp4 --time-limit 30
=head2 B<C<electric-sheep-pull-app-apk.pl>>
Extract the APK file (java bytecode) for an app installed on the device and save locally, perhaps, for disassembly and/or modification and/or re-installation.
electric-sheep-pull-app-apk.pl --package calendar2 --wildcard --output anoutdir --configfile config/myapp.conf --device Pixel_2_API_30_x86_
=head2 B<C<electric-sheep-install-app>>
Install an APK file onto the device, passing extra installation
parameters C<-r> (for re-install) and C<-g> (for granting permissions),
electric-sheep-install-app --apk-filename test.apk -p '-r' -p '-g' --configfile config/myapp.conf --device Pixel_2_API_30_x86_
=head2 B<C<electric-sheep-viber-send-message.pl>>
Send a message using the Viber app.
electric-sheep-viber-send-message.pl --message 'hello%sthere' --recipient 'george' --configfile config/myapp.conf --device Pixel_2_API_30_x86_
This one saves a lot of debugging information to C<debug> which can be used to
deal with special cases or different versions of Viber:
electric-sheep-viber-send-message.pl --outbase debug --verbosity 1 --message 'hello%sthere' --recipient 'george' --configfile config/myapp.conf --device Pixel_2_API_30_x86_
=head1 TESTING
The normal tests under the C<t/> directory, initiated with C<make test> command,
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 the C<xt/live> directory, initiated with
C<make livetest> command, require
an Android emulator or real device (the latter B<is not recommended>)
connected to your desktop computer on which you are doing the testing.
Note that 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.
It is very easy to get an emulated Android device running on any OS.
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
buggers.
At least one of the I<author tests> under the C<xt/author> directory,
initiated with
C<make authortest> command, require an APK file (to be installed on the connected
device) which is quite large and it is not included in the distribution
bundle of this module. Anyway, it is not a good idea to install
an unknown APK to your device. But if you want to make this test
then pull an APK of an existing app on your connected device
with L<electric-sheep-pull-app-apk.pl> and point the test file
to this APK.
Testing will not send any messages via the device's apps.
E.g. the plugin L<Android::ElectricSheep::Automator::Plugins::Apps::Viber>
will not send a message via Viber but it will mock it.
The live tests will sometimes fail because, so far,
something unexpected happened in the device. For example,
in testing sending input text to a text-edit widget,
the calendar will be opened and a new entry will be added
and its text-edit widget will be targeted. Well, sometimes
the calendar app will give you some notification
on startup 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
"I<something is not responding, shall I close it>".
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 it
(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.
You will not be using the IDE or anything, just
the accompaniying binaries and libraries it comes with.
Additionally, Android Studio offers possibly the
easiest way to create Android Virtual Devices (AVD) which emulate
an Android phone of various specifications, phone models and sizes,
API levels, etc.
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. Perhaps you will need an Android
emulator image which comes with Google Play Services,
if you are installing apps from their store.
This is great for
experimenting without plugging 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
( run in 1.811 second using v1.01-cache-2.11-cpan-39bf76dae61 )