line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
3
|
|
|
|
|
|
|
# Command line build of an Android apk without resorting to ant or gradle |
4
|
|
|
|
|
|
|
# Philip R Brenan at gmail dot com, Appa Apps Ltd, 2017 |
5
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
6
|
|
|
|
|
|
|
package Android::Build; |
7
|
|
|
|
|
|
|
require v5.16.0; |
8
|
1
|
|
|
1
|
|
691
|
use warnings FATAL => qw(all); |
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
48
|
|
9
|
1
|
|
|
1
|
|
8
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
44
|
|
10
|
1
|
|
|
1
|
|
8
|
use Carp qw(confess); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
93
|
|
11
|
1
|
|
|
1
|
|
472
|
use Data::Dump qw(dump); |
|
1
|
|
|
|
|
6860
|
|
|
1
|
|
|
|
|
62
|
|
12
|
1
|
|
|
1
|
|
894
|
use Data::Table::Text qw(:all); |
|
1
|
|
|
|
|
63767
|
|
|
1
|
|
|
|
|
699
|
|
13
|
1
|
|
|
1
|
|
669
|
use File::Copy; |
|
1
|
|
|
|
|
2197
|
|
|
1
|
|
|
|
|
64
|
|
14
|
1
|
|
|
1
|
|
6
|
use POSIX qw(strftime); # http://www.cplusplus.com/reference/ctime/strftime/ |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
8
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
our $VERSION = '20180626'; |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
19
|
|
|
|
|
|
|
# Constants |
20
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my $home = currentDirectory(); # Home directory |
23
|
|
|
|
|
|
|
my $permissions = # Default permissions |
24
|
|
|
|
|
|
|
[qw(INTERNET ACCESS_WIFI_STATE ACCESS_NETWORK_STATE WRITE_EXTERNAL_STORAGE), |
25
|
|
|
|
|
|
|
qw(READ_EXTERNAL_STORAGE RECEIVE_BOOT_COMPLETED)]; |
26
|
|
|
|
|
|
|
my $version = strftime('%Y%m%d', localtime); # Version number without dots |
27
|
|
|
|
|
|
|
my $javaTarget = 7; # Java release level to target |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
30
|
|
|
|
|
|
|
# Private methods |
31
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub getSDKLevels($) # File name of Android jar for linting |
34
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; # Android build |
35
|
0
|
|
|
|
|
0
|
my $l = $android->sdkLevels; |
36
|
0
|
0
|
0
|
|
|
0
|
return @$l if $l and @$l; |
37
|
0
|
|
|
|
|
0
|
(15,25) |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub getInstructions # How to get the build tools |
41
|
|
|
|
|
|
|
{<
|
42
|
|
|
|
|
|
|
Download the Linux tools as specified at the end of page: |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
https://developer.android.com/studio/index.html |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
last set to: |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Unzip the retrieved file to get the sdkmanager. Use the sdkmanager to get the |
51
|
|
|
|
|
|
|
version of the SDK that you need, for example: |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sdkmanager 'platforms;android-25' 'build-tools;25.0.3 |
54
|
|
|
|
|
|
|
END |
55
|
0
|
|
|
0
|
0
|
0
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub getPlatform # Get and validate the SDK Platform folder |
58
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
59
|
0
|
|
|
|
|
0
|
my $f = $a->platformX; |
60
|
0
|
0
|
|
|
|
0
|
$f or confess <
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
"platform" parameter required - it should be the name of the folder containing |
63
|
|
|
|
|
|
|
the android.jar file that you wish to use. You can get this jar file from: |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
END |
66
|
0
|
0
|
|
|
|
0
|
-d $f or confess <
|
67
|
|
|
|
|
|
|
Cannot find platformTools folder: |
68
|
|
|
|
|
|
|
$f |
69
|
|
|
|
|
|
|
END |
70
|
0
|
|
|
|
|
0
|
$f |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub getBuildTools # Get and validate the SDK Platform build-tools folder |
74
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
75
|
0
|
|
|
|
|
0
|
my $f = $a->buildToolsX; |
76
|
0
|
0
|
|
|
|
0
|
$f or confess <
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
"buildTools" parameter required - it should be the name of the folder |
79
|
|
|
|
|
|
|
containing the Android SDK build tools. You can get these tools from: |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
END |
82
|
0
|
0
|
|
|
|
0
|
-d $f or confess <
|
83
|
|
|
|
|
|
|
Cannot find buildTools folder: |
84
|
|
|
|
|
|
|
$f |
85
|
|
|
|
|
|
|
END |
86
|
0
|
|
|
|
|
0
|
$f |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub getPlatformTools # Get and validate the SDK Platform tools folder |
90
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
91
|
0
|
|
|
|
|
0
|
my $f = $a->platformToolsX; |
92
|
0
|
0
|
|
|
|
0
|
$f or confess <
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
"platformTools" parameter required - it should be the name of the folder |
95
|
|
|
|
|
|
|
containing the Android SDK platform tools. You can get these tools from: |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
END |
98
|
0
|
0
|
|
|
|
0
|
-d $f or confess <
|
99
|
|
|
|
|
|
|
Cannot find platformTools folder: |
100
|
|
|
|
|
|
|
$f |
101
|
|
|
|
|
|
|
END |
102
|
0
|
|
|
|
|
0
|
$f |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub getDevice($) # Device to be used |
106
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
107
|
0
|
|
|
|
|
0
|
my $d = $android->device; |
108
|
0
|
0
|
|
|
|
0
|
return '-e' unless $d; |
109
|
0
|
0
|
|
|
|
0
|
return $d if $d =~ m(\A-)s; |
110
|
0
|
|
|
|
|
0
|
"-s $d" |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub getAndroidJar($) # File name of Android jar for linting |
114
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
115
|
0
|
|
|
|
|
0
|
my $p = $android->getPlatform; |
116
|
0
|
|
|
|
|
0
|
my $a = filePath($p, qw(android.jar)); |
117
|
0
|
0
|
|
|
|
0
|
-e $a or confess "Cannot find android.jar via file:\n$a\n"; |
118
|
0
|
|
|
|
|
0
|
$a |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub getPackage # Get and validate the package name for this app |
122
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
123
|
0
|
|
|
|
|
0
|
my $d = $a->package; |
124
|
0
|
0
|
|
|
|
0
|
$d or confess <
|
125
|
|
|
|
|
|
|
"package" parameter required - it should be the value used on the package |
126
|
|
|
|
|
|
|
statement in the Activity for this app |
127
|
|
|
|
|
|
|
END |
128
|
0
|
0
|
|
|
|
0
|
$d =~ /\./ or confess <
|
129
|
|
|
|
|
|
|
package "$d" should contain at least one '.' |
130
|
|
|
|
|
|
|
END |
131
|
0
|
|
|
|
|
0
|
$d |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub getLintFile # Name of the file to be linted |
135
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
136
|
0
|
|
|
|
|
0
|
my $f = $a->lintFileX; |
137
|
0
|
0
|
|
|
|
0
|
$f or confess <
|
138
|
|
|
|
|
|
|
"lintFile" parameter required to lint a file |
139
|
|
|
|
|
|
|
END |
140
|
0
|
0
|
|
|
|
0
|
-e $f or confess <
|
141
|
|
|
|
|
|
|
File to be linted does not exist: |
142
|
|
|
|
|
|
|
$f |
143
|
|
|
|
|
|
|
END |
144
|
0
|
|
|
|
|
0
|
$f |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub getActivity # Activity for app |
148
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
149
|
0
|
|
0
|
|
|
0
|
$a->activity // 'Activity'; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub getAppName # Single word name of app used to construct file names |
153
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
154
|
0
|
|
|
|
|
0
|
my $d = $a->getPackage; |
155
|
0
|
|
|
|
|
0
|
(split /\./, $d)[-1]; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub getTitle # Title of app |
159
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
160
|
0
|
|
0
|
|
|
0
|
$a->title // $a->getAppName; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub apkFileName # Apk name - shorn of path |
164
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
165
|
0
|
|
|
|
|
0
|
$a->getAppName.'.apk'; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub apk # Apk name - with full path |
169
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
170
|
0
|
|
|
|
|
0
|
$a->getBinFolder.$a->apkFileName; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub getVersion # Version of the app or default to today's date |
174
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
175
|
0
|
|
0
|
|
|
0
|
$a->version // $version; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub buildArea($) # Build folder name |
179
|
0
|
|
|
0
|
0
|
0
|
{my ($a) = @_; |
180
|
0
|
|
0
|
|
|
0
|
$a->buildFolder // '/tmp/app/' # Either the user supplied build folder name or the default |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
0
|
|
|
0
|
0
|
0
|
sub getAssFolder($) {my ($a) = @_; $a->buildArea.'assets/'} # Assets folder name |
|
0
|
|
|
|
|
0
|
|
184
|
0
|
|
|
0
|
0
|
0
|
sub getBinFolder($) {my ($a) = @_; $a->buildArea.'bin/'} # Bin folder name |
|
0
|
|
|
|
|
0
|
|
185
|
0
|
|
|
0
|
0
|
0
|
sub getGenFolder($) {my ($a) = @_; $a->buildArea.'gen/'} # Gen folder name |
|
0
|
|
|
|
|
0
|
|
186
|
0
|
|
|
0
|
0
|
0
|
sub getResFolder($) {my ($a) = @_; $a->buildArea.'res/'} # Res folder name |
|
0
|
|
|
|
|
0
|
|
187
|
0
|
|
|
0
|
0
|
0
|
sub getManifestFile($) {my ($a) = @_; $a->buildArea.'AndroidManifest.xml'} # Name of manifest file |
|
0
|
|
|
|
|
0
|
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub logMessage($@) # Log a message |
190
|
0
|
|
|
0
|
0
|
0
|
{my ($android, @message) = @_; |
191
|
0
|
|
|
|
|
0
|
my $s = join '', grep {$_} @message; |
|
0
|
|
|
|
|
0
|
|
192
|
0
|
0
|
|
|
|
0
|
chomp($s) if $s =~ /\n\Z/; |
193
|
0
|
|
|
|
|
0
|
push @{$android->log}, $s; |
|
0
|
|
|
|
|
0
|
|
194
|
0
|
0
|
|
|
|
0
|
say STDERR $s if -t STDERR; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
198
|
|
|
|
|
|
|
# Create icons for app |
199
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
sub pushIcon # Create and transfer each icon using Imagemagick |
202
|
0
|
|
|
0
|
0
|
0
|
{my ($android, $icon, $size, $dir) = @_; |
203
|
0
|
|
|
|
|
0
|
my $res = $android->getResFolder; |
204
|
0
|
|
|
|
|
0
|
my $man = $android->getManifestFile; |
205
|
|
|
|
|
|
|
|
206
|
0
|
|
|
|
|
0
|
for my $i(qw(ic_launcher)) |
207
|
0
|
|
|
|
|
0
|
{for my $d(qw(drawable)) |
208
|
0
|
|
|
|
|
0
|
{my $s = $size; |
209
|
0
|
|
|
|
|
0
|
my $T = $res.$d.'-'.$dir.'dpi/'.$i.'.png'; |
210
|
0
|
|
|
|
|
0
|
makePath($T); |
211
|
0
|
|
|
|
|
0
|
unlink $T; |
212
|
0
|
|
|
|
|
0
|
my $c = "convert -strip \"$icon\" -resize ${s}x${s}! \"$T\""; # Convert icon to required size and make it square |
213
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
0
|
my $r = zzz($c); |
215
|
|
|
|
|
|
|
# say STDERR dump([$c, $icon, $T, -e $T, fileSize($T), $r ]); |
216
|
0
|
0
|
0
|
|
|
0
|
confess "Unable to create icon:\n$T\n$r\n" # Check icon was created |
|
|
|
0
|
|
|
|
|
217
|
|
|
|
|
|
|
if $r or !-e $T or fileSize($T) < 10; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub pushIcons # Create icons possibly in parallel |
223
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
224
|
0
|
|
|
|
|
0
|
my $icon = $android->iconX; |
225
|
0
|
0
|
|
|
|
0
|
-e $icon or confess "Cannot find icon file:\n$icon\n"; |
226
|
0
|
|
|
|
|
0
|
my @pid; |
227
|
0
|
|
|
|
|
0
|
my @i = ([48, "m"], [72, "h"], [96, "xh"], [144, "xxh"]); # Icon specifications |
228
|
|
|
|
|
|
|
|
229
|
0
|
0
|
|
|
|
0
|
if ($android->fastIcons) # Speed up - but it does produce a lot of error messages |
230
|
0
|
|
|
|
|
0
|
{for(@i) |
231
|
0
|
0
|
|
|
|
0
|
{if (my $pid = fork()) |
232
|
0
|
|
|
|
|
0
|
{push @pid, $pid |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
else |
235
|
0
|
|
|
|
|
0
|
{eval {$android->pushIcon($icon, @$_)}; |
|
0
|
|
|
|
|
0
|
|
236
|
0
|
|
|
|
|
0
|
exit; |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
} |
239
|
0
|
|
|
|
|
0
|
waitpid($_, 0) for @pid; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
else |
242
|
0
|
|
|
|
|
0
|
{$android->pushIcon($icon, @$_) for @i; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
247
|
|
|
|
|
|
|
# Create manifest for app |
248
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
sub addPermissions # Create permissions |
251
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
252
|
0
|
|
|
|
|
0
|
my $P = "android.permission"; |
253
|
0
|
|
|
|
|
0
|
my %p = (map {$_=>1} @{$android->permissions}); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
254
|
0
|
|
|
|
|
0
|
my $p = "\n"; |
255
|
|
|
|
|
|
|
|
256
|
0
|
|
|
|
|
0
|
for(sort keys %p) |
257
|
0
|
|
|
|
|
0
|
{$p .= " \n"; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
$p |
261
|
0
|
|
|
|
|
0
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub manifest |
264
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
265
|
0
|
|
|
|
|
0
|
my $permissions = $android->addPermissions; |
266
|
0
|
|
|
|
|
0
|
my ($minSdk, $targetSdk) = $android->getSDKLevels; |
267
|
0
|
|
|
|
|
0
|
my $package = $android->getPackage; |
268
|
0
|
|
|
|
|
0
|
my $version = $android->getVersion; |
269
|
0
|
|
|
|
|
0
|
my $man = $android->getManifestFile; |
270
|
0
|
|
|
|
|
0
|
my $activity = $android->activityX; |
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
0
|
my $manifest = << "END"; |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
package="$package" |
276
|
|
|
|
|
|
|
android:installLocation="auto" |
277
|
|
|
|
|
|
|
android:versionCode="$version" |
278
|
|
|
|
|
|
|
android:versionName="\@string/versionName"> |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
android:minSdkVersion="$minSdk" |
282
|
|
|
|
|
|
|
android:targetSdkVersion="$targetSdk"/> |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
android:allowBackup="true" |
285
|
|
|
|
|
|
|
android:icon="\@drawable/ic_launcher" |
286
|
|
|
|
|
|
|
android:largeHeap="true" |
287
|
|
|
|
|
|
|
android:debuggable="true" |
288
|
|
|
|
|
|
|
android:hardwareAccelerated="true" |
289
|
|
|
|
|
|
|
android:label="\@string/app_name"> |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
android:name=".$activity" |
292
|
|
|
|
|
|
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize" |
293
|
|
|
|
|
|
|
android:screenOrientation="sensor" |
294
|
|
|
|
|
|
|
android:theme="\@android:style/Theme.NoTitleBar" |
295
|
|
|
|
|
|
|
android:label="\@string/app_name"> |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
$permissions |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
END |
305
|
0
|
0
|
|
|
|
0
|
$manifest =~ s/android:debuggable="true"//gs unless $android->debug; |
306
|
0
|
|
|
|
|
0
|
writeFile($man, $manifest); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
310
|
|
|
|
|
|
|
# Create resources for app |
311
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub resources() |
314
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
315
|
0
|
|
|
|
|
0
|
my $title = $android->getTitle; |
316
|
0
|
|
|
|
|
0
|
my $version = $android->getVersion; |
317
|
0
|
|
0
|
|
|
0
|
my $parameters = $android->parameters // ''; |
318
|
0
|
|
|
|
|
0
|
my $package = $android->getPackage; |
319
|
0
|
|
|
|
|
0
|
my $res = $android->getResFolder; |
320
|
|
|
|
|
|
|
my $strings = sub |
321
|
0
|
0
|
|
0
|
|
0
|
{return qq($parameters) |
322
|
|
|
|
|
|
|
unless ref $parameters; |
323
|
0
|
|
|
|
|
0
|
my $s = ''; |
324
|
0
|
|
|
|
|
0
|
for my $key(sort keys %$parameters) |
325
|
0
|
|
|
|
|
0
|
{my $val = $parameters->{$key}; |
326
|
0
|
|
|
|
|
0
|
$s .= qq($val\n); |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
$s |
329
|
0
|
|
|
|
|
0
|
}->(); |
|
0
|
|
|
|
|
0
|
|
330
|
0
|
|
|
|
|
0
|
my $t = << "END"; |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
$package |
334
|
|
|
|
|
|
|
$title |
335
|
|
|
|
|
|
|
$version |
336
|
|
|
|
|
|
|
$strings |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
END |
339
|
0
|
|
|
|
|
0
|
writeFile($res."values/strings.xml", $t); |
340
|
|
|
|
|
|
|
|
341
|
0
|
0
|
|
|
|
0
|
if (my $titles = $android->titles) # Create additional titles from a hash of: {ISO::639 2 digit language code=>title in that language} |
342
|
0
|
|
|
|
|
0
|
{for my $l(sort keys %$titles) |
343
|
0
|
|
|
|
|
0
|
{my $t = $title->{$l}; |
344
|
0
|
|
|
|
|
0
|
writeFile($res."values-$l/strings.xml", <
|
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
$t |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
END |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
355
|
|
|
|
|
|
|
# Create app |
356
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
sub create |
359
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
360
|
0
|
|
|
|
|
0
|
$android->pushIcons; |
361
|
0
|
|
|
|
|
0
|
$android->manifest; |
362
|
0
|
|
|
|
|
0
|
$android->resources; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
366
|
|
|
|
|
|
|
# Make app |
367
|
|
|
|
|
|
|
#------------------------------------------------------------------------------- |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub getAdb |
370
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
371
|
0
|
|
|
|
|
0
|
filePath($android->getPlatformTools, qw(adb)) |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
my $confirmRequiredUtilitiesAreInPosition; |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub confirmRequiredUtilitiesAreInPosition($) # Confirm required utilities are in position |
377
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
378
|
0
|
0
|
|
|
|
0
|
return if $confirmRequiredUtilitiesAreInPosition++; # Only do this once per run |
379
|
|
|
|
|
|
|
|
380
|
0
|
|
|
|
|
0
|
my $buildTools = $android->getBuildTools; |
381
|
0
|
|
|
|
|
0
|
my $adb = $android->getAdb; |
382
|
0
|
|
|
|
|
0
|
my $aapt = filePath($buildTools, qw(aapt)); |
383
|
0
|
|
|
|
|
0
|
my $dx = filePath($buildTools, qw(dx)); |
384
|
0
|
|
|
|
|
0
|
my $zipAlign = filePath($buildTools, qw(zipalign)); |
385
|
|
|
|
|
|
|
|
386
|
0
|
|
|
|
|
0
|
zzz("$aapt version", qr(Android Asset Packaging Tool), 0, |
387
|
|
|
|
|
|
|
"aapt not found at:\n$aapt"); |
388
|
0
|
|
|
|
|
0
|
zzz("$adb version", qr(Android Debug Bridge), 0, "adb not found at:\n$adb"); |
389
|
0
|
|
|
|
|
0
|
zzz("$dx --version", qr(dx version), 0, "dx not found at:\n$dx"); |
390
|
0
|
|
|
|
|
0
|
zzz("jarsigner", qr(Usage: jarsigner), 0, "jarsigner not found"); |
391
|
0
|
|
|
|
|
0
|
zzz("javac -version", qr(javac), 0, "javac not found"); |
392
|
0
|
|
|
|
|
0
|
zzz("zip -v", qr(Info-ZIP), 0, "zip not found\n"); |
393
|
0
|
|
|
|
|
0
|
zzz("$zipAlign", 0, 2, "zipalign not found at:\n$zipAlign"); |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
sub signApkFile($$) # Sign an apk file |
397
|
0
|
|
|
0
|
0
|
0
|
{my ($android, $apkFile) = @_; # Android, apk file to sign |
398
|
0
|
|
|
|
|
0
|
$android->confirmRequiredUtilitiesAreInPosition; |
399
|
|
|
|
|
|
|
|
400
|
0
|
|
|
|
|
0
|
my $keyStoreFile = $android->keyStoreFileX; |
401
|
0
|
0
|
|
|
|
0
|
-e $keyStoreFile or confess"Key store file does not exists:\n$keyStoreFile\n"; |
402
|
0
|
|
|
|
|
0
|
my $keyAlias = $android->keyAliasX; |
403
|
0
|
|
|
|
|
0
|
my $keyStorePwd = $android->keyStorePwd; |
404
|
|
|
|
|
|
|
|
405
|
0
|
0
|
|
|
|
0
|
my $alg = $android->debug ? '' : "-sigalg SHA1withRSA -digestalg SHA1"; |
406
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
0
|
my $c = |
408
|
|
|
|
|
|
|
"echo $keyStorePwd |". |
409
|
|
|
|
|
|
|
"jarsigner $alg -keystore $keyStoreFile $apkFile $keyAlias"; |
410
|
0
|
|
|
|
|
0
|
my $s = zzz($c); |
411
|
|
|
|
|
|
|
|
412
|
0
|
0
|
|
|
|
0
|
$s =~ /reference a valid KeyStore key entry containing a private key/s and |
413
|
|
|
|
|
|
|
confess "Invalid keystore password: $keyStorePwd ". |
414
|
|
|
|
|
|
|
"for keystore:\n$keyStoreFile\n". |
415
|
|
|
|
|
|
|
"Specify the correct password via the keyStorePwd() method\n"; |
416
|
|
|
|
|
|
|
|
417
|
0
|
0
|
|
|
|
0
|
$s =~ /jar signed/s or confess "Unable to sign $apkFile\n"; |
418
|
|
|
|
|
|
|
|
419
|
0
|
0
|
|
|
|
0
|
if ($android->verifyApk) # Optional verify |
420
|
0
|
|
|
|
|
0
|
{my $v = zzz("jarsigner -verify $apkFile"); |
421
|
0
|
0
|
|
|
|
0
|
$v =~ /jar verified/s or confess "Unable to verify $apkFile\n"; |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub make |
426
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; |
427
|
0
|
|
|
|
|
0
|
$android->confirmRequiredUtilitiesAreInPosition; |
428
|
0
|
|
|
|
|
0
|
my $getAppName = $android->getAppName; |
429
|
0
|
|
|
|
|
0
|
my $buildTools = $android->getBuildTools; |
430
|
0
|
|
|
|
|
0
|
my $buildArea = $android->buildArea; |
431
|
0
|
|
|
|
|
0
|
my $adb = $android->getAdb; |
432
|
0
|
|
|
|
|
0
|
my $androidJar = $android->getAndroidJar; |
433
|
0
|
|
|
|
|
0
|
my $aapt = filePath($buildTools, qw(aapt)); |
434
|
0
|
|
|
|
|
0
|
my $dx = filePath($buildTools, qw(dx)); |
435
|
0
|
|
|
|
|
0
|
my $zipAlign = filePath($buildTools, qw(zipalign)); |
436
|
0
|
|
|
|
|
0
|
my $bin = $android->getBinFolder; |
437
|
0
|
|
|
|
|
0
|
my $gen = $android->getGenFolder; |
438
|
0
|
|
|
|
|
0
|
my $res = $android->getResFolder; |
439
|
0
|
|
|
|
|
0
|
my @src = @{$android->src}; |
|
0
|
|
|
|
|
0
|
|
440
|
0
|
|
|
|
|
0
|
my @libs = @{$android->libs}; |
|
0
|
|
|
|
|
0
|
|
441
|
0
|
|
|
|
|
0
|
my $manifest = $android->getManifestFile; |
442
|
0
|
|
|
|
|
0
|
my $binRes = filePath($bin, $res); |
443
|
0
|
|
|
|
|
0
|
my $classes = filePath($bin, qw(classes)); |
444
|
0
|
|
|
|
|
0
|
my $api = $bin."$getAppName.ap_"; |
445
|
0
|
|
|
|
|
0
|
my $apj = $bin."$getAppName-unaligned.apk"; |
446
|
0
|
|
|
|
|
0
|
my $apk = $bin."$getAppName.apk"; |
447
|
|
|
|
|
|
|
|
448
|
0
|
|
|
|
|
0
|
if (1) # Confirm required files are in position |
449
|
0
|
|
|
|
|
0
|
{for( |
450
|
|
|
|
|
|
|
[qq(buildArea), $buildArea ], |
451
|
|
|
|
|
|
|
[qq(androidJar), $androidJar], |
452
|
|
|
|
|
|
|
[qq(res), $res ], |
453
|
|
|
|
|
|
|
[qq(manifest), $manifest ], |
454
|
|
|
|
|
|
|
) |
455
|
0
|
|
|
|
|
0
|
{my ($name, $file) = @$_; |
456
|
0
|
0
|
|
|
|
0
|
-e $file or confess "Unable to find $name:\n$file\n"; |
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
0
|
|
|
|
|
0
|
for my $file(@{$android->src}) # Check source files |
|
0
|
|
|
|
|
0
|
|
461
|
0
|
0
|
|
|
|
0
|
{-e $file or confess "Unable to find source file:\n$file\n"; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
0
|
|
|
|
|
0
|
for my $file(@{$android->libs}) # Check library files |
|
0
|
|
|
|
|
0
|
|
465
|
0
|
0
|
|
|
|
0
|
{-e $file or confess "Unable to find library:\n$file\n"; |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
0
|
|
|
|
|
0
|
unlink $_ for $api, $apj, $apk; # Remove any existing apks |
469
|
|
|
|
|
|
|
|
470
|
0
|
|
|
|
|
0
|
if (1) # Generate R.java |
471
|
0
|
|
|
|
|
0
|
{makePath($gen); |
472
|
0
|
|
|
|
|
0
|
my $c = "$aapt package -f -m -0 apk -M $manifest -S $res -I $androidJar". |
473
|
|
|
|
|
|
|
" -J $gen --generate-dependencies"; |
474
|
0
|
|
|
|
|
0
|
zzz($c); |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
0
|
|
|
|
|
0
|
if (1) # Compile java |
478
|
0
|
|
|
|
|
0
|
{makePath(filePathDir($classes)); |
479
|
0
|
|
|
|
|
0
|
my $j = join ' ', grep {/\.java\Z/} @src, # Java sources files |
|
0
|
|
|
|
|
0
|
|
480
|
|
|
|
|
|
|
findFiles(filePathDir($gen)); |
481
|
|
|
|
|
|
|
|
482
|
0
|
|
|
|
|
0
|
my $J = join ':', $androidJar, @libs; # Jar files for javac |
483
|
|
|
|
|
|
|
|
484
|
0
|
|
|
|
|
0
|
my $c = "javac -g -Xlint:-options -source $javaTarget ". # Compile java source files |
485
|
|
|
|
|
|
|
" -target $javaTarget -cp $J -d $classes $j"; |
486
|
0
|
|
|
|
|
0
|
zzz($c); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
0
|
|
|
|
|
0
|
if (1) # Dx |
490
|
0
|
|
|
|
|
0
|
{my $j = join ' ', @libs; # Jar files to include in dex |
491
|
0
|
|
|
|
|
0
|
zzz("$dx --incremental --dex --force-jumbo ". |
492
|
|
|
|
|
|
|
" --output $classes.dex $classes $j"); |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
0
|
|
|
|
|
0
|
if (1) # Crunch |
496
|
0
|
|
|
|
|
0
|
{makePath($binRes); |
497
|
0
|
|
|
|
|
0
|
zzz("$aapt crunch -S $res -C $binRes"); |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
|
500
|
0
|
|
|
|
|
0
|
if (1) # Package |
501
|
0
|
|
|
|
|
0
|
{zzz |
502
|
|
|
|
|
|
|
("$aapt package --no-crunch -f -0 apk -M $manifest". |
503
|
|
|
|
|
|
|
" -S $binRes -S $res -I $androidJar". |
504
|
|
|
|
|
|
|
" -F $api". |
505
|
|
|
|
|
|
|
" --generate-dependencies"); |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
|
508
|
0
|
|
|
|
|
0
|
if (1) # Create apk and sign |
509
|
0
|
|
|
|
|
0
|
{zzz("mv $api $apj"); # Create apk |
510
|
0
|
|
|
|
|
0
|
zzz("cd $bin && zip -qv $apj classes.dex"); # Add dexed classes |
511
|
|
|
|
|
|
|
} |
512
|
|
|
|
|
|
|
|
513
|
0
|
0
|
|
|
|
0
|
if (my $assetsFiles = $android->assets) # Create asset files if necessary |
514
|
0
|
|
|
|
|
0
|
{my $assetsFolder = $android->getAssFolder; |
515
|
0
|
|
|
|
|
0
|
writeFiles($assetsFiles, $assetsFolder); |
516
|
0
|
|
|
|
|
0
|
zzz(qq(cd $assetsFolder && cd .. && zip -rv $apj assets)); # Add assets to apk |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
0
|
|
|
|
|
0
|
$android->signApkFile($apj); # Sign the apk file |
520
|
|
|
|
|
|
|
|
521
|
0
|
|
|
|
|
0
|
zzz("$zipAlign -f 4 $apj $apk"); # Zip align |
522
|
|
|
|
|
|
|
|
523
|
0
|
|
|
|
|
0
|
unlink $_ for $api, $apj; # Remove intermediate apks |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
sub cloneApk2($$) # Clone an apk file: copy the apk, replace the L, re-sign, zipalign, return the name of the newly created apk file. |
527
|
0
|
|
|
0
|
0
|
0
|
{my ($android, $oldApk) = @_; # Android, file name of apk to be cloned |
528
|
0
|
|
|
|
|
0
|
$android->confirmRequiredUtilitiesAreInPosition; |
529
|
|
|
|
|
|
|
|
530
|
0
|
0
|
|
|
|
0
|
confess "Old apk file name not supplied\n" unless $oldApk; |
531
|
0
|
0
|
|
|
|
0
|
confess "Old apk does not exist:\n$oldApk\n" unless -e $oldApk; |
532
|
|
|
|
|
|
|
|
533
|
0
|
|
|
|
|
0
|
my $buildTools = $android->getBuildTools; |
534
|
0
|
|
|
|
|
0
|
my $zipAlign = filePath($buildTools, qw(zipalign)); |
535
|
|
|
|
|
|
|
|
536
|
0
|
|
|
|
|
0
|
my $tempFolder = temporaryFolder; # Temporary folder to unzip into |
537
|
0
|
|
|
|
|
0
|
zzz(<<"END", 0, 0, "Unable to unzip"); # Unzip old apk |
538
|
|
|
|
|
|
|
unzip -o $oldApk -d $tempFolder -x "assets/*" "META-INF/*" |
539
|
|
|
|
|
|
|
END |
540
|
|
|
|
|
|
|
|
541
|
0
|
0
|
|
|
|
0
|
if (my $assetsFiles = $android->assets) # Create asset files if necessary |
542
|
0
|
|
|
|
|
0
|
{my $assetsFolder = fpd($tempFolder, q(assets)); |
543
|
0
|
|
|
|
|
0
|
writeFiles($assetsFiles, $assetsFolder); |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
|
546
|
0
|
|
|
|
|
0
|
my $tmpApk = fpe(temporaryFile, q(apk)); # Temporary Apk |
547
|
0
|
|
|
|
|
0
|
zzz(qq(cd $tempFolder && zip -rv $tmpApk *), 0, 0, "Unable to rezip"); # Recreate apk |
548
|
|
|
|
|
|
|
|
549
|
0
|
|
|
|
|
0
|
$android->signApkFile($tmpApk); # Sign |
550
|
|
|
|
|
|
|
|
551
|
0
|
|
|
|
|
0
|
my $newApk = fpe(temporaryFile, q(apk)); # New apk |
552
|
0
|
|
|
|
|
0
|
zzz("$zipAlign -f 4 $tmpApk $newApk", 0, 0, "Unable to zipalign"); # Zip align |
553
|
|
|
|
|
|
|
|
554
|
0
|
|
|
|
|
0
|
unlink $tmpApk; # Clean up |
555
|
0
|
|
|
|
|
0
|
clearFolder($tempFolder, 100); |
556
|
|
|
|
|
|
|
|
557
|
0
|
|
|
|
|
0
|
return $newApk; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
sub compile2($) #P Compile the app |
561
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; # Android build |
562
|
0
|
|
|
|
|
0
|
$android->create; |
563
|
0
|
|
|
|
|
0
|
$android->make; # Compile the app |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub install2($) #P Install an already L app on the selected L: |
567
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; # Android build |
568
|
0
|
|
|
|
|
0
|
my $apk = $android->apk; |
569
|
0
|
|
|
|
|
0
|
my $device = $android->getDevice; |
570
|
0
|
|
|
|
|
0
|
my $package = $android->getPackage; |
571
|
0
|
|
|
|
|
0
|
my $activity = $android->activityX; |
572
|
0
|
|
|
|
|
0
|
my $adb = $android->getAdb." $device "; |
573
|
|
|
|
|
|
|
|
574
|
0
|
|
|
|
|
0
|
zzz("$adb install -r $apk"); |
575
|
0
|
|
|
|
|
0
|
zzz("$adb shell am start $package/.Activity"); |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub lint2($) #P Lint all the source code java files for the app |
579
|
0
|
|
|
0
|
0
|
0
|
{my ($android) = @_; # Android build |
580
|
0
|
|
|
|
|
0
|
my $src = $android->getLintFile; |
581
|
0
|
|
|
|
|
0
|
my $androidJar = $android->getAndroidJar; |
582
|
0
|
|
0
|
|
|
0
|
my $area = $android->classes // 'Classes'; |
583
|
0
|
|
|
|
|
0
|
makePath($area); |
584
|
0
|
|
|
|
|
0
|
zzz("javac *.java -d $area -cp $androidJar:$area"); # Android, plus locally created classes |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
#1 Methods and attributes |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
sub new() #S Create a new build. |
590
|
0
|
|
|
0
|
1
|
0
|
{bless{action =>qq(run), |
591
|
|
|
|
|
|
|
activity =>qw(Activity), |
592
|
|
|
|
|
|
|
device =>qq(emulator-5554), |
593
|
|
|
|
|
|
|
home =>$home, |
594
|
|
|
|
|
|
|
icon =>'icon.png', |
595
|
|
|
|
|
|
|
log =>[], |
596
|
|
|
|
|
|
|
parameters =>'', |
597
|
|
|
|
|
|
|
permissions=>$permissions, |
598
|
|
|
|
|
|
|
version =>$version}; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
if (1) { # Parameters that can be set by the caller - see the pod at the end of this file for a complete description of what each parameter does |
602
|
|
|
|
|
|
|
genLValueScalarMethods(qw(activity)); # Activity name: default is B. The name of the activity to start on your android device: L is L/L |
603
|
|
|
|
|
|
|
genLValueScalarMethods(qw(assets)); # A hash containing your assets folder (if any). Each key is the file name in the assets folder, each corresponding value is the data for that file. The keys of this hash may contain B> to create sub folders. |
604
|
|
|
|
|
|
|
genLValueScalarMethods(qw(buildTools)); # Name of the folder containing the build tools to be used to build the app, see L |
605
|
|
|
|
|
|
|
genLValueScalarMethods(qw(buildFolder)); # Name of a folder in which to build the app, The default is B |
606
|
|
|
|
|
|
|
genLValueScalarMethods(qw(classes)); # A folder containing precompiled java classes and jar files that you wish to L against. |
607
|
|
|
|
|
|
|
genLValueScalarMethods(qw(debug)); # The app will be debuggable if this option is true. |
608
|
|
|
|
|
|
|
genLValueScalarMethods(qw(device)); # Device to run on, default is the only emulator or specify '-d', '-e', or '-s SERIAL' per L |
609
|
|
|
|
|
|
|
genLValueScalarMethods(qw(fastIcons)); # Create icons in parallel if true - the default is to create them serially which takes more elapsed time. |
610
|
|
|
|
|
|
|
genLValueScalarMethods(qw(icon)); # Jpg file containing a picture that will be converted and scaled by L to make an icon for the app, default is B in the current directory. |
611
|
|
|
|
|
|
|
genLValueScalarMethods(qw(keyAlias)); # Alias of the key in your key store file which will be used to sign this app. See L for how to generate a key. |
612
|
|
|
|
|
|
|
genLValueScalarMethods(qw(keyStoreFile)); # Name of your key store file. See L for how to generate a key. |
613
|
|
|
|
|
|
|
genLValueScalarMethods(qw(keyStorePwd)); # Password of your key store file. See L for how to generate a key. |
614
|
|
|
|
|
|
|
genLValueArrayMethods (qw(libs)); # A reference to an array of jar files to be copied into the app build to be used as libraries. |
615
|
|
|
|
|
|
|
genLValueScalarMethods(qw(lintFile)); # A file to be linted with the L action using the android L and the L specified. |
616
|
|
|
|
|
|
|
genLValueArrayMethods (qw(log)); # Output: a reference to an array of messages showing all the non fatal errors produced by this running this build. To catch fatal error enclose L with L |
617
|
|
|
|
|
|
|
genLValueScalarMethods(qw(package)); # The package name used in the manifest file to identify the app. The java file containing the L for this app should use this package name on its B statement. |
618
|
|
|
|
|
|
|
genLValueScalarMethods(qw(parameters)); # Optional parameter string to be placed in folder: B as a string accessible via: B from within the app. Alternatively, if this is a reference to a hash, strings are created for each hash key=value |
619
|
|
|
|
|
|
|
genLValueArrayMethods (qw(permissions)); # A reference to an array of permissions, a standard useful set is applied by default if none are specified. |
620
|
|
|
|
|
|
|
genLValueScalarMethods(qw(platform)); # Folder containing B. For example B<~/Android/sdk/platforms/25.0.2> |
621
|
|
|
|
|
|
|
genLValueScalarMethods(qw(platformTools)); # Folder containing L |
622
|
|
|
|
|
|
|
genLValueArrayMethods (qw(sdkLevels)); # [minSdkVersion, targetSdkVersion], default is [15, 25] |
623
|
|
|
|
|
|
|
genLValueArrayMethods (qw(src)); # A reference to an array of java source files to be compiled to create this app. |
624
|
|
|
|
|
|
|
genLValueScalarMethods(qw(title)); # Title of app, the default is the L name of the app. |
625
|
|
|
|
|
|
|
genLValueScalarMethods(qw(titles)); # A hash of translated titles: {ISO::639 2 digit language code=>title in that language}* for this app. |
626
|
|
|
|
|
|
|
genLValueScalarMethods(qw(verifyApk)); # Verify the signed apk if this is true. |
627
|
|
|
|
|
|
|
genLValueScalarMethods(qw(version)); # The version number of the app. Default is today's date, formatted as B |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
sub compile($) # Compile the app. |
631
|
0
|
|
|
0
|
1
|
0
|
{my ($android) = @_; # Android build |
632
|
0
|
|
|
|
|
0
|
eval {&compile2(@_)}; |
|
0
|
|
|
|
|
0
|
|
633
|
0
|
0
|
|
|
|
0
|
if ($@) |
634
|
0
|
|
|
|
|
0
|
{$android->logMessage($@); |
635
|
0
|
|
|
|
|
0
|
return $@; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
undef # No errors encountered |
638
|
0
|
|
|
|
|
0
|
} |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
sub cloneApk($$) # Clone an apk file: copy the existing apk, replace the L, re-sign, zipalign, return the name of the newly created apk file. |
641
|
0
|
|
|
0
|
1
|
0
|
{my ($android, $oldApk) = @_; # Android build, the file name of the apk to be cloned |
642
|
0
|
|
|
|
|
0
|
&cloneApk2(@_); |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
sub lint($) # Lint all the Java source code files for the app. |
646
|
0
|
|
|
0
|
1
|
0
|
{my ($android) = @_; # Android build |
647
|
0
|
|
|
|
|
0
|
eval {&lint2(@_)}; |
|
0
|
|
|
|
|
0
|
|
648
|
0
|
0
|
|
|
|
0
|
if ($@) |
649
|
0
|
|
|
|
|
0
|
{$android->logMessage($@); |
650
|
0
|
|
|
|
|
0
|
return $@; |
651
|
|
|
|
|
|
|
} |
652
|
|
|
|
|
|
|
undef # No errors encountered |
653
|
0
|
|
|
|
|
0
|
} |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
sub install($) # Install an already L app on to the selected L |
656
|
0
|
|
|
0
|
1
|
0
|
{my ($android) = @_; # Android build |
657
|
0
|
|
|
|
|
0
|
eval {&install2(@_)}; |
|
0
|
|
|
|
|
0
|
|
658
|
0
|
0
|
|
|
|
0
|
if ($@) |
659
|
0
|
|
|
|
|
0
|
{$android->logMessage($@); |
660
|
0
|
|
|
|
|
0
|
return $@; |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
undef # No errors encountered |
663
|
0
|
|
|
|
|
0
|
} |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
sub run($) # L the app, L and then run it on the selected L |
666
|
0
|
|
|
0
|
1
|
0
|
{my ($android) = @_; # Android build |
667
|
0
|
|
|
|
|
0
|
for(qw(compile install)) # Compile, install and run |
668
|
0
|
|
|
|
|
0
|
{my $r = $android->$_; |
669
|
0
|
0
|
|
|
|
0
|
return $r if $r; |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
undef # No errors encountered |
672
|
0
|
|
|
|
|
0
|
} |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
# podDocumentation |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
=encoding utf-8 |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=head1 Name |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
Android::Build - Lint, compile, install, run an Android app using the command line tools minus Ant and Gradle thus freeing development effort from the strictures imposed by Android Studio. |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=head1 Prerequisites |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sudo apt-get install imagemagick zip openjdk-8-jdk |
685
|
|
|
|
|
|
|
sudo cpan install Data::Table::Text Data::Dump Carp POSIX File::Copy; |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
You will need a version of the |
688
|
|
|
|
|
|
|
L |
689
|
|
|
|
|
|
|
as specified right at the end of the page below all the inappropriate |
690
|
|
|
|
|
|
|
advertising for Android Studio. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
Download: |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip |
695
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
then using the sdkmanager to get the version of the SDK that you want to use, |
697
|
|
|
|
|
|
|
for example: |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
sdkmanager --list --verbose |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
sdkmanager 'platforms;android-25' 'build-tools;25.0.3' emulator \ |
702
|
|
|
|
|
|
|
'system-images;android-25;google_apis;x86_64' |
703
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
=head1 Synopsis |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
use Android::Build; |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
my $a = &Android::Build::new(); # Create new builder |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
$a->buildTools = qq(~/Android/sdk/build-tools/25.0.2/); # Android SDK Build tools folder |
711
|
|
|
|
|
|
|
$a->icon = qq(~/images/Jets/EEL.jpg); # Image that will be scaled to make an icon using Imagemagick - the English Electric Lightening |
712
|
|
|
|
|
|
|
$a->keyAlias = qq(xxx); # Alias of key to be used to sign this app |
713
|
|
|
|
|
|
|
$a->keyStoreFile = qq(~/keystore/release-key.keystore); # Key store file |
714
|
|
|
|
|
|
|
$a->keyStorePwd = qq(xxx); # Password for key store file |
715
|
|
|
|
|
|
|
$a->package = qq(com.appaapps.genapp); # Package name containing the activity for this app |
716
|
|
|
|
|
|
|
$a->platform = qq(~/Android/sdk/platforms/android-25/); # Android SDK platform folder |
717
|
|
|
|
|
|
|
$a->platformTools = qq(~/Android/sdk/platform-tools/); # Android SDK platform tools folder |
718
|
|
|
|
|
|
|
$a->src = [q(~/AndroidBuild/SampleApp/src/Activity.java)]; # Source code for the app |
719
|
|
|
|
|
|
|
$a->title = qq(Generic App); # Title of the app as seen under the icon |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
$a->run; # Build, install and run the app on the only emulator |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
Modify the code above to reflect your local environment, then start an emulator |
724
|
|
|
|
|
|
|
and run the modified code to compile your app and load it into the emulator. |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=head2 Sample App |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
A sample app is included in folder: |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
./SampleApp |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
Modify the values in |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
./SampleApp/perl/makeWithPerl.pl |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
to reflect your local environment, then start an emulator and run: |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
perl ./SampleApp/perl/makeWithPerl.pl |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
to compile the sample app and load it into the running emulator. |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=head2 Signing key |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
If you do not already have a signing key, you can create one with the supplied |
745
|
|
|
|
|
|
|
script: |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
./SampleApp/perl/generateAKey.pl |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=head1 Description |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
Lint, compile, install, run an Android app using the command line tools minus Ant and Gradle thus freeing development effort from the strictures imposed by Android Studio. |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
The following sections describe the methods in each functional area of this |
754
|
|
|
|
|
|
|
module. For an alphabetic listing of all methods by name see L. |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
=head1 Methods and attributes |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
=head2 new() |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
Create a new build. |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
This is a static method and so should be invoked as: |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
Android::Build::new |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
=head2 activity :lvalue |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
Activity name: default is B. The name of the activity to start on your android device: L is L/L |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=head2 assets :lvalue |
776
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
A hash containing your assets folder (if any). Each key is the file name in the assets folder, each corresponding value is the data for that file. The keys of this hash may contain B> to create sub folders. |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=head2 buildTools :lvalue |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
Name of the folder containing the build tools to be used to build the app, see L |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
=head2 buildFolder :lvalue |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
Name of a folder in which to build the app, The default is B |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
=head2 classes :lvalue |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
A folder containing precompiled java classes and jar files that you wish to L against. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=head2 debug :lvalue |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
The app will be debuggable if this option is true. |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
=head2 device :lvalue |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
Device to run on, default is the only emulator or specify '-d', '-e', or '-s SERIAL' per L |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
=head2 fastIcons :lvalue |
806
|
|
|
|
|
|
|
|
807
|
|
|
|
|
|
|
Create icons in parallel if true - the default is to create them serially which takes more elapsed time. |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=head2 icon :lvalue |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
Jpg file containing a picture that will be converted and scaled by L to make an icon for the app, default is B in the current directory. |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
=head2 keyAlias :lvalue |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
Alias of the key in your key store file which will be used to sign this app. See L for how to generate a key. |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=head2 keyStoreFile :lvalue |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
Name of your key store file. See L for how to generate a key. |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
=head2 keyStorePwd :lvalue |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
Password of your key store file. See L for how to generate a key. |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=head2 libs :lvalue |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
A reference to an array of jar files to be copied into the app build to be used as libraries. |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
=head2 lintFile :lvalue |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
A file to be linted with the L action using the android L and the L specified. |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
=head2 log :lvalue |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
Output: a reference to an array of messages showing all the non fatal errors produced by this running this build. To catch fatal error enclose L with L |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=head2 package :lvalue |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
The package name used in the manifest file to identify the app. The java file containing the L for this app should use this package name on its B statement. |
848
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=head2 parameters :lvalue |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
Optional parameter string to be placed in folder: B as a string accessible via: B from within the app. Alternatively, if this is a reference to a hash, strings are created for each hash key=value |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=head2 permissions :lvalue |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
A reference to an array of permissions, a standard useful set is applied by default if none are specified. |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
=head2 platform :lvalue |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
Folder containing B. For example B<~/Android/sdk/platforms/25.0.2> |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=head2 platformTools :lvalue |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
Folder containing L |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
=head2 sdkLevels :lvalue |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
[minSdkVersion, targetSdkVersion], default is [15, 25] |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
=head2 src :lvalue |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
A reference to an array of java source files to be compiled to create this app. |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=head2 title :lvalue |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
Title of app, the default is the L name of the app. |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
=head2 titles :lvalue |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
A hash of translated titles: {ISO::639 2 digit language code=>title in that language}* for this app. |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
=head2 verifyApk :lvalue |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
Verify the signed apk if this is true. |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
=head2 version :lvalue |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
The version number of the app. Default is today's date, formatted as B |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=head2 compile($) |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
Compile the app. |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
Parameter Description |
905
|
|
|
|
|
|
|
1 $android Android build |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
=head2 cloneApk($$) |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
Clone an apk file: copy the existing apk, replace the L, re-sign, zipalign, return the name of the newly created apk file. |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
Parameter Description |
912
|
|
|
|
|
|
|
1 $android Android build |
913
|
|
|
|
|
|
|
2 $oldApk The file name of the apk to be cloned |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=head2 lint($) |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
Lint all the Java source code files for the app. |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
Parameter Description |
920
|
|
|
|
|
|
|
1 $android Android build |
921
|
|
|
|
|
|
|
|
922
|
|
|
|
|
|
|
=head2 install($) |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
Install an already L app on to the selected L |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
Parameter Description |
927
|
|
|
|
|
|
|
1 $android Android build |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
=head2 run($) |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
L the app, L and then run it on the selected L |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
Parameter Description |
934
|
|
|
|
|
|
|
1 $android Android build |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
=head1 Index |
938
|
|
|
|
|
|
|
|
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
1 L |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
2 L |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
3 L |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
4 L |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
5 L |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
6 L |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
7 L |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
8 L |
955
|
|
|
|
|
|
|
|
956
|
|
|
|
|
|
|
9 L |
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
10 L |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
11 L |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
12 L |
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
13 L |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
14 L |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
15 L |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
16 L |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
17 L |
973
|
|
|
|
|
|
|
|
974
|
|
|
|
|
|
|
18 L |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
19 L |
977
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
20 L |
979
|
|
|
|
|
|
|
|
980
|
|
|
|
|
|
|
21 L |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
22 L |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
23 L |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
24 L |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
25 L |
989
|
|
|
|
|
|
|
|
990
|
|
|
|
|
|
|
26 L |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
27 L |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
28 L |
995
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
29 L |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
30 L |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
31 L |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
32 L |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
=head1 Installation |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
This module is written in 100% Pure Perl and, thus, it is easy to read, |
1007
|
|
|
|
|
|
|
comprehend, use, modify and install via B: |
1008
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
sudo cpan install Android::Build |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
=head1 Author |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
L |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
L |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
=head1 Copyright |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
Copyright (c) 2016-2018 Philip R Brenan. |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
This module is free software. It may be used, redistributed and/or modified |
1022
|
|
|
|
|
|
|
under the same terms as Perl itself. |
1023
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
=cut |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
# Tests and documentation |
1029
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
sub test |
1031
|
1
|
|
|
1
|
0
|
10
|
{my $p = __PACKAGE__; |
1032
|
1
|
|
|
|
|
7
|
binmode($_, ":utf8") for *STDOUT, *STDERR; |
1033
|
1
|
50
|
|
|
|
51
|
return if eval "eof(${p}::DATA)"; |
1034
|
1
|
|
|
|
|
41
|
my $s = eval "join('', <${p}::DATA>)"; |
1035
|
1
|
50
|
|
|
|
6
|
$@ and die $@; |
1036
|
1
|
|
|
1
|
|
573
|
eval $s; |
|
1
|
|
|
|
|
71312
|
|
|
1
|
|
|
|
|
12
|
|
|
1
|
|
|
|
|
47
|
|
1037
|
1
|
50
|
|
|
|
464
|
$@ and die $@; |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
test unless caller; |
1041
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
1; |
1043
|
|
|
|
|
|
|
# podDocumentation |
1044
|
|
|
|
|
|
|
__DATA__ |