Android-ElectricSheep-Automator
view release on metacpan or search on metacpan
lib/Android/ElectricSheep/Automator/XMLParsers.pm view on Meta::CPAN
package Android::ElectricSheep::Automator::XMLParsers;
use 5.006;
use strict;
use warnings;
our $VERSION = '0.09';
use Mojo::Log;
use XML::LibXML;
use Data::Roundtrip qw/perl2dump no-unicode-escape-permanently/;
# This parser is for parsing UI Automator dump of the screen
# which comes up when you swipe from the bottom up
# which brings all the installed apps, we will find all
# those apps (name+coordinates) and return them as a hash
# keyed on app name.
# e.g. myappname => {'name' => myappname, 'bounds' => [x1,y1,x2,y2]}
# It takes in a UI Automator XML dump either from:
# a file : 'xml-filename' => ...
# or from a string: 'xml-string' => ...
# It returns the apps as a HASHref (keyed on appname) on success
# or undef on failure.
# my $xmlstring = $self->dump_current_screen_ui();
# my $ret = Android::ElectricSheep::Automator::XMLParsers::XMLParser_find_all_apps(
# {'xml-string' => $xmlstring}
# );
# will call fromXML();
# Or make it yourself: adb shell uiautomator dump outfile
# or adb exec-out uiautomator dump /dev/tty | awk '{gsub("UI hierchary dumped to: /dev/tty", "");print}'
sub XMLParser_find_all_apps {
my $params = $_[0];
$params //= {};
my $parent = ( caller(1) )[3] || "N/A";
my $whoami = ( caller(0) )[3];
my $log = (exists($params->{'log'}) && defined($params->{'log'})) ? $params->{'log'} : Mojo::Log->new;
my $verbosity = (exists($params->{'verbosity'}) && defined($params->{'verbosity'})) ? $params->{'verbosity'} : 0;
my $doc;
if( exists($params->{'xml-filename'}) && defined($params->{'xml-filename'}) ){
$doc = eval { XML::LibXML->load_xml(location => $params->{'xml-filename'}) };
if( $@ || (! defined $doc) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'XML::LibXML->load_xml()'." has failed for file '".$params->{'xml-filename'}."' : $@"); return undef }
} elsif( exists($params->{'xml-string'}) && defined($params->{'xml-string'}) ){
$doc = eval { XML::LibXML->load_xml(string => $params->{'xml-string'}) };
if( $@ || (! defined $doc) ){ $log->error($params->{'xml-string'}."\n${whoami} (via $parent), line ".__LINE__." : error, call to ".'XML::LibXML->load_xml()'." has failed for above XML string: $@"); return undef }
} else { $log->error("${whoami} (via $parent), line ".__LINE__." : error, either 'xml-filename' or 'xml-string' must be specified in the input parameters."); return undef }
# find a node AT THE TOP for setting the width and height
# text=""
# content-desc=""
# resource-id=""
# class="android.view.FrameLayout"
my $xpath =
'/hierarchy/node'
. '//node[contains(@resource-id, \'id/apps_view\')]'
. '/node[contains(@resource-id, \'id/apps_list_view\')]'
. '/node[contains(@resource-id, \'id/icon\') and @class=\'android.widget.TextView\']'
;
my @nodes = eval { $doc->findnodes($xpath) };
if( $@ ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'findnodes()'." has failed for this xpath: $xpath : $@"); return undef };
my %ret;
foreach my $anode (@nodes){
my $appname = $anode->getAttribute('text');
my $bounds = $anode->getAttribute('bounds');
$bounds =~ s/\s+//;
my %item = (
'name' => $appname
);
if( $bounds =~ /^\[(\d+),(\d+)\]\[(\d+),(\d+)\]$/ ){
( run in 1.714 second using v1.01-cache-2.11-cpan-39bf76dae61 )