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 )