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;

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

	# click the Recipient contact name 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 }
	usleep(1.5);

	# Put the text into the text-edit Message... (note: ... is unicode ellipses something)
	# get the UI
	$outfile = defined($outbase) ? $outbase.'_chat.xml' : undef;
	$ui = $self->mother->dump_current_screen_ui({'filename'=>$outfile});
	$dom = $ui->{'XML::LibXML'};
	$xc = $ui->{'XML::LibXML::XPathContext'};
	$asel = '//node'
		. '['
		.   ' matches(@class,\'EditText$\',"i")'
		.   ' and @resource-id="com.viber.voip:id/send_text"'
		.   ' and @package="com.viber.voip"'
		. ']'
	;
	@nodes = $xc->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 specified recipient (".$params->{'recipient'}."): ${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 specified reci...
	$node = $nodes[0];
	$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]];

	# add the text in the node
	# the message will be sanitised in input_text(), spaces will be replaced with %s
	# unicode is not supported
	# TODO: perhaps warn about unicode in message?
	if( $self->mother->input_text({'bounds' => $bounds, 'text' => $message}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to input text on ${boundstr}. Note that there may be a maximum length for the message. Your message w...
	sleep(1);

	# and send!
	# on the same XML we search for the send button
	$asel = '//node'
		. '['
		.   ' matches(@class,\'FrameLayout$\',"i")'
		.   ' and @resource-id="com.viber.voip:id/btn_send"'
		.   ' and @package="com.viber.voip"'
		. ']'
	;
	@nodes = $xc->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 recipient (".$params->{'recipient'}.") from the contacts on the central pane: ${asel}"); return un...
	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 recipient (".$...
	$node = $nodes[0];
	# click the Send button
	$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]];
	# I know it does not support sending unicode to a textbox but ...
	# TODO: check with a unicode recipient handle to see if this works
	if( $verbosity > 0 ){ $log->info("To: '".Encode::decode_utf8($recipient)."'\nMessage:\n".Encode::decode_utf8($message)."\n--end message.\n${whoami} (via $parent), line ".__LINE__." : about to send the above message ...") }
	if( $mock == 0 ){
		# click it if not mocking
		if( $self->mother->tap({'bounds' => $bounds}) ){ $log->error("${whoami} (via $parent), line ".__LINE__." : error, failed to tap on $bounds."); return undef }
	} else {
		$log->warn("${whoami} (via $parent), line ".__LINE__." : The 'Send' button was not clicked because mock is ON.")
	}
	usleep(0.8);

	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.09

=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,
    });

    # go to home screen to start fresh
    $plugobj->mother->home_screen();

    # open the viber app
    $plugobj->open_app() or die

    # is the app running now?
    $plugobj->is_app_running() or die

    $plugobj->send_message({
        'recipient' => 'My Notes', # some of your contacts
        # 1) no unicode, 2) each space must be converted to '%s'
        'message' => 'thank%syou'
    }) or die;

=head1 MOTIVATION

I wanted to send viber messages from my beloved Linux Desktop
using the command line. I did not want to install Viber app on
my smartphone. I do not use a smartphone except for
reading the news.

=head1 INSTALLING VIBER

Firstly, I installed an Android emulator
in my desktop computer, as described here
L<https://metacpan.org/pod/Android::ElectricSheep::Automator#Android-Emulators>

Note that some Android emulators do not allow access to
Google App Store (whatever is called). You can only install apps
via third party which is not very safe (even for your emulator).
See this for how to enable the App Store:



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