Android-ElectricSheep-Automator

 view release on metacpan or  search on metacpan

lib/Android/ElectricSheep/Automator/Plugins/Apps/Viber.pm  view on Meta::CPAN

package Android::ElectricSheep::Automator::Plugins::Apps::Viber;

use strict;
use warnings;

use parent 'Android::ElectricSheep::Automator::Plugins::Apps::Base';

use Time::HiRes qw/usleep/;
use Encode qw/encode_utf8 decode_utf8/;
use Data::Roundtrip qw/perl2dump no-unicode-escape-permanently/;

use Android::ElectricSheep::Automator::XMLParsers;

sub new {
	my ($class, $params) = @_;
	my $self = $class->SUPER::new({
		%$params,
		'child-class' => $class,
	});
	$self->{'_private'}->{'appname'} = 'com.viber.voip';

	return $self;
}

# keeps pressing the back-arrow at the top of the app to hopefully
# arrive at the main activity of the app,
# TODO: is there a way to tell it to go to main activity ? WelcomeActivity does not seem to work
# returns 1 on failure, 0 on success.
sub navigate_to_viber_home_activity {
	my ($self, $params) = @_;
	my $parent = ( caller(1) )[3] || "N/A";
	my $whoami = ( caller(0) )[3];
	my $log = $self->log();
	my $verbosity = $self->verbosity();

	my ($outbase, $outfile);
	# for debugging purposes, save each UI we get here
	$outbase = exists($params->{'outbase'}) ? $params->{'outbase'} : undef;

	my ($ui, $dom, $xc, $asel, @nodes, $N, $node, $boundstr, $bounds);

	my $repeats = 3;
	my $repeatsUI = 3;
	ONBACKARROW:
	while(--$repeats > 0){
		# we assume the app is open and at the foreground
		# get the UI
		$outfile = defined($outbase) ? $outbase.'_main.xml' : undef;
		do {
			$ui = $self->mother->dump_current_screen_ui({'filename'=>$outfile});
			usleep(0.75);
		} while( ($repeatsUI-- > 0) && ! defined($ui) );
		if( ! defined $ui ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to dump the UI, call to ".'dump_current_screen_ui()'." has failed after a number of repeats. I am not sure what the problem is, most likely a race conditio...

		$dom = $ui->{'XML::LibXML'};
		$xc = $ui->{'XML::LibXML::XPathContext'};
		$asel = '//node'
			. '['
			.   ' matches(@content-desc,\'navigate\s+up\',"i")'
			.   ' and matches(@class,\'ImageButton\',"i")'
			.   ' and @package="com.viber.voip"'
			.   ' and matches(@bounds,\'^\[\',"i")'
			. ']'
		;
		@nodes = $xc->findnodes($asel);
		$N = scalar @nodes;
		if( $N == 0 ){
			if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : no nodes matching this XPath selector (for getting the 'back-arrow' icon, hopefully, this means we reached home-page of the app): ${asel}") }
			last ONBACKARROW;
		} elsif( $N > 1 ){ $log->error("--begin matched nodes:\n".join("\n", @nodes)."\n--end nodes matched.\n\n${whoami} (via $parent), line ".__LINE__." : error, matched more than one node (see above) with this XPath selector (for getting the 'Chats' ico...
		$node = $nodes[0];

		# click the 'back-arrow' at the top
		$boundstr = $node->getAttribute('bounds');
		if( ! defined $boundstr ){ $log->error("${node}\n\n${whoami} (via $parent), line ".__LINE__." : error, above node does not have attribute 'bounds'."); return 1 }
		if( $boundstr !~ /\[\s*(\d+)\s*,\s*(\d+)\]\s*\[\s*(\d+)\s*,\s*(\d+)\]/ ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to parse bounds string: '$boundstr'."); return 1 }
		$bounds = [[$1,$2],[$3,$4]];
		# click it
		if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : clicking the back-arrow ...") }
		if( $self->mother->tap({'bounds' => $bounds}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to tap on $bounds."); return 1 }
		usleep(0.75);
	}

	if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : hopefully we are now at the very first screen of the app.") }
	return 0 # success
}

# "type" the specified message into 
# the message will be sanitised in input_text(), spaces will be replaced with %s
# unicode is not supported
sub send_message {
	my ($self, $params) = @_;
	my $parent = ( caller(1) )[3] || "N/A";
	my $whoami = ( caller(0) )[3];
	my $log = $self->log();
	my $verbosity = $self->verbosity();

	my ($recipient, $message, $outbase, $outfile);
	if( ! exists($params->{'recipient'}) || ! defined($recipient=$params->{'recipient'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'recipient' was not specified."); return undef }
	if( ! exists($params->{'message'}) || ! defined($message=$params->{'message'}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, input parameter 'message' was not specified."); return undef }
	# for debugging purposes, save each UI we get here
	$outbase = exists($params->{'outbase'}) ? $params->{'outbase'} : undef;
	# do everything except clicking the send button
	my $mock = exists($params->{'mock'}) ? $params->{'mock'} : 0;

	my ($dom, $xc, $asel, @nodes, $N, $node, $boundstr, $bounds);

	if( $self->navigate_to_viber_home_activity({'outbase'=>$outbase}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, call to ".'navigate_to_viber_home_activity()'." has failed."); return undef }
	usleep(1.5);

	# we assume the app is open and at the foreground
	# get the UI
	$outfile = defined($outbase) ? $outbase.'_main.xml' : undef;
	# there is always the chance you get 'Is taking too long'...
	# and all will fail TODO!
	my $ui;
	my $repeatsUI = 3;
	do {
		if( $verbosity > 0 ){ $log->info("${whoami} (via $parent), line ".__LINE__." : calling ".'dump_current_screen_ui()'." for at repeat $repeatsUI ...") }
		$ui = $self->mother->dump_current_screen_ui({'filename'=>$outfile});
		usleep(0.75);
	} while( ($repeatsUI-- > 0) && (! defined($ui)) );
	if( ! defined $ui ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to dump the UI, call to ".'dump_current_screen_ui()'." has failed after a number of repeats. I am not sure what the problem is, most likely a race condition...

	$dom = $ui->{'XML::LibXML'};
	$asel = '//node[@text="Chats" and @resource-id="com.viber.voip:id/bottomBarItemTitle"]';
	@nodes = $dom->findnodes($asel);
	$N = scalar @nodes;
	if( $N == 0 ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to find any nodes matching this XPath selector (for getting the 'Chats' icon): ${asel}"); return undef }
	elsif( $N > 1 ){ $log->error("--begin matched nodes:\n".join("\n", @nodes)."\n--end nodes matched.\n\n${whoami} (via $parent), line ".__LINE__." : error, matched more than one node (see above) with this XPath selector (for getting the 'Chats' icon):...
	$node = $nodes[0];

	# click the 'Chats' at the bottom
	$boundstr = $node->getAttribute('bounds');
	if( ! defined $boundstr ){ $log->error("${node}\n\n${whoami} (via $parent), line ".__LINE__." : error, above node does not have attribute 'bounds'."); return undef }
	if( $boundstr !~ /\[\s*(\d+)\s*,\s*(\d+)\]\s*\[\s*(\d+)\s*,\s*(\d+)\]/ ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to parse bounds string: '$boundstr'."); return undef }
	$bounds = [[$1,$2],[$3,$4]];
	# click it
	if( $self->mother->tap({'bounds' => $bounds}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to tap on $bounds."); return undef }
	sleep(1);

	# now we are on the chats screen, in the centre pane there are all our contacts
	# get the ui for this screen
	$outfile = defined($outbase) ? $outbase.'_chats.xml' : undef;
	$ui = $self->mother->dump_current_screen_ui({'filename'=>$outfile});
	$dom = $ui->{'XML::LibXML'};
	$xc = $ui->{'XML::LibXML::XPathContext'};
	$asel = '//node'
		. '['
		.   '@text'
		.   ' and matches(@text,\''.$params->{'recipient'}.'\',"i")'
		.   ' and @resource-id="com.viber.voip:id/from"'
		. ']'
	;



( run in 0.999 second using v1.01-cache-2.11-cpan-39bf76dae61 )