Compare commits
589 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdcb4ddcd9 | ||
|
|
8e53e2aa56 | ||
|
|
5276f56cc4 | ||
|
|
9df6793f34 | ||
|
|
d90e191837 | ||
|
|
d5d448888d | ||
|
|
8d8600b442 | ||
|
|
0bf6455153 | ||
|
|
80b7a7f6ac | ||
|
|
6b789c57e8 | ||
|
|
b8a344fc17 | ||
|
|
1b6319e9cf | ||
|
|
2cc81253ff | ||
|
|
499c694146 | ||
|
|
bc32cca281 | ||
|
|
dead4b4ab6 | ||
|
|
305cb2cdd5 | ||
|
|
287bfcbaa3 | ||
|
|
2a61b751ab | ||
|
|
a830145f36 | ||
|
|
08dc1dd9b9 | ||
|
|
e86b211cd1 | ||
|
|
6c5febc189 | ||
|
|
4d0d60c294 | ||
|
|
16a88ec631 | ||
|
|
1ce2b6b315 | ||
|
|
43a6805902 | ||
|
|
566262c923 | ||
|
|
e4bff281b2 | ||
|
|
9a675912f8 | ||
|
|
f86044e6ce | ||
|
|
43fdaa91a3 | ||
|
|
80f46aefcd | ||
|
|
71f63d7b33 | ||
|
|
8d47cd73c0 | ||
|
|
6d451bc6f9 | ||
|
|
6402e7b755 | ||
|
|
c93f93f637 | ||
|
|
6e51943d15 | ||
|
|
c81cd871f8 | ||
|
|
8ab1dbc373 | ||
|
|
fb26050fab | ||
|
|
c56cd4d5a8 | ||
|
|
de105e8651 | ||
|
|
8e98de6e7c | ||
|
|
6372ca3fac | ||
|
|
3712619f5c | ||
|
|
dee1e77d0b | ||
|
|
d01ed80a61 | ||
|
|
92268b2e76 | ||
|
|
0924654a47 | ||
|
|
8ef742e79d | ||
|
|
64ef13c6e0 | ||
|
|
66ad2c948e | ||
|
|
60e022fedd | ||
|
|
09256b766f | ||
|
|
a951793431 | ||
|
|
f4b8f44d4a | ||
|
|
08ab7d4b59 | ||
|
|
5889001465 | ||
|
|
91d2716122 | ||
|
|
0e6ad28e56 | ||
|
|
fd57909730 | ||
|
|
e3cc75caff | ||
|
|
e26142f43b | ||
|
|
d0f972e128 | ||
|
|
e42fedc820 | ||
|
|
f0c9814c04 | ||
|
|
529278190c | ||
|
|
fcaab36484 | ||
|
|
5dfa995a4b | ||
|
|
c35a990c09 | ||
|
|
11f40bd2cc | ||
|
|
7e8b47d012 | ||
|
|
902aa32dda | ||
|
|
42c0cba7f7 | ||
|
|
f2b84d8d83 | ||
|
|
1b11206174 | ||
|
|
c93e3e9f6f | ||
|
|
9808a0d4d3 | ||
|
|
bd1697dbd2 | ||
|
|
b3b8690bbd | ||
|
|
ad742ec93c | ||
|
|
1de7c38134 | ||
|
|
997943a194 | ||
|
|
47c6048d53 | ||
|
|
a64d459c8e | ||
|
|
a5ad440f17 | ||
|
|
acad24d62a | ||
|
|
989b4cc913 | ||
|
|
38c6627999 | ||
|
|
4b9e18c6b8 | ||
|
|
906f8cc002 | ||
|
|
01ab11644c | ||
|
|
4cf3dcfaae | ||
|
|
b177f84825 | ||
|
|
485e6e0e4d | ||
|
|
516c3411aa | ||
|
|
908354e7fa | ||
|
|
9531dbbc7b | ||
|
|
d10dd1c0b4 | ||
|
|
6533474070 | ||
|
|
576edb53bb | ||
|
|
20e390af85 | ||
|
|
931251a5a8 | ||
|
|
9697550488 | ||
|
|
3dac984613 | ||
|
|
867da56e2e | ||
|
|
719acd3ab1 | ||
|
|
549cae0a06 | ||
|
|
bb45f4f3ba | ||
|
|
b4de6f55c4 | ||
|
|
e99c145d55 | ||
|
|
9cb9f32fbb | ||
|
|
a6f30b6977 | ||
|
|
73692e60d8 | ||
|
|
4abe3d3a49 | ||
|
|
54df4a116c | ||
|
|
715ce2f9ac | ||
|
|
7a98708eef | ||
|
|
d9c08f12a7 | ||
|
|
e41fbc5708 | ||
|
|
634f92efb6 | ||
|
|
98a7b759e8 | ||
|
|
3976685e4e | ||
|
|
f7688bc64e | ||
|
|
18e28294d6 | ||
|
|
39bd0d6463 | ||
|
|
c1819cc027 | ||
|
|
f228d90ca2 | ||
|
|
5f44af3757 | ||
|
|
8a4ae311ce | ||
|
|
ef2434188e | ||
|
|
a014228f7c | ||
|
|
2163c8ae05 | ||
|
|
47bb5184a0 | ||
|
|
f1f1ac3cbd | ||
|
|
f1396c7aad | ||
|
|
21ae48eada | ||
|
|
53e1c1b65c | ||
|
|
858611f95e | ||
|
|
5c334b6c10 | ||
|
|
cf5a02ea0f | ||
|
|
a67bc75b93 | ||
|
|
895ab0c970 | ||
|
|
21f84732c8 | ||
|
|
7eed65e9b4 | ||
|
|
cf5915ad01 | ||
|
|
a4caa87d29 | ||
|
|
bbb3913a36 | ||
|
|
92f07d0418 | ||
|
|
578a642209 | ||
|
|
9d03e4ec0a | ||
|
|
576ad183e7 | ||
|
|
e31596f60e | ||
|
|
ef493b4c0f | ||
|
|
c15312ee70 | ||
|
|
3caefcae49 | ||
|
|
c2f6631f91 | ||
|
|
fb1dfb27df | ||
|
|
898a6a8d8d | ||
|
|
7da53741cd | ||
|
|
47aabc631d | ||
|
|
fb75ac371d | ||
|
|
98c866704a | ||
|
|
e58453d3e6 | ||
|
|
03c62e1ce2 | ||
|
|
2c10545cd8 | ||
|
|
ff2ec7eed1 | ||
|
|
8dfddef6f9 | ||
|
|
73edf4de7b | ||
|
|
cfa58a5661 | ||
|
|
905bff3d59 | ||
|
|
eadcd33e84 | ||
|
|
76fe89dd1f | ||
|
|
3f487c5c28 | ||
|
|
c35a46ec45 | ||
|
|
e3eb933182 | ||
|
|
dacb0e5c72 | ||
|
|
23b24491c3 | ||
|
|
7ab0cf123d | ||
|
|
8f2a4c7231 | ||
|
|
ce53154555 | ||
|
|
c0188ab95a | ||
|
|
ebbd91f87d | ||
|
|
4b0725dfc2 | ||
|
|
350d35fb24 | ||
|
|
8ee3a73dd1 | ||
|
|
b6e4598e7c | ||
|
|
bd2ad99402 | ||
|
|
d26c1199ab | ||
|
|
1ea7c1366a | ||
|
|
b2263fe35e | ||
|
|
bd07907a4c | ||
|
|
ca8931c8af | ||
|
|
46a036ef26 | ||
|
|
e9750b5cab | ||
|
|
0bf0e48698 | ||
|
|
4f1aa97ea9 | ||
|
|
f90f004dea | ||
|
|
559b0833b4 | ||
|
|
31bad290d5 | ||
|
|
2e88f7ead2 | ||
|
|
1a8154c90e | ||
|
|
8fb49ec7ec | ||
|
|
393dad6349 | ||
|
|
2c3db19310 | ||
|
|
472ab626d6 | ||
|
|
2106e2e081 | ||
|
|
d3c80ea5d5 | ||
|
|
6dad25668c | ||
|
|
d862d42e76 | ||
|
|
828b5f053a | ||
|
|
032d1fa9cb | ||
|
|
83ad646734 | ||
|
|
88c25a6a6a | ||
|
|
a254cfc841 | ||
|
|
0d4b9f4ba6 | ||
|
|
bf29fe0e10 | ||
|
|
3df8f8b120 | ||
|
|
38d0e684f1 | ||
|
|
83e9aefff5 | ||
|
|
02ee925103 | ||
|
|
fc69da7a42 | ||
|
|
5dc179ebef | ||
|
|
59e3b907e9 | ||
|
|
fe7629e8fc | ||
|
|
76180d3ea1 | ||
|
|
5c4f8ca246 | ||
|
|
b8530a6b70 | ||
|
|
83686542b2 | ||
|
|
b1551cad98 | ||
|
|
5af6b016c1 | ||
|
|
6abd6d6b47 | ||
|
|
e456a325f9 | ||
|
|
eddad666ff | ||
|
|
ca9a25e860 | ||
|
|
8d9a1b82dd | ||
|
|
f1b57dd9b4 | ||
|
|
d2a0323ae4 | ||
|
|
3a339ba37f | ||
|
|
e9603b0738 | ||
|
|
3d83fd784b | ||
|
|
38a5f891a4 | ||
|
|
8563c8beef | ||
|
|
f91102ee07 | ||
|
|
cc08e9d84a | ||
|
|
075d38117a | ||
|
|
6cc4896690 | ||
|
|
673a6773b2 | ||
|
|
3a6923988d | ||
|
|
0d1692681f | ||
|
|
fcf705e007 | ||
|
|
5c93c214b9 | ||
|
|
7cbb8401a2 | ||
|
|
b3c262cd47 | ||
|
|
45a7b90c6c | ||
|
|
e1c3b4fd94 | ||
|
|
109112ae75 | ||
|
|
a24ba41eda | ||
|
|
be3f8ef80d | ||
|
|
d50dedb5d0 | ||
|
|
74e9e213d6 | ||
|
|
d8d92cae61 | ||
|
|
99d5b4d982 | ||
|
|
52fd32c837 | ||
|
|
061f08bc03 | ||
|
|
735bc98243 | ||
|
|
e53a65ef07 | ||
|
|
3642ffb57a | ||
|
|
3dcc319cd2 | ||
|
|
8484f7b906 | ||
|
|
59018ab632 | ||
|
|
c6cfeb15f4 | ||
|
|
adc7dab377 | ||
|
|
a9e01f4309 | ||
|
|
0269e532df | ||
|
|
4863320e45 | ||
|
|
3ad1ed7dbc | ||
|
|
ae823e6c4e | ||
|
|
9056e5a2dc | ||
|
|
2b20802dbb | ||
|
|
a972d1ef62 | ||
|
|
30c3713dca | ||
|
|
6f58d4c474 | ||
|
|
9d9bac397b | ||
|
|
d8f10c33dc | ||
|
|
5d57eff612 | ||
|
|
43956c1bc8 | ||
|
|
2930900963 | ||
|
|
6404780186 | ||
|
|
5d99e50c4a | ||
|
|
aaeb630eb1 | ||
|
|
3acba59494 | ||
|
|
3760616639 | ||
|
|
026dce563b | ||
|
|
b77febc7a7 | ||
|
|
83601dca2f | ||
|
|
05aeaf1bd2 | ||
|
|
b138867b78 | ||
|
|
a1195cefd5 | ||
|
|
b83c3b3684 | ||
|
|
5502ddaf0d | ||
|
|
f54336eb61 | ||
|
|
18d6884522 | ||
|
|
8ba0109e55 | ||
|
|
f50ca85a95 | ||
|
|
39e6765e64 | ||
|
|
47e20da631 | ||
|
|
bb7b47b063 | ||
|
|
a2618dcde5 | ||
|
|
9fdb126715 | ||
|
|
b2a81c09ec | ||
|
|
04fa5d3feb | ||
|
|
d73108cc13 | ||
|
|
3ba00f91bb | ||
|
|
cbee8580d0 | ||
|
|
6ccd6b009b | ||
|
|
6cf6e20d2e | ||
|
|
93efe71080 | ||
|
|
697c8f129c | ||
|
|
c12f7fef76 | ||
|
|
cde238de94 | ||
|
|
ec83d481fa | ||
|
|
1f12fdbeea | ||
|
|
8743e88550 | ||
|
|
f3c238db1c | ||
|
|
2534a3c767 | ||
|
|
b1f527e682 | ||
|
|
6fa16b615b | ||
|
|
e5c90badba | ||
|
|
be6c6ba976 | ||
|
|
7f3be98199 | ||
|
|
0e498db735 | ||
|
|
7203b740fd | ||
|
|
97aab900da | ||
|
|
dddb2837dd | ||
|
|
7d282426c4 | ||
|
|
1637937664 | ||
|
|
03144eb160 | ||
|
|
53210710ba | ||
|
|
0d98e09fb1 | ||
|
|
e7a972df77 | ||
|
|
cb2f396e33 | ||
|
|
2940d05209 | ||
|
|
2377aa7ac2 | ||
|
|
f18086f83a | ||
|
|
dce3b7ed6c | ||
|
|
5bda4df7fa | ||
|
|
1a8e36ccd3 | ||
|
|
14816c7c81 | ||
|
|
8fdb16c555 | ||
|
|
940439866e | ||
|
|
e91b19d006 | ||
|
|
46905ebe99 | ||
|
|
d364f46baa | ||
|
|
2b53c98cf5 | ||
|
|
36d07d7a15 | ||
|
|
458e479681 | ||
|
|
5fe95570d2 | ||
|
|
c3ce2f8a07 | ||
|
|
d0dab4bb09 | ||
|
|
893356abcd | ||
|
|
00c879e27d | ||
|
|
b65af106ff | ||
|
|
fdd7eb3446 | ||
|
|
22645d9158 | ||
|
|
76dd8613ca | ||
|
|
99f15c507d | ||
|
|
849b887e20 | ||
|
|
245d9a1e46 | ||
|
|
5917d4ef0b | ||
|
|
90053eb9df | ||
|
|
a7304b9a19 | ||
|
|
540929c6a0 | ||
|
|
e456175a81 | ||
|
|
3ff32092a3 | ||
|
|
55d7cf3865 | ||
|
|
ac4ac935f6 | ||
|
|
d83d49d83b | ||
|
|
e36158a0da | ||
|
|
b20028c42b | ||
|
|
5cc14b8031 | ||
|
|
1cda7a9de0 | ||
|
|
49b76f5c71 | ||
|
|
c0474e811d | ||
|
|
40c9709445 | ||
|
|
b67e9905bc | ||
|
|
c74192d578 | ||
|
|
33feb00e8f | ||
|
|
8f16df4c90 | ||
|
|
fb6cb51e64 | ||
|
|
28ebbb8f02 | ||
|
|
bd4ddcdedd | ||
|
|
e621edfba7 | ||
|
|
304a899114 | ||
|
|
8391af2e8f | ||
|
|
69ab6a0e0d | ||
|
|
a216f0db75 | ||
|
|
69260fb96a | ||
|
|
db87e0ae6a | ||
|
|
8ead919fae | ||
|
|
b73c04f3c8 | ||
|
|
f790aeb8f6 | ||
|
|
7b17abc555 | ||
|
|
ffadf5dd51 | ||
|
|
23d8d99925 | ||
|
|
d88df59c32 | ||
|
|
17906735df | ||
|
|
3a6e898b12 | ||
|
|
0cc3df3747 | ||
|
|
2bc842a2b3 | ||
|
|
1c6f640026 | ||
|
|
6daad829cc | ||
|
|
7d926822ed | ||
|
|
d4dcbb13fc | ||
|
|
f396712f59 | ||
|
|
d97250f968 | ||
|
|
e7e8e95242 | ||
|
|
d518a655a8 | ||
|
|
b6a5844027 | ||
|
|
9d9abea157 | ||
|
|
ee1165ea33 | ||
|
|
2704ee54cf | ||
|
|
ad01d28351 | ||
|
|
a215c1cf30 | ||
|
|
cadea2f6c3 | ||
|
|
e13e15d3e9 | ||
|
|
6ef2f67ae8 | ||
|
|
765c4ee9f6 | ||
|
|
a4103d8dc8 | ||
|
|
8e0f021cad | ||
|
|
002ab85f76 | ||
|
|
4a0f69a3f0 | ||
|
|
8a2e96d995 | ||
|
|
13dbd2f5d4 | ||
|
|
6d084428a8 | ||
|
|
312efc3878 | ||
|
|
301faebfcc | ||
|
|
a8af103ff4 | ||
|
|
0df50c3f33 | ||
|
|
08c80f77a9 | ||
|
|
ea8d65d7fc | ||
|
|
e3198b0f6b | ||
|
|
19220de388 | ||
|
|
52878d6c5b | ||
|
|
abfece70c1 | ||
|
|
33da6e9eb8 | ||
|
|
4919a2d2a8 | ||
|
|
2f2e8a560d | ||
|
|
884818934f | ||
|
|
1d8e6f53a1 | ||
|
|
3554267adf | ||
|
|
f7687a2567 | ||
|
|
ebd4a02d2c | ||
|
|
4a354bba86 | ||
|
|
d40c22441f | ||
|
|
6395eda0c8 | ||
|
|
eb6ada8091 | ||
|
|
a7ef686a27 | ||
|
|
e2af492a63 | ||
|
|
84de9ee0da | ||
|
|
e182e669f0 | ||
|
|
a9a28d3e77 | ||
|
|
6a77acdbf2 | ||
|
|
028725468a | ||
|
|
9dbffa6ee8 | ||
|
|
2d3f3cae13 | ||
|
|
fb9f0f3ee8 | ||
|
|
ab276504fd | ||
|
|
3cc4e5b440 | ||
|
|
b2664bc469 | ||
|
|
7d5afdebe1 | ||
|
|
3bbc7fb328 | ||
|
|
ac6ad2dea1 | ||
|
|
0b116f1b5c | ||
|
|
acdb934ef9 | ||
|
|
5591a1a4e8 | ||
|
|
124574bb3a | ||
|
|
a2cfaab7f4 | ||
|
|
6355425a6f | ||
|
|
2d96995801 | ||
|
|
3fc4daa447 | ||
|
|
c7ff24b983 | ||
|
|
3f674faf30 | ||
|
|
b9ad1b6b26 | ||
|
|
676f0ddc2e | ||
|
|
6c60dc5dc8 | ||
|
|
1af5ade39a | ||
|
|
ad40d33400 | ||
|
|
5017e2302b | ||
|
|
3bfeda4a3b | ||
|
|
348b1b4dda | ||
|
|
0fd7e7f040 | ||
|
|
cfa0fa7243 | ||
|
|
6a63d9df0a | ||
|
|
36646eb527 | ||
|
|
48940bf4c0 | ||
|
|
5346a386f7 | ||
|
|
b3201ad073 | ||
|
|
35b0ba6d07 | ||
|
|
3c087c6716 | ||
|
|
854946e313 | ||
|
|
d6a1d7a913 | ||
|
|
700b425774 | ||
|
|
e31634b0fb | ||
|
|
1f06176149 | ||
|
|
e628026cf0 | ||
|
|
3a512c300e | ||
|
|
7f9e7c73ab | ||
|
|
3e7be6cc0f | ||
|
|
81dfe96355 | ||
|
|
efa23bd39b | ||
|
|
6aa4b1b301 | ||
|
|
304cbef5ed | ||
|
|
263968e019 | ||
|
|
c05521e55a | ||
|
|
2f3c71feaa | ||
|
|
0b710a86a9 | ||
|
|
2e37d2c253 | ||
|
|
d7c1dc5517 | ||
|
|
77b9fd62a7 | ||
|
|
f838db4e18 | ||
|
|
b2d7124424 | ||
|
|
2532c4a39e | ||
|
|
be19585c68 | ||
|
|
ca0cbad1c7 | ||
|
|
fb53452d37 | ||
|
|
b5246f3f09 | ||
|
|
4be413af79 | ||
|
|
5db2de95f5 | ||
|
|
6418add83d | ||
|
|
7d26c51da6 | ||
|
|
dcada79e06 | ||
|
|
37384c583d | ||
|
|
9544783b5e | ||
|
|
eaf9b319ca | ||
|
|
47abde2f8d | ||
|
|
30325e4f32 | ||
|
|
9cf38f8705 | ||
|
|
7de4803e21 | ||
|
|
9586a7ddcd | ||
|
|
a4f6c8bf7b | ||
|
|
f5ab6fc602 | ||
|
|
222fb1c0e7 | ||
|
|
511fe7c51e | ||
|
|
acb3cc80b7 | ||
|
|
23fd0982b0 | ||
|
|
d2a159d2dc | ||
|
|
dc0bfeb0cc | ||
|
|
deea0f7e4f | ||
|
|
3a33f4c0a6 | ||
|
|
312872adaf | ||
|
|
ba5e3a81e0 | ||
|
|
bf5c32d780 | ||
|
|
d3cb38de29 | ||
|
|
047ed52d60 | ||
|
|
bb96bb9a88 | ||
|
|
0ee672d2b0 | ||
|
|
2a8a6b3970 | ||
|
|
5dbc51da59 | ||
|
|
f1a6d938cd | ||
|
|
991a2b1873 | ||
|
|
172c01561b | ||
|
|
b2f061cbaf | ||
|
|
1f1b2977a4 | ||
|
|
4a0a7bc424 | ||
|
|
8fbb6d7c62 | ||
|
|
0299467831 | ||
|
|
2efe240617 | ||
|
|
ed543dc648 | ||
|
|
db6295c917 | ||
|
|
d54a42f0f0 | ||
|
|
74ae6651c8 | ||
|
|
f33f352501 | ||
|
|
39c17e71ef | ||
|
|
081637134f | ||
|
|
39165a8694 | ||
|
|
ad163ce244 | ||
|
|
a7176d39f0 | ||
|
|
f1c185b771 | ||
|
|
103e1e8d24 | ||
|
|
30198c4bdd | ||
|
|
841f02f43f | ||
|
|
a5e9861440 | ||
|
|
d69698c23f | ||
|
|
d763f14ffe | ||
|
|
bcf3f8611a | ||
|
|
2cefb42559 |
22
.asf.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
notifications:
|
||||
commits: commits@cordova.apache.org
|
||||
issues: issues@cordova.apache.org
|
||||
pullrequests_status: issues@cordova.apache.org
|
||||
pullrequests_comment: issues@cordova.apache.org
|
||||
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
bin/templates/project/assets/www/cordova.js
|
||||
test/android/app
|
||||
test/androidx/app
|
||||
27
.eslintrc.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
extends: '@cordova/eslint-config/node'
|
||||
|
||||
overrides:
|
||||
- files: [spec/**/*.js]
|
||||
extends: '@cordova/eslint-config/node-tests'
|
||||
rules:
|
||||
prefer-promise-reject-errors: off
|
||||
|
||||
- files: [cordova-js-src/**/*.js]
|
||||
extends: '@cordova/eslint-config/browser'
|
||||
42
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
Please have a look at the issue templates you get when you click "New issue" in the GitHub UI.
|
||||
We very much prefer issues created by using one of these templates.
|
||||
-->
|
||||
|
||||
### Issue Type
|
||||
<!-- Please check the boxes by putting an x in the [ ] like so: [x] -->
|
||||
|
||||
- [ ] Bug Report
|
||||
- [ ] Feature Request
|
||||
- [ ] Support Question
|
||||
|
||||
## Description
|
||||
|
||||
## Information
|
||||
<!-- Include all relevant information that might help understand and reproduce the problem -->
|
||||
|
||||
### Command or Code
|
||||
<!-- What command or code is needed to reproduce the problem? -->
|
||||
|
||||
### Environment, Platform, Device
|
||||
<!-- In what environment, on what platform or on which device are you experiencing the issue? -->
|
||||
|
||||
|
||||
|
||||
### Version information
|
||||
<!--
|
||||
What are relevant versions you are using?
|
||||
For example:
|
||||
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
|
||||
Other Frameworks: Ionic Framework and CLI version
|
||||
Operating System, Android Studio, Xcode etc.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## Checklist
|
||||
<!-- Please check the boxes by putting an `x` in the `[ ]` like so: `[x]` -->
|
||||
|
||||
- [ ] I searched for already existing GitHub issues about this
|
||||
- [ ] I updated all Cordova tooling to their most recent version
|
||||
- [ ] I included all the necessary information above
|
||||
50
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: 🐛 Bug Report
|
||||
about: If something isn't working as expected.
|
||||
|
||||
---
|
||||
|
||||
# Bug Report
|
||||
|
||||
## Problem
|
||||
|
||||
### What is expected to happen?
|
||||
|
||||
|
||||
|
||||
### What does actually happen?
|
||||
|
||||
|
||||
|
||||
## Information
|
||||
<!-- Include all relevant information that might help understand and reproduce the problem -->
|
||||
|
||||
|
||||
|
||||
### Command or Code
|
||||
<!-- What command or code is needed to reproduce the problem? -->
|
||||
|
||||
|
||||
|
||||
### Environment, Platform, Device
|
||||
<!-- In what environment, on what platform or on which device are you experiencing the issue? -->
|
||||
|
||||
|
||||
|
||||
### Version information
|
||||
<!--
|
||||
What are relevant versions you are using?
|
||||
For example:
|
||||
Cordova: Cordova CLI, Cordova Platforms, Cordova Plugins
|
||||
Other Frameworks: Ionic Framework and CLI version
|
||||
Operating System, Android Studio, Xcode etc.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## Checklist
|
||||
<!-- Please check the boxes by putting an x in the [ ] like so: [x] -->
|
||||
|
||||
- [ ] I searched for existing GitHub issues
|
||||
- [ ] I updated all Cordova tooling to most recent version
|
||||
- [ ] I included all the necessary information above
|
||||
29
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: 🚀 Feature Request
|
||||
about: A suggestion for a new functionality
|
||||
|
||||
---
|
||||
|
||||
# Feature Request
|
||||
|
||||
## Motivation Behind Feature
|
||||
<!-- Why should this feature be implemented? What problem does it solve? -->
|
||||
|
||||
|
||||
|
||||
## Feature Description
|
||||
<!--
|
||||
Describe your feature request in detail
|
||||
Please provide any code examples or screenshots of what this feature would look like
|
||||
Are there any drawbacks? Will this break anything for existing users?
|
||||
-->
|
||||
|
||||
|
||||
|
||||
## Alternatives or Workarounds
|
||||
<!--
|
||||
Describe alternatives or workarounds you are currently using
|
||||
Are there ways to do this with existing functionality?
|
||||
-->
|
||||
|
||||
|
||||
27
.github/ISSUE_TEMPLATE/SUPPORT_QUESTION.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: 💬 Support Question
|
||||
about: If you have a question, please check out our Slack or StackOverflow!
|
||||
|
||||
---
|
||||
|
||||
<!------------^ Click "Preview" for a nicer view! -->
|
||||
|
||||
Apache Cordova uses GitHub Issues as a feature request and bug tracker _only_.
|
||||
For usage and support questions, please check out the resources below. Thanks!
|
||||
|
||||
---
|
||||
|
||||
You can get answers to your usage and support questions about **Apache Cordova** on:
|
||||
|
||||
* Slack Community Chat: https://cordova.slack.com (you can sign-up at http://slack.cordova.io/)
|
||||
* StackOverflow: https://stackoverflow.com/questions/tagged/cordova using the tag `cordova`
|
||||
|
||||
---
|
||||
|
||||
If you are using a tool that uses Cordova internally, like e.g. Ionic, check their support channels:
|
||||
|
||||
* **Ionic Framework**
|
||||
* [Ionic Community Forum](https://forum.ionicframework.com/)
|
||||
* [Ionic Worldwide Slack](https://ionicworldwide.herokuapp.com/)
|
||||
* **PhoneGap**
|
||||
* [PhoneGap Developer Community](https://forums.adobe.com/community/phonegap)
|
||||
35
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<!--
|
||||
Please make sure the checklist boxes are all checked before submitting the PR. The checklist is intended as a quick reference, for complete details please see our Contributor Guidelines:
|
||||
|
||||
http://cordova.apache.org/contribute/contribute_guidelines.html
|
||||
|
||||
Thanks!
|
||||
-->
|
||||
|
||||
### Platforms affected
|
||||
|
||||
|
||||
|
||||
### Motivation and Context
|
||||
<!-- Why is this change required? What problem does it solve? -->
|
||||
<!-- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
|
||||
|
||||
### Description
|
||||
<!-- Describe your changes in detail -->
|
||||
|
||||
|
||||
|
||||
### Testing
|
||||
<!-- Please describe in detail how you tested your changes. -->
|
||||
|
||||
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] I've run the tests to see all new and existing tests pass
|
||||
- [ ] I added automated test coverage as appropriate for this change
|
||||
- [ ] Commit is prefixed with `(platform)` if this change only applies to one platform (e.g. `(android)`)
|
||||
- [ ] If this Pull Request resolves an issue, I linked to the issue in the text above (and used the correct [keyword to close issues using keywords](https://help.github.com/articles/closing-issues-using-keywords/))
|
||||
- [ ] I've updated the documentation if necessary
|
||||
61
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
name: Node CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: NodeJS ${{ matrix.node-version }} on ${{ matrix.os }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
|
||||
- name: Environment Information
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
gradle --version
|
||||
|
||||
- name: npm install and test
|
||||
run: |
|
||||
npm i
|
||||
npm t
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
55
.gitignore
vendored
@@ -1,11 +1,21 @@
|
||||
.DS_Store
|
||||
.gradle
|
||||
.metadata
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*.class
|
||||
*.jar
|
||||
default.properties
|
||||
gen
|
||||
assets/www/cordova.js
|
||||
local.properties
|
||||
proguard.cfg
|
||||
proguard.cfg
|
||||
proguard-project.txt
|
||||
example
|
||||
/coverage
|
||||
/framework/lib
|
||||
/framework/build
|
||||
/framework/bin
|
||||
@@ -15,35 +25,32 @@ proguard-project.txt
|
||||
/framework/libs
|
||||
/framework/javadoc-public
|
||||
/framework/javadoc-private
|
||||
/test/libs
|
||||
example
|
||||
/test/bin
|
||||
/test/.externalNativeBuild
|
||||
|
||||
/test/android/gradle
|
||||
/test/android/gradlew
|
||||
/test/android/gradlew.bat
|
||||
/test/androidx/gradle
|
||||
/test/androidx/gradlew
|
||||
/test/androidx/gradlew.bat
|
||||
|
||||
/test/assets/www/.tmp*
|
||||
/test/assets/www/cordova.js
|
||||
/test/gradle
|
||||
/test/gradlew
|
||||
/test/gradlew.bat
|
||||
/test/bin
|
||||
/test/build
|
||||
.gradle
|
||||
/test/captures
|
||||
/test/libs
|
||||
tmp/**
|
||||
.metadata
|
||||
tmp/**/*
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*.class
|
||||
*.jar
|
||||
!/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
|
||||
# IntelliJ IDEA files
|
||||
**/.idea/**/*
|
||||
*.iml
|
||||
.idea
|
||||
npm-debug.log
|
||||
/framework/build
|
||||
node_modules/jshint
|
||||
node_modules/promise-matchers
|
||||
node_modules/jasmine-node
|
||||
node_modules/rewire
|
||||
node_modules/istanbul
|
||||
/coverage
|
||||
node_modules/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
# Eclipse Buildship files
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
bin/node_modules/*
|
||||
bin/templates/project/*
|
||||
spec/fixtures/*
|
||||
10
.jshintrc
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"node": true
|
||||
, "bitwise": true
|
||||
, "undef": true
|
||||
, "trailing": true
|
||||
, "quotmark": true
|
||||
, "indent": 4
|
||||
, "unused": "vars"
|
||||
, "latedef": "nofunc"
|
||||
}
|
||||
5
.npmignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.*
|
||||
coverage
|
||||
test
|
||||
spec
|
||||
framework/build
|
||||
@@ -2,3 +2,7 @@
|
||||
bin
|
||||
gen
|
||||
proguard-project.txt
|
||||
spec
|
||||
framework/build
|
||||
ic_launcher.png
|
||||
build
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
GUESS_FIELDS = True
|
||||
OPEN_BROWSER = True
|
||||
TARGET_GROUPS = 'cordova'
|
||||
REVIEWBOARD_URL = 'http://reviews.apache.org'
|
||||
REVIEWBOARD_URL = 'https://reviews.apache.org'
|
||||
|
||||
|
||||
12
.travis.yml
@@ -1,12 +0,0 @@
|
||||
language: android
|
||||
sudo: false
|
||||
install:
|
||||
- npm install
|
||||
- npm install -g codecov
|
||||
- echo y | android update sdk -u --filter android-22,android-23
|
||||
script:
|
||||
- npm run jshint
|
||||
- npm run cover
|
||||
- npm run test-build
|
||||
after_script:
|
||||
- codecov
|
||||
@@ -25,13 +25,12 @@ Anyone can contribute to Cordova. And we need your contributions.
|
||||
|
||||
There are multiple ways to contribute: report bugs, improve the docs, and
|
||||
contribute code.
|
||||
|
||||
|
||||
For instructions on this, start with the
|
||||
[contribution overview](http://cordova.apache.org/contribute/).
|
||||
|
||||
The details are explained there, but the important items are:
|
||||
- Sign and submit an Apache ICLA (Contributor License Agreement).
|
||||
- Have a Jira issue open that corresponds to your contribution.
|
||||
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
|
||||
- Run the tests so your patch doesn't break existing functionality.
|
||||
|
||||
We look forward to your contributions!
|
||||
|
||||
101
LICENSE
@@ -187,7 +187,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2015 Apache Cordova
|
||||
Copyright 2015-2020 Apache Cordova
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -207,7 +207,7 @@
|
||||
bin/node_modules/q
|
||||
================================================================================
|
||||
|
||||
Copyright 2009–2012 Kristopher Michael Kowal. All rights reserved.
|
||||
Copyright 2009–2017 Kristopher Michael Kowal. All rights reserved.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
@@ -226,89 +226,42 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
bin/node_modules/shelljs
|
||||
================================================================================
|
||||
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
|
||||
All rights reserved.
|
||||
|
||||
You may use this project under the terms of the New BSD license as follows:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Artur Adib nor the
|
||||
names of the contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
================================================================================
|
||||
bin/node_modules/nopt
|
||||
================================================================================
|
||||
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
The ISC License
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
================================================================================
|
||||
bin/node_modules/which
|
||||
================================================================================
|
||||
|
||||
Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
|
||||
All rights reserved.
|
||||
The ISC License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
14
NOTICE
@@ -1,15 +1,5 @@
|
||||
Apache Cordova
|
||||
Copyright 2015 The Apache Software Foundation
|
||||
Copyright 2015-2020 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org)
|
||||
|
||||
=========================================================================
|
||||
== NOTICE file corresponding to the section 4 d of ==
|
||||
== the Apache License, Version 2.0, ==
|
||||
== in this case for the Android-specific code. ==
|
||||
=========================================================================
|
||||
|
||||
This product includes software developed as part of
|
||||
The Android Open Source Project (http://source.android.com).
|
||||
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
||||
|
||||
25
README.md
@@ -19,26 +19,21 @@
|
||||
#
|
||||
-->
|
||||
|
||||
[](https://ci.appveyor.com/project/Humbedooh/cordova-android)
|
||||
[](https://travis-ci.org/apache/cordova-android)
|
||||
[](https://codecov.io/github/apache/cordova-android?branch=master)
|
||||
|
||||
# Cordova Android
|
||||
|
||||
Cordova Android is an Android application library that allows for Cordova-based
|
||||
projects to be built for the Android Platform. Cordova based applications are,
|
||||
at the core, applications written with web technology: HTML, CSS and JavaScript.
|
||||
[](https://nodei.co/npm/cordova-android/)
|
||||
|
||||
[](https://github.com/apache/cordova-android/actions?query=branch%3Amaster)
|
||||
[](https://codecov.io/github/apache/cordova-android?branch=master)
|
||||
|
||||
Cordova Android is an Android application library that allows for Cordova-based projects to be built for the Android Platform. Cordova based applications are, at the core, applications written with web technology: HTML, CSS and JavaScript.
|
||||
|
||||
[Apache Cordova](https://cordova.apache.org) is a project of The Apache Software Foundation (ASF).
|
||||
|
||||
:warning: Report issues on the [Apache Cordova issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20%28Open%2C%20%22In%20Progress%22%2C%20Reopened%29%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Android%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC)
|
||||
|
||||
|
||||
## Requires
|
||||
|
||||
- Java JDK 1.6 or greater
|
||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||
|
||||
- Java JDK 1.8
|
||||
- Android SDK [http://developer.android.com](https://developer.android.com/)
|
||||
|
||||
## Cordova Android Developer Tools
|
||||
|
||||
@@ -62,3 +57,7 @@ These commands live in a generated Cordova Android project. Any interactions wit
|
||||
|
||||
1. Create a project
|
||||
2. Import it via "Non-Android Studio Project"
|
||||
|
||||
## Running the Native Tests
|
||||
|
||||
The `test/` directory in this project contains an Android test project that can be used to run different kinds of native tests. Check out the [README contained therein](test/README.md) for more details!
|
||||
|
||||
849
RELEASENOTES.md
19
appveyor.yml
@@ -1,19 +0,0 @@
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.12"
|
||||
- nodejs_version: "4.2"
|
||||
|
||||
install:
|
||||
# - cinst android-sdk
|
||||
# - echo y | android update sdk -u --filter android-22,android-23
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- npm install
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- node --version
|
||||
- npm --version
|
||||
- npm run test
|
||||
# - npm run test-build
|
||||
@@ -19,11 +19,9 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var android_sdk_version = require('./lib/android_sdk_version');
|
||||
var android_sdk = require('./templates/cordova/lib/android_sdk');
|
||||
|
||||
android_sdk_version.run().done(null, function(err) {
|
||||
console.log(err);
|
||||
android_sdk.print_newest_available_sdk_target().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0android_sdk_version"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'android_sdk_version' script in 'bin' folder, aborting...>&2
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var check_reqs = require('./lib/check_reqs');
|
||||
var check_reqs = require('./templates/cordova/lib/check_reqs');
|
||||
|
||||
check_reqs.run().done(
|
||||
function success() {
|
||||
check_reqs.run().then(
|
||||
function success () {
|
||||
console.log('Looks like your environment fully supports cordova-android development!');
|
||||
}, function fail(err) {
|
||||
},
|
||||
function fail (err) {
|
||||
console.log(err);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0check_reqs"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'check_reqs' script in 'bin' folder, aborting...>&2
|
||||
|
||||
28
bin/clean_test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
if (fs.existsSync('./test/gradlew')) {
|
||||
fs.unlinkSync('./test/gradlew');
|
||||
}
|
||||
|
||||
if (fs.existsSync('./test/gradlew.bat')) {
|
||||
fs.unlinkSync('./test/gradlew.bat');
|
||||
}
|
||||
17
bin/create
@@ -23,12 +23,12 @@ var ConfigParser = require('cordova-common').ConfigParser;
|
||||
var Api = require('./templates/cordova/Api');
|
||||
|
||||
var argv = require('nopt')({
|
||||
'help' : Boolean,
|
||||
'cli' : Boolean,
|
||||
'shared' : Boolean,
|
||||
'link' : Boolean,
|
||||
'activity-name' : [String, undefined]
|
||||
}, { 'd' : '--verbose' });
|
||||
help: Boolean,
|
||||
cli: Boolean,
|
||||
shared: Boolean,
|
||||
link: Boolean,
|
||||
'activity-name': [String, undefined]
|
||||
}, { d: '--verbose' });
|
||||
|
||||
if (argv.help || argv.argv.remain.length === 0) {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(__dirname, 'create')) + ' <path_to_new_project> <package_name> <project_name> [<template_path>] [--activity-name <activity_name>] [--link]');
|
||||
@@ -55,4 +55,7 @@ var options = {
|
||||
|
||||
require('./templates/cordova/loggingHelper').adjustLoggerLevel(argv);
|
||||
|
||||
Api.createPlatform(argv.argv.remain[0], config, options).done();
|
||||
Api.createPlatform(argv.argv.remain[0], config, options).catch(err => {
|
||||
console.error(err);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -23,4 +23,4 @@ IF EXIST %script_path% (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'create' script in 'bin' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var child_process = require('child_process'),
|
||||
Q = require('q');
|
||||
|
||||
var get_highest_sdk = function(results){
|
||||
var reg = /\d+/;
|
||||
var apiLevels = [];
|
||||
for(var i=0;i<results.length;i++){
|
||||
apiLevels[i] = parseInt(results[i].match(reg)[0]);
|
||||
}
|
||||
apiLevels.sort(function(a,b){return b-a;});
|
||||
console.log(apiLevels[0]);
|
||||
};
|
||||
|
||||
var get_sdks = function() {
|
||||
var d = Q.defer();
|
||||
child_process.exec('android list targets', function(err, stdout, stderr) {
|
||||
if (err) d.reject(stderr);
|
||||
else d.resolve(stdout);
|
||||
});
|
||||
|
||||
return d.promise.then(function(output) {
|
||||
var reg = /android-\d+/gi;
|
||||
var results = output.match(reg);
|
||||
if(results.length===0){
|
||||
return Q.reject(new Error('No android sdks installed.'));
|
||||
}else{
|
||||
get_highest_sdk(results);
|
||||
}
|
||||
|
||||
return Q();
|
||||
}, function(stderr) {
|
||||
if (stderr.match(/command\snot\sfound/) || stderr.match(/'android' is not recognized/)) {
|
||||
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
|
||||
} else {
|
||||
return Q.reject(new Error('An error occurred while listing Android targets'));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.run = function() {
|
||||
return Q.all([get_sdks()]);
|
||||
};
|
||||
|
||||
@@ -1,332 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var shelljs = require('shelljs'),
|
||||
child_process = require('child_process'),
|
||||
Q = require('q'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var isWindows = process.platform == 'win32';
|
||||
|
||||
function forgivingWhichSync(cmd) {
|
||||
try {
|
||||
return fs.realpathSync(shelljs.which(cmd));
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function tryCommand(cmd, errMsg, catchStderr) {
|
||||
var d = Q.defer();
|
||||
child_process.exec(cmd, function(err, stdout, stderr) {
|
||||
if (err) d.reject(new CordovaError(errMsg));
|
||||
// Sometimes it is necessary to return an stderr instead of stdout in case of success, since
|
||||
// some commands prints theirs output to stderr instead of stdout. 'javac' is the example
|
||||
else d.resolve((catchStderr ? stderr : stdout).trim());
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
// Get valid target from framework/project.properties
|
||||
module.exports.get_target = function() {
|
||||
function extractFromFile(filePath) {
|
||||
var target = shelljs.grep(/\btarget=/, filePath);
|
||||
if (!target) {
|
||||
throw new Error('Could not find android target within: ' + filePath);
|
||||
}
|
||||
return target.split('=')[1].trim();
|
||||
}
|
||||
if (fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
|
||||
return extractFromFile(path.join(ROOT, 'framework', 'project.properties'));
|
||||
}
|
||||
if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
|
||||
// if no target found, we're probably in a project and project.properties is in ROOT.
|
||||
return extractFromFile(path.join(ROOT, 'project.properties'));
|
||||
}
|
||||
throw new Error('Could not find android target. File missing: ' + path.join(ROOT, 'project.properties'));
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
module.exports.check_ant = function() {
|
||||
return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.')
|
||||
.then(function (output) {
|
||||
// Parse Ant version from command output
|
||||
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
module.exports.check_gradle = function() {
|
||||
var sdkDir = process.env['ANDROID_HOME'];
|
||||
if (!sdkDir)
|
||||
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
|
||||
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
|
||||
|
||||
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
||||
if (!fs.existsSync(wrapperDir)) {
|
||||
return Q.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Might need to update your Android SDK.\n' +
|
||||
'Looked here: ' + wrapperDir));
|
||||
}
|
||||
return Q.when();
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_java = function() {
|
||||
var javacPath = forgivingWhichSync('javac');
|
||||
var hasJavaHome = !!process.env['JAVA_HOME'];
|
||||
return Q().then(function() {
|
||||
if (hasJavaHome) {
|
||||
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
|
||||
if (!javacPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
|
||||
}
|
||||
} else {
|
||||
if (javacPath) {
|
||||
var msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
|
||||
// OS X has a command for finding JAVA_HOME.
|
||||
if (fs.existsSync('/usr/libexec/java_home')) {
|
||||
return tryCommand('/usr/libexec/java_home', msg)
|
||||
.then(function(stdout) {
|
||||
process.env['JAVA_HOME'] = stdout.trim();
|
||||
});
|
||||
} else {
|
||||
// See if we can derive it from javac's location.
|
||||
// fs.realpathSync is require on Ubuntu, which symplinks from /usr/bin -> JDK
|
||||
var maybeJavaHome = path.dirname(path.dirname(javacPath));
|
||||
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
|
||||
process.env['JAVA_HOME'] = maybeJavaHome;
|
||||
} else {
|
||||
throw new CordovaError(msg);
|
||||
}
|
||||
}
|
||||
} else if (isWindows) {
|
||||
// Try to auto-detect java in the default install paths.
|
||||
var oldSilent = shelljs.config.silent;
|
||||
shelljs.config.silent = true;
|
||||
var firstJdkDir =
|
||||
shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
|
||||
shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
|
||||
shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
|
||||
shelljs.config.silent = oldSilent;
|
||||
if (firstJdkDir) {
|
||||
// shelljs always uses / in paths.
|
||||
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
|
||||
if (!javacPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
|
||||
}
|
||||
process.env['JAVA_HOME'] = firstJdkDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(function() {
|
||||
var msg =
|
||||
'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
|
||||
'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
|
||||
if (process.env['JAVA_HOME']) {
|
||||
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
|
||||
}
|
||||
// We use tryCommand with catchStderr = true, because
|
||||
// javac writes version info to stderr instead of stdout
|
||||
return tryCommand('javac -version', msg, true)
|
||||
.then(function (output) {
|
||||
var match = /javac ((?:\d+\.)+(?:\d+))/i.exec(output);
|
||||
return match && match[1];
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_android = function() {
|
||||
return Q().then(function() {
|
||||
var androidCmdPath = forgivingWhichSync('android');
|
||||
var adbInPath = !!forgivingWhichSync('adb');
|
||||
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
||||
function maybeSetAndroidHome(value) {
|
||||
if (!hasAndroidHome && fs.existsSync(value)) {
|
||||
hasAndroidHome = true;
|
||||
process.env['ANDROID_HOME'] = value;
|
||||
}
|
||||
}
|
||||
if (!hasAndroidHome && !androidCmdPath) {
|
||||
if (isWindows) {
|
||||
// Android Studio 1.0 installer
|
||||
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'sdk'));
|
||||
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'sdk'));
|
||||
// Android Studio pre-1.0 installer
|
||||
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-studio', 'sdk'));
|
||||
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-studio', 'sdk'));
|
||||
// Stand-alone installer
|
||||
maybeSetAndroidHome(path.join(process.env['LOCALAPPDATA'], 'Android', 'android-sdk'));
|
||||
maybeSetAndroidHome(path.join(process.env['ProgramFiles'], 'Android', 'android-sdk'));
|
||||
} else if (process.platform == 'darwin') {
|
||||
// Android Studio 1.0 installer
|
||||
maybeSetAndroidHome(path.join(process.env['HOME'], 'Library', 'Android', 'sdk'));
|
||||
// Android Studio pre-1.0 installer
|
||||
maybeSetAndroidHome('/Applications/Android Studio.app/sdk');
|
||||
// Stand-alone zip file that user might think to put under /Applications
|
||||
maybeSetAndroidHome('/Applications/android-sdk-macosx');
|
||||
maybeSetAndroidHome('/Applications/android-sdk');
|
||||
}
|
||||
if (process.env['HOME']) {
|
||||
// Stand-alone zip file that user might think to put under their home directory
|
||||
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk-macosx'));
|
||||
maybeSetAndroidHome(path.join(process.env['HOME'], 'android-sdk'));
|
||||
}
|
||||
}
|
||||
if (hasAndroidHome && !androidCmdPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
|
||||
}
|
||||
if (androidCmdPath && !hasAndroidHome) {
|
||||
var parentDir = path.dirname(androidCmdPath);
|
||||
var grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) == 'tools') {
|
||||
process.env['ANDROID_HOME'] = path.dirname(parentDir);
|
||||
hasAndroidHome = true;
|
||||
} else if (fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
|
||||
process.env['ANDROID_HOME'] = grandParentDir;
|
||||
hasAndroidHome = true;
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
|
||||
'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include path to valid SDK directory.');
|
||||
}
|
||||
}
|
||||
if (hasAndroidHome && !adbInPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
|
||||
}
|
||||
if (!process.env['ANDROID_HOME']) {
|
||||
throw new CordovaError('Failed to find \'ANDROID_HOME\' environment variable. Try setting setting it manually.\n' +
|
||||
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
|
||||
}
|
||||
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
|
||||
throw new CordovaError('\'ANDROID_HOME\' environment variable is set to non-existent path: ' + process.env['ANDROID_HOME'] +
|
||||
'\nTry update it manually to point to valid SDK directory.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.getAbsoluteAndroidCmd = function () {
|
||||
var cmd = forgivingWhichSync('android');
|
||||
if (process.platform === 'win32') {
|
||||
return '"' + cmd + '"';
|
||||
}
|
||||
return cmd.replace(/(\s)/g, '\\$1');
|
||||
};
|
||||
|
||||
module.exports.check_android_target = function(originalError) {
|
||||
// valid_target can look like:
|
||||
// android-19
|
||||
// android-L
|
||||
// Google Inc.:Google APIs:20
|
||||
// Google Inc.:Glass Development Kit Preview:20
|
||||
var valid_target = module.exports.get_target();
|
||||
var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.';
|
||||
return tryCommand('android list targets --compact', msg)
|
||||
.then(function(output) {
|
||||
var targets = output.split('\n');
|
||||
if (targets.indexOf(valid_target) >= 0) {
|
||||
return targets;
|
||||
}
|
||||
|
||||
var androidCmd = module.exports.getAbsoluteAndroidCmd();
|
||||
var msg = 'Please install Android target: "' + valid_target + '".\n\n' +
|
||||
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
|
||||
'You will require:\n' +
|
||||
'1. "SDK Platform" for ' + valid_target + '\n' +
|
||||
'2. "Android SDK Platform-tools (latest)\n' +
|
||||
'3. "Android SDK Build-tools" (latest)';
|
||||
if (originalError) {
|
||||
msg = originalError + '\n' + msg;
|
||||
}
|
||||
throw new CordovaError(msg);
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.run = function() {
|
||||
return Q.all([this.check_java(), this.check_android()])
|
||||
.then(function() {
|
||||
console.log('ANDROID_HOME=' + process.env['ANDROID_HOME']);
|
||||
console.log('JAVA_HOME=' + process.env['JAVA_HOME']);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Object thar represents one of requirements for current platform.
|
||||
* @param {String} id The unique identifier for this requirements.
|
||||
* @param {String} name The name of requirements. Human-readable field.
|
||||
* @param {String} version The version of requirement installed. In some cases could be an array of strings
|
||||
* (for example, check_android_target returns an array of android targets installed)
|
||||
* @param {Boolean} installed Indicates whether the requirement is installed or not
|
||||
*/
|
||||
var Requirement = function (id, name, version, installed) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.installed = installed || false;
|
||||
this.metadata = {
|
||||
version: version,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Methods that runs all checks one by one and returns a result of checks
|
||||
* as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method
|
||||
*
|
||||
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
|
||||
*/
|
||||
module.exports.check_all = function() {
|
||||
|
||||
var requirements = [
|
||||
new Requirement('java', 'Java JDK'),
|
||||
new Requirement('androidSdk', 'Android SDK'),
|
||||
new Requirement('androidTarget', 'Android target'),
|
||||
new Requirement('gradle', 'Gradle')
|
||||
];
|
||||
|
||||
var checkFns = [
|
||||
this.check_java,
|
||||
this.check_android,
|
||||
this.check_android_target,
|
||||
this.check_gradle
|
||||
];
|
||||
|
||||
// Then execute requirement checks one-by-one
|
||||
return checkFns.reduce(function (promise, checkFn, idx) {
|
||||
// Update each requirement with results
|
||||
var requirement = requirements[idx];
|
||||
return promise.then(checkFn)
|
||||
.then(function (version) {
|
||||
requirement.installed = true;
|
||||
requirement.metadata.version = version;
|
||||
}, function (err) {
|
||||
requirement.metadata.reason = err instanceof Error ? err.message : err;
|
||||
});
|
||||
}, Q())
|
||||
.then(function () {
|
||||
// When chain is completed, return requirements array to upstream API
|
||||
return requirements;
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,78 +17,65 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var shell = require('shelljs'),
|
||||
Q = require('q'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
check_reqs = require('./check_reqs'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
var MIN_SDK_VERSION = 14;
|
||||
var path = require('path');
|
||||
var fs = require('fs-extra');
|
||||
var utils = require('../../bin/lib/utils');
|
||||
var check_reqs = require('./../templates/cordova/lib/check_reqs');
|
||||
var ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var AndroidManifest = require('../templates/cordova/lib/AndroidManifest');
|
||||
|
||||
function setShellFatal(value, func) {
|
||||
var oldVal = shell.config.fatal;
|
||||
shell.config.fatal = value;
|
||||
func();
|
||||
shell.config.fatal = oldVal;
|
||||
}
|
||||
// Export all helper functions, and make sure internally within this module, we
|
||||
// reference these methods via the `exports` object - this helps with testing
|
||||
// (since we can then mock and control behaviour of all of these functions)
|
||||
exports.validatePackageName = validatePackageName;
|
||||
exports.validateProjectName = validateProjectName;
|
||||
exports.copyJsAndLibrary = copyJsAndLibrary;
|
||||
exports.copyScripts = copyScripts;
|
||||
exports.copyBuildRules = copyBuildRules;
|
||||
exports.writeProjectProperties = writeProjectProperties;
|
||||
exports.prepBuildFiles = prepBuildFiles;
|
||||
|
||||
function getFrameworkDir(projectPath, shared) {
|
||||
function getFrameworkDir (projectPath, shared) {
|
||||
return shared ? path.join(ROOT, 'framework') : path.join(projectPath, 'CordovaLib');
|
||||
}
|
||||
|
||||
function copyJsAndLibrary(projectPath, shared, projectName) {
|
||||
function copyJsAndLibrary (projectPath, shared, projectName, isLegacy) {
|
||||
var nestedCordovaLibPath = getFrameworkDir(projectPath, false);
|
||||
var srcCordovaJsPath = path.join(ROOT, 'bin', 'templates', 'project', 'assets', 'www', 'cordova.js');
|
||||
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'assets', 'www', 'cordova.js'));
|
||||
var app_path = path.join(projectPath, 'app', 'src', 'main');
|
||||
const platform_www = path.join(projectPath, 'platform_www');
|
||||
|
||||
if (isLegacy) {
|
||||
app_path = projectPath;
|
||||
}
|
||||
|
||||
fs.copySync(srcCordovaJsPath, path.join(app_path, 'assets', 'www', 'cordova.js'));
|
||||
|
||||
// Copy the cordova.js file to platforms/<platform>/platform_www/
|
||||
// The www dir is nuked on each prepare so we keep cordova.js in platform_www
|
||||
shell.mkdir('-p', path.join(projectPath, 'platform_www'));
|
||||
shell.cp('-f', srcCordovaJsPath, path.join(projectPath, 'platform_www'));
|
||||
fs.ensureDirSync(platform_www);
|
||||
fs.copySync(srcCordovaJsPath, path.join(platform_www, 'cordova.js'));
|
||||
|
||||
// Copy cordova-js-src directory into platform_www directory.
|
||||
// We need these files to build cordova.js if using browserify method.
|
||||
shell.cp('-rf', path.join(ROOT, 'cordova-js-src'), path.join(projectPath, 'platform_www'));
|
||||
fs.copySync(path.join(ROOT, 'cordova-js-src'), path.join(platform_www, 'cordova-js-src'));
|
||||
|
||||
// Don't fail if there are no old jars.
|
||||
setShellFatal(false, function() {
|
||||
shell.ls(path.join(projectPath, 'libs', 'cordova-*.jar')).forEach(function(oldJar) {
|
||||
console.log('Deleting ' + oldJar);
|
||||
shell.rm('-f', oldJar);
|
||||
});
|
||||
var wasSymlink = true;
|
||||
try {
|
||||
// Delete the symlink if it was one.
|
||||
fs.unlinkSync(nestedCordovaLibPath);
|
||||
} catch (e) {
|
||||
wasSymlink = false;
|
||||
}
|
||||
// Delete old library project if it existed.
|
||||
if (shared) {
|
||||
shell.rm('-rf', nestedCordovaLibPath);
|
||||
} else if (!wasSymlink) {
|
||||
// Delete only the src, since Eclipse / Android Studio can't handle their project files being deleted.
|
||||
shell.rm('-rf', path.join(nestedCordovaLibPath, 'src'));
|
||||
}
|
||||
});
|
||||
if (shared) {
|
||||
var relativeFrameworkPath = path.relative(projectPath, getFrameworkDir(projectPath, true));
|
||||
fs.symlinkSync(relativeFrameworkPath, nestedCordovaLibPath, 'dir');
|
||||
} else {
|
||||
shell.mkdir('-p', nestedCordovaLibPath);
|
||||
shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
|
||||
shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
|
||||
shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
|
||||
shell.cp('-f', path.join(ROOT, 'framework', 'cordova.gradle'), nestedCordovaLibPath);
|
||||
shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
|
||||
fs.ensureDirSync(nestedCordovaLibPath);
|
||||
fs.copySync(path.join(ROOT, 'framework', 'AndroidManifest.xml'), path.join(nestedCordovaLibPath, 'AndroidManifest.xml'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'project.properties'), path.join(nestedCordovaLibPath, 'project.properties'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'build.gradle'), path.join(nestedCordovaLibPath, 'build.gradle'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'cordova.gradle'), path.join(nestedCordovaLibPath, 'cordova.gradle'));
|
||||
fs.copySync(path.join(ROOT, 'framework', 'src'), path.join(nestedCordovaLibPath, 'src'));
|
||||
}
|
||||
}
|
||||
|
||||
function extractSubProjectPaths(data) {
|
||||
function extractSubProjectPaths (data) {
|
||||
var ret = {};
|
||||
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg;
|
||||
var m;
|
||||
@@ -100,7 +85,7 @@ function extractSubProjectPaths(data) {
|
||||
return Object.keys(ret);
|
||||
}
|
||||
|
||||
function writeProjectProperties(projectPath, target_api) {
|
||||
function writeProjectProperties (projectPath, target_api) {
|
||||
var dstPath = path.join(projectPath, 'project.properties');
|
||||
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
|
||||
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
|
||||
@@ -108,11 +93,10 @@ function writeProjectProperties(projectPath, target_api) {
|
||||
var data = fs.readFileSync(srcPath, 'utf8');
|
||||
data = data.replace(/^target=.*/m, 'target=' + target_api);
|
||||
var subProjects = extractSubProjectPaths(data);
|
||||
subProjects = subProjects.filter(function(p) {
|
||||
subProjects = subProjects.filter(function (p) {
|
||||
return !(/^CordovaLib$/m.exec(p) ||
|
||||
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
|
||||
/^(\.\.[\\\/])+framework$/m.exec(p)
|
||||
);
|
||||
/[\\/]cordova-android[\\/]framework$/m.exec(p) ||
|
||||
/^(\.\.[\\/])+framework$/m.exec(p));
|
||||
});
|
||||
subProjects.unshift('CordovaLib');
|
||||
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
|
||||
@@ -120,34 +104,59 @@ function writeProjectProperties(projectPath, target_api) {
|
||||
data += '\n';
|
||||
}
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
|
||||
data += 'android.library.reference.' + (i + 1) + '=' + subProjects[i] + '\n';
|
||||
}
|
||||
fs.writeFileSync(dstPath, data);
|
||||
}
|
||||
|
||||
function prepBuildFiles(projectPath) {
|
||||
var buildModule = require(path.resolve(projectPath, 'cordova/lib/builders/builders'));
|
||||
buildModule.getBuilder('gradle').prepBuildFiles();
|
||||
// This makes no sense, what if you're building with a different build system?
|
||||
function prepBuildFiles (projectPath) {
|
||||
var buildModule = require('../templates/cordova/lib/builders/builders');
|
||||
buildModule.getBuilder(projectPath).prepBuildFiles();
|
||||
}
|
||||
|
||||
function copyBuildRules(projectPath) {
|
||||
function copyBuildRules (projectPath, isLegacy) {
|
||||
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
|
||||
|
||||
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
|
||||
if (isLegacy) {
|
||||
// The project's build.gradle is identical to the earlier build.gradle, so it should still work
|
||||
fs.copySync(path.join(srcDir, 'legacy', 'build.gradle'), path.join(projectPath, 'legacy', 'build.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
|
||||
} else {
|
||||
fs.copySync(path.join(srcDir, 'build.gradle'), path.join(projectPath, 'build.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'app', 'build.gradle'), path.join(projectPath, 'app', 'build.gradle'));
|
||||
fs.copySync(path.join(srcDir, 'wrapper.gradle'), path.join(projectPath, 'wrapper.gradle'));
|
||||
}
|
||||
}
|
||||
|
||||
function copyScripts(projectPath) {
|
||||
var srcScriptsDir = path.join(ROOT, 'bin', 'templates', 'cordova');
|
||||
function copyScripts (projectPath) {
|
||||
var bin = path.join(ROOT, 'bin');
|
||||
var srcScriptsDir = path.join(bin, 'templates', 'cordova');
|
||||
var destScriptsDir = path.join(projectPath, 'cordova');
|
||||
// Delete old scripts directory if this is an update.
|
||||
shell.rm('-rf', destScriptsDir);
|
||||
fs.removeSync(destScriptsDir);
|
||||
// Copy in the new ones.
|
||||
shell.cp('-r', srcScriptsDir, projectPath);
|
||||
shell.cp('-r', path.join(ROOT, 'node_modules'), destScriptsDir);
|
||||
shell.cp(path.join(ROOT, 'bin', 'check_reqs*'), destScriptsDir);
|
||||
shell.cp(path.join(ROOT, 'bin', 'lib', 'check_reqs.js'), path.join(projectPath, 'cordova', 'lib', 'check_reqs.js'));
|
||||
shell.cp(path.join(ROOT, 'bin', 'android_sdk_version'), path.join(destScriptsDir, 'android_sdk_version'));
|
||||
shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
|
||||
fs.copySync(srcScriptsDir, destScriptsDir);
|
||||
|
||||
const nodeModulesDir = path.join(ROOT, 'node_modules');
|
||||
if (fs.existsSync(nodeModulesDir)) fs.copySync(nodeModulesDir, path.join(destScriptsDir, 'node_modules'));
|
||||
|
||||
fs.copySync(path.join(bin, 'check_reqs'), path.join(destScriptsDir, 'check_reqs'));
|
||||
fs.copySync(path.join(bin, 'check_reqs.bat'), path.join(destScriptsDir, 'check_reqs.bat'));
|
||||
fs.copySync(path.join(bin, 'android_sdk_version'), path.join(destScriptsDir, 'android_sdk_version'));
|
||||
fs.copySync(path.join(bin, 'android_sdk_version.bat'), path.join(destScriptsDir, 'android_sdk_version.bat'));
|
||||
|
||||
var check_reqs = path.join(destScriptsDir, 'check_reqs');
|
||||
var android_sdk_version = path.join(destScriptsDir, 'android_sdk_version');
|
||||
|
||||
// TODO: the two files being edited on-the-fly here are shared between
|
||||
// platform and project-level commands. the below is updating the
|
||||
// `require` path for the two libraries. if there's a better way to share
|
||||
// modules across both the repo and generated projects, we should make sure
|
||||
// to remove/update this.
|
||||
const templatesCordovaRegex = /templates\/cordova\//;
|
||||
utils.replaceFileContents(android_sdk_version, templatesCordovaRegex, '');
|
||||
utils.replaceFileContents(check_reqs, templatesCordovaRegex, '');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,46 +164,37 @@ function copyScripts(projectPath) {
|
||||
* Returns a promise, fulfilled if the package name is acceptable; rejected
|
||||
* otherwise.
|
||||
*/
|
||||
function validatePackageName(package_name) {
|
||||
//Make the package conform to Java package types
|
||||
//http://developer.android.com/guide/topics/manifest/manifest-element.html#package
|
||||
//Enforce underscore limitation
|
||||
function validatePackageName (package_name) {
|
||||
// Make the package conform to Java package types
|
||||
// http://developer.android.com/guide/topics/manifest/manifest-element.html#package
|
||||
// Enforce underscore limitation
|
||||
var msg = 'Error validating package name. ';
|
||||
|
||||
if (!/^[a-zA-Z][a-zA-Z0-9_]+(\.[a-zA-Z][a-zA-Z0-9_]*)+$/.test(package_name)) {
|
||||
return Q.reject(new CordovaError(msg + 'Package name must look like: com.company.Name'));
|
||||
return Promise.reject(new CordovaError(msg + 'Must look like: `com.company.Name`. Currently is: `' + package_name + '`'));
|
||||
}
|
||||
|
||||
//Class is a reserved word
|
||||
if(/\b[Cc]lass\b/.test(package_name)) {
|
||||
return Q.reject(new CordovaError(msg + '"class" is a reserved word'));
|
||||
// Class is a reserved word
|
||||
if (/\b[Cc]lass\b/.test(package_name)) {
|
||||
return Promise.reject(new CordovaError(msg + '"class" is a reserved word'));
|
||||
}
|
||||
|
||||
return Q.resolve();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether a project name is acceptable for use as an android class.
|
||||
* Test whether given string is acceptable for use as a project name
|
||||
* Returns a promise, fulfilled if the project name is acceptable; rejected
|
||||
* otherwise.
|
||||
*/
|
||||
function validateProjectName(project_name) {
|
||||
function validateProjectName (project_name) {
|
||||
var msg = 'Error validating project name. ';
|
||||
//Make sure there's something there
|
||||
// Make sure there's something there
|
||||
if (project_name === '') {
|
||||
return Q.reject(new CordovaError(msg + 'Project name cannot be empty'));
|
||||
return Promise.reject(new CordovaError(msg + 'Project name cannot be empty'));
|
||||
}
|
||||
|
||||
//Enforce stupid name error
|
||||
if (project_name === 'CordovaActivity') {
|
||||
return Q.reject(new CordovaError(msg + 'Project name cannot be CordovaActivity'));
|
||||
}
|
||||
|
||||
//Classes in Java don't begin with numbers
|
||||
if (/^[0-9]/.test(project_name)) {
|
||||
return Q.reject(new CordovaError(msg + 'Project name must not begin with a number'));
|
||||
}
|
||||
|
||||
return Q.resolve();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,83 +215,92 @@ function validateProjectName(project_name) {
|
||||
*
|
||||
* @return {Promise<String>} Directory where application has been created
|
||||
*/
|
||||
exports.create = function(project_path, config, options, events) {
|
||||
|
||||
exports.create = function (project_path, config, options, events) {
|
||||
options = options || {};
|
||||
|
||||
// Set default values for path, package and name
|
||||
project_path = path.relative(process.cwd(), (project_path || 'CordovaExample'));
|
||||
// Check if project already exists
|
||||
if(fs.existsSync(project_path)) {
|
||||
return Q.reject(new CordovaError('Project already exists! Delete and recreate'));
|
||||
if (fs.existsSync(project_path)) {
|
||||
return Promise.reject(new CordovaError('Project already exists! Delete and recreate'));
|
||||
}
|
||||
|
||||
var package_name = config.packageName() || 'my.cordova.project';
|
||||
var project_name = config.name() ?
|
||||
config.name().replace(/[^\w.]/g,'_') : 'CordovaExample';
|
||||
var package_name = config.android_packageName() || config.packageName() || 'my.cordova.project';
|
||||
var project_name = config.name()
|
||||
? config.name().replace(/[^\w.]/g, '_') : 'CordovaExample';
|
||||
|
||||
var safe_activity_name = config.android_activityName() || options.activityName || 'MainActivity';
|
||||
var target_api = check_reqs.get_target();
|
||||
var target_api = check_reqs.get_target();
|
||||
|
||||
//Make the package conform to Java package types
|
||||
return validatePackageName(package_name)
|
||||
.then(function() {
|
||||
validateProjectName(project_name);
|
||||
}).then(function() {
|
||||
// Make the package conform to Java package types
|
||||
return exports.validatePackageName(package_name)
|
||||
.then(function () {
|
||||
return exports.validateProjectName(project_name);
|
||||
}).then(function () {
|
||||
// Log the given values for the project
|
||||
events.emit('log', 'Creating Cordova project for the Android platform:');
|
||||
events.emit('log', '\tPath: ' + project_path);
|
||||
events.emit('log', '\tPackage: ' + package_name);
|
||||
events.emit('log', '\tName: ' + project_name);
|
||||
events.emit('log', '\tActivity: ' + safe_activity_name);
|
||||
events.emit('log', '\tAndroid target: ' + target_api);
|
||||
events.emit('log', 'Creating Cordova project for the Android platform:');
|
||||
events.emit('log', '\tPath: ' + project_path);
|
||||
events.emit('log', '\tPackage: ' + package_name);
|
||||
events.emit('log', '\tName: ' + project_name);
|
||||
events.emit('log', '\tActivity: ' + safe_activity_name);
|
||||
events.emit('log', '\tAndroid target: ' + target_api);
|
||||
|
||||
events.emit('verbose', 'Copying android template project to ' + project_path);
|
||||
events.emit('verbose', 'Copying android template project to ' + project_path);
|
||||
|
||||
setShellFatal(true, function() {
|
||||
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');
|
||||
var app_path = path.join(project_path, 'app', 'src', 'main');
|
||||
|
||||
// copy project template
|
||||
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
|
||||
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
|
||||
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
fs.ensureDirSync(app_path);
|
||||
fs.copySync(path.join(project_template_dir, 'assets'), path.join(app_path, 'assets'));
|
||||
fs.copySync(path.join(project_template_dir, 'res'), path.join(app_path, 'res'));
|
||||
fs.copySync(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
|
||||
// Manually create directories that would be empty within the template (since git doesn't track directories).
|
||||
shell.mkdir(path.join(project_path, 'libs'));
|
||||
fs.ensureDirSync(path.join(app_path, 'libs'));
|
||||
|
||||
// copy cordova.js, cordova.jar
|
||||
copyJsAndLibrary(project_path, options.link, safe_activity_name);
|
||||
exports.copyJsAndLibrary(project_path, options.link, safe_activity_name);
|
||||
|
||||
// Set up ther Android Studio paths
|
||||
var java_path = path.join(app_path, 'java');
|
||||
var assets_path = path.join(app_path, 'assets');
|
||||
var resource_path = path.join(app_path, 'res');
|
||||
fs.ensureDirSync(java_path);
|
||||
fs.ensureDirSync(assets_path);
|
||||
fs.ensureDirSync(resource_path);
|
||||
|
||||
// interpolate the activity name and package
|
||||
var packagePath = package_name.replace(/\./g, path.sep);
|
||||
var activity_dir = path.join(project_path, 'src', packagePath);
|
||||
var activity_dir = path.join(java_path, packagePath);
|
||||
var activity_path = path.join(activity_dir, safe_activity_name + '.java');
|
||||
shell.mkdir('-p', activity_dir);
|
||||
shell.cp('-f', path.join(project_template_dir, 'Activity.java'), activity_path);
|
||||
shell.sed('-i', /__ACTIVITY__/, safe_activity_name, activity_path);
|
||||
shell.sed('-i', /__NAME__/, project_name, path.join(project_path, 'res', 'values', 'strings.xml'));
|
||||
shell.sed('-i', /__ID__/, package_name, activity_path);
|
||||
|
||||
fs.ensureDirSync(activity_dir);
|
||||
fs.copySync(path.join(project_template_dir, 'Activity.java'), activity_path);
|
||||
utils.replaceFileContents(activity_path, /__ACTIVITY__/, safe_activity_name);
|
||||
utils.replaceFileContents(path.join(app_path, 'res', 'values', 'strings.xml'), /__NAME__/, project_name);
|
||||
utils.replaceFileContents(activity_path, /__ID__/, package_name);
|
||||
|
||||
var manifest = new AndroidManifest(path.join(project_template_dir, 'AndroidManifest.xml'));
|
||||
manifest.setPackageId(package_name)
|
||||
.setTargetSdkVersion(target_api.split('-')[1])
|
||||
.getActivity().setName(safe_activity_name);
|
||||
|
||||
var manifest_path = path.join(project_path, 'AndroidManifest.xml');
|
||||
var manifest_path = path.join(app_path, 'AndroidManifest.xml');
|
||||
manifest.write(manifest_path);
|
||||
|
||||
copyScripts(project_path);
|
||||
copyBuildRules(project_path);
|
||||
});
|
||||
// Link it to local android install.
|
||||
writeProjectProperties(project_path, target_api);
|
||||
prepBuildFiles(project_path);
|
||||
events.emit('log', generateDoneMessage('create', options.link));
|
||||
}).thenResolve(project_path);
|
||||
exports.copyScripts(project_path);
|
||||
exports.copyBuildRules(project_path);
|
||||
|
||||
// Link it to local android install.
|
||||
exports.writeProjectProperties(project_path, target_api);
|
||||
exports.prepBuildFiles(project_path);
|
||||
events.emit('log', generateDoneMessage('create', options.link));
|
||||
}).then(() => project_path);
|
||||
};
|
||||
|
||||
function generateDoneMessage(type, link) {
|
||||
function generateDoneMessage (type, link) {
|
||||
var pkg = require('../../package');
|
||||
var msg = 'Android project ' + (type == 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
|
||||
var msg = 'Android project ' + (type === 'update' ? 'updated ' : 'created ') + 'with ' + pkg.name + '@' + pkg.version;
|
||||
if (link) {
|
||||
msg += ' and has a linked CordovaLib';
|
||||
}
|
||||
@@ -299,34 +308,16 @@ function generateDoneMessage(type, link) {
|
||||
}
|
||||
|
||||
// Returns a promise.
|
||||
exports.update = function(projectPath, options, events) {
|
||||
options = options || {};
|
||||
exports.update = function (projectPath, options, events) {
|
||||
var errorString =
|
||||
'An in-place platform update is not supported. \n' +
|
||||
'The `platforms` folder is always treated as a build artifact in the CLI workflow.\n' +
|
||||
'To update your platform, you have to remove, then add your android platform again.\n' +
|
||||
'Make sure you save your plugins beforehand using `cordova plugin save`, and save \n' + 'a copy of the platform first if you had manual changes in it.\n' +
|
||||
'\tcordova plugin save\n' +
|
||||
'\tcordova platform rm android\n' +
|
||||
'\tcordova platform add android\n'
|
||||
;
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
|
||||
var manifest = new AndroidManifest(path.join(projectPath, 'AndroidManifest.xml'));
|
||||
|
||||
if (Number(manifest.getMinSdkVersion()) < MIN_SDK_VERSION) {
|
||||
events.emit('verbose', 'Updating minSdkVersion to ' + MIN_SDK_VERSION + ' in AndroidManifest.xml');
|
||||
manifest.setMinSdkVersion(MIN_SDK_VERSION);
|
||||
}
|
||||
|
||||
manifest.setDebuggable(false).write();
|
||||
|
||||
var projectName = manifest.getActivity().getName();
|
||||
var target_api = check_reqs.get_target();
|
||||
|
||||
copyJsAndLibrary(projectPath, options.link, projectName);
|
||||
copyScripts(projectPath);
|
||||
copyBuildRules(projectPath);
|
||||
writeProjectProperties(projectPath, target_api);
|
||||
prepBuildFiles(projectPath);
|
||||
events.emit('log', generateDoneMessage('update', options.link));
|
||||
}).thenResolve(projectPath);
|
||||
return Promise.reject(errorString);
|
||||
};
|
||||
|
||||
|
||||
// For testing
|
||||
exports.validatePackageName = validatePackageName;
|
||||
exports.validateProjectName = validateProjectName;
|
||||
|
||||
47
bin/lib/utils.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Provides a set of utility methods, which can also be spied on during unit tests.
|
||||
*/
|
||||
|
||||
// TODO: Perhaps this should live in cordova-common?
|
||||
|
||||
const fs = require('fs-extra');
|
||||
|
||||
/**
|
||||
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
|
||||
* A backup is not made.
|
||||
*
|
||||
* @param {string} file A file path to a readable & writable file
|
||||
* @param {RegExp} searchRegex The search regex
|
||||
* @param {string} replacementString The string to replace the found occurences
|
||||
* @returns {void}
|
||||
*/
|
||||
exports.replaceFileContents = function (file, searchRegex, replacementString) {
|
||||
// let contents;
|
||||
try {
|
||||
var contents = fs.readFileSync(file).toString();
|
||||
} catch (ex) {
|
||||
console.log('TRYING TO READ: ', file);
|
||||
throw ex;
|
||||
}
|
||||
contents = contents.replace(searchRegex, replacementString);
|
||||
fs.writeFileSync(file, contents);
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"node": true
|
||||
, "bitwise": true
|
||||
, "undef": true
|
||||
, "trailing": true
|
||||
, "quotmark": true
|
||||
, "indent": 4
|
||||
, "unused": "vars"
|
||||
, "latedef": "nofunc"
|
||||
}
|
||||
595
bin/templates/cordova/Api.js
vendored
@@ -17,6 +17,15 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @todo update coho to update this line.
|
||||
* @todo use `package.json` instead but first
|
||||
* figure out how this fit in with the platform-centered workflow structure.
|
||||
* This workflow would not have the `package.json` file.
|
||||
*/
|
||||
// Coho updates this line
|
||||
const VERSION = '9.0.0';
|
||||
|
||||
var path = require('path');
|
||||
|
||||
var AndroidProject = require('./lib/AndroidProject');
|
||||
@@ -24,10 +33,12 @@ var PluginManager = require('cordova-common').PluginManager;
|
||||
|
||||
var CordovaLogger = require('cordova-common').CordovaLogger;
|
||||
var selfEvents = require('cordova-common').events;
|
||||
var ConfigParser = require('cordova-common').ConfigParser;
|
||||
const prepare = require('./lib/prepare').prepare;
|
||||
|
||||
var PLATFORM = 'android';
|
||||
|
||||
function setupEvents(externalEventEmitter) {
|
||||
function setupEvents (externalEventEmitter) {
|
||||
if (externalEventEmitter) {
|
||||
// This will make the platform internal events visible outside
|
||||
selfEvents.forwardEventsTo(externalEventEmitter);
|
||||
@@ -50,300 +61,324 @@ function setupEvents(externalEventEmitter) {
|
||||
* The PlatformApi instance also should define the following field:
|
||||
*
|
||||
* * platform: String that defines a platform name.
|
||||
* @class Api
|
||||
*/
|
||||
function Api(platform, platformRootDir, events) {
|
||||
this.platform = PLATFORM;
|
||||
this.root = path.resolve(__dirname, '..');
|
||||
class Api {
|
||||
constructor (platform, platformRootDir, events) {
|
||||
this.platform = PLATFORM;
|
||||
this.root = path.resolve(__dirname, '..');
|
||||
|
||||
setupEvents(events);
|
||||
setupEvents(events);
|
||||
|
||||
var self = this;
|
||||
const appMain = path.join(this.root, 'app', 'src', 'main');
|
||||
const appRes = path.join(appMain, 'res');
|
||||
|
||||
this.locations = {
|
||||
root: self.root,
|
||||
www: path.join(self.root, 'assets/www'),
|
||||
platformWww: path.join(self.root, 'platform_www'),
|
||||
configXml: path.join(self.root, 'res/xml/config.xml'),
|
||||
defaultConfigXml: path.join(self.root, 'cordova/defaults.xml'),
|
||||
strings: path.join(self.root, 'res/values/strings.xml'),
|
||||
manifest: path.join(self.root, 'AndroidManifest.xml'),
|
||||
// NOTE: Due to platformApi spec we need to return relative paths here
|
||||
cordovaJs: 'bin/templates/project/assets/www/cordova.js',
|
||||
cordovaJsSrc: 'cordova-js-src'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs platform to specified directory and creates a platform project.
|
||||
*
|
||||
* @param {String} destination Destination directory, where insatll platform to
|
||||
* @param {ConfigParser} [config] ConfgiParser instance, used to retrieve
|
||||
* project creation options, such as package id and project name.
|
||||
* @param {Object} [options] An options object. The most common options are:
|
||||
* @param {String} [options.customTemplate] A path to custom template, that
|
||||
* should override the default one from platform.
|
||||
* @param {Boolean} [options.link] Flag that indicates that platform's
|
||||
* sources will be linked to installed platform instead of copying.
|
||||
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
|
||||
* logging purposes. If no EventEmitter provided, all events will be logged to
|
||||
* console
|
||||
*
|
||||
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
|
||||
* instance or rejected with CordovaError.
|
||||
*/
|
||||
Api.createPlatform = function (destination, config, options, events) {
|
||||
events = setupEvents(events);
|
||||
|
||||
return require('../../lib/create')
|
||||
.create(destination, config, options, events)
|
||||
.then(function (destination) {
|
||||
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
|
||||
return new PlatformApi(PLATFORM, destination, events);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates already installed platform.
|
||||
*
|
||||
* @param {String} destination Destination directory, where platform installed
|
||||
* @param {Object} [options] An options object. The most common options are:
|
||||
* @param {String} [options.customTemplate] A path to custom template, that
|
||||
* should override the default one from platform.
|
||||
* @param {Boolean} [options.link] Flag that indicates that platform's
|
||||
* sources will be linked to installed platform instead of copying.
|
||||
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
|
||||
* logging purposes. If no EventEmitter provided, all events will be logged to
|
||||
* console
|
||||
*
|
||||
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
|
||||
* instance or rejected with CordovaError.
|
||||
*/
|
||||
Api.updatePlatform = function (destination, options, events) {
|
||||
events = setupEvents(events);
|
||||
|
||||
return require('../../lib/create')
|
||||
.update(destination, options, events)
|
||||
.then(function (destination) {
|
||||
var PlatformApi = require(path.resolve(destination, 'cordova/Api'));
|
||||
return new PlatformApi('android', destination, events);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a CordovaPlatform object, that represents the platform structure.
|
||||
*
|
||||
* @return {CordovaPlatform} A structure that contains the description of
|
||||
* platform's file structure and other properties of platform.
|
||||
*/
|
||||
Api.prototype.getPlatformInfo = function () {
|
||||
var result = {};
|
||||
result.locations = this.locations;
|
||||
result.root = this.root;
|
||||
result.name = this.platform;
|
||||
result.version = require('./version');
|
||||
result.projectConfig = this._config;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates installed platform with provided www assets and new app
|
||||
* configuration. This method is required for CLI workflow and will be called
|
||||
* each time before build, so the changes, made to app configuration and www
|
||||
* code, will be applied to platform.
|
||||
*
|
||||
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
|
||||
* project structure and configuration, that should be applied to platform
|
||||
* (contains project's www location and ConfigParser instance for project's
|
||||
* config).
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError instance.
|
||||
*/
|
||||
Api.prototype.prepare = function (cordovaProject, prepareOptions) {
|
||||
return require('./lib/prepare').prepare.call(this, cordovaProject, prepareOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Installs a new plugin into platform. This method only copies non-www files
|
||||
* (sources, libs, etc.) to platform. It also doesn't resolves the
|
||||
* dependencies of plugin. Both of handling of www files, such as assets and
|
||||
* js-files and resolving dependencies are the responsibility of caller.
|
||||
*
|
||||
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
|
||||
* that will be installed.
|
||||
* @param {Object} installOptions An options object. Possible options below:
|
||||
* @param {Boolean} installOptions.link: Flag that specifies that plugin
|
||||
* sources will be symlinked to app's directory instead of copying (if
|
||||
* possible).
|
||||
* @param {Object} installOptions.variables An object that represents
|
||||
* variables that will be used to install plugin. See more details on plugin
|
||||
* variables in documentation:
|
||||
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError instance.
|
||||
*/
|
||||
Api.prototype.addPlugin = function (plugin, installOptions) {
|
||||
var project = AndroidProject.getProjectFile(this.root);
|
||||
|
||||
installOptions = installOptions || {};
|
||||
installOptions.variables = installOptions.variables || {};
|
||||
// Add PACKAGE_NAME variable into vars
|
||||
if (!installOptions.variables.PACKAGE_NAME) {
|
||||
installOptions.variables.PACKAGE_NAME = project.getPackageName();
|
||||
this.locations = {
|
||||
root: this.root,
|
||||
www: path.join(appMain, 'assets', 'www'),
|
||||
res: appRes,
|
||||
platformWww: path.join(this.root, 'platform_www'),
|
||||
configXml: path.join(appRes, 'xml', 'config.xml'),
|
||||
defaultConfigXml: path.join(this.root, 'cordova', 'defaults.xml'),
|
||||
strings: path.join(appRes, 'values', 'strings.xml'),
|
||||
manifest: path.join(appMain, 'AndroidManifest.xml'),
|
||||
build: path.join(this.root, 'build'),
|
||||
javaSrc: path.join(appMain, 'java')
|
||||
};
|
||||
}
|
||||
|
||||
return PluginManager.get(this.platform, this.locations, project)
|
||||
.addPlugin(plugin, installOptions)
|
||||
.then(function () {
|
||||
/**
|
||||
* Gets a CordovaPlatform object, that represents the platform structure.
|
||||
*
|
||||
* @return {CordovaPlatform} A structure that contains the description of
|
||||
* platform's file structure and other properties of platform.
|
||||
*/
|
||||
getPlatformInfo () {
|
||||
var result = {};
|
||||
result.locations = this.locations;
|
||||
result.root = this.root;
|
||||
result.name = this.platform;
|
||||
result.version = Api.version();
|
||||
result.projectConfig = this._config;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates installed platform with provided www assets and new app
|
||||
* configuration. This method is required for CLI workflow and will be called
|
||||
* each time before build, so the changes, made to app configuration and www
|
||||
* code, will be applied to platform.
|
||||
*
|
||||
* @param {CordovaProject} cordovaProject A CordovaProject instance, that defines a
|
||||
* project structure and configuration, that should be applied to platform
|
||||
* (contains project's www location and ConfigParser instance for project's
|
||||
* config).
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError instance.
|
||||
*/
|
||||
prepare (cordovaProject, prepareOptions) {
|
||||
cordovaProject.projectConfig = new ConfigParser(cordovaProject.locations.rootConfigXml || cordovaProject.projectConfig.path);
|
||||
|
||||
return prepare.call(this, cordovaProject, prepareOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a new plugin into platform. This method only copies non-www files
|
||||
* (sources, libs, etc.) to platform. It also doesn't resolves the
|
||||
* dependencies of plugin. Both of handling of www files, such as assets and
|
||||
* js-files and resolving dependencies are the responsibility of caller.
|
||||
*
|
||||
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
|
||||
* that will be installed.
|
||||
* @param {Object} installOptions An options object. Possible options below:
|
||||
* @param {Boolean} installOptions.link: Flag that specifies that plugin
|
||||
* sources will be symlinked to app's directory instead of copying (if
|
||||
* possible).
|
||||
* @param {Object} installOptions.variables An object that represents
|
||||
* variables that will be used to install plugin. See more details on plugin
|
||||
* variables in documentation:
|
||||
* https://cordova.apache.org/docs/en/4.0.0/plugin_ref_spec.md.html
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError instance.
|
||||
*/
|
||||
addPlugin (plugin, installOptions) {
|
||||
var project = AndroidProject.getProjectFile(this.root);
|
||||
var self = this;
|
||||
|
||||
installOptions = installOptions || {};
|
||||
installOptions.variables = installOptions.variables || {};
|
||||
// Add PACKAGE_NAME variable into vars
|
||||
if (!installOptions.variables.PACKAGE_NAME) {
|
||||
installOptions.variables.PACKAGE_NAME = project.getPackageName();
|
||||
}
|
||||
|
||||
return Promise.resolve().then(function () {
|
||||
return PluginManager.get(self.platform, self.locations, project).addPlugin(plugin, installOptions);
|
||||
}).then(function () {
|
||||
if (plugin.getFrameworks(this.platform).length === 0) return;
|
||||
|
||||
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
|
||||
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
|
||||
// This should pick the correct builder, not just get gradle
|
||||
require('./lib/builders/builders').getBuilder().prepBuildFiles();
|
||||
}.bind(this))
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
.thenResolve(true);
|
||||
};
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
.then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an installed plugin from platform.
|
||||
*
|
||||
* Since method accepts PluginInfo instance as input parameter instead of plugin
|
||||
* id, caller shoud take care of managing/storing PluginInfo instances for
|
||||
* future uninstalls.
|
||||
*
|
||||
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
|
||||
* that will be installed.
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError instance.
|
||||
*/
|
||||
Api.prototype.removePlugin = function (plugin, uninstallOptions) {
|
||||
var project = AndroidProject.getProjectFile(this.root);
|
||||
return PluginManager.get(this.platform, this.locations, project)
|
||||
.removePlugin(plugin, uninstallOptions)
|
||||
.then(function () {
|
||||
if (plugin.getFrameworks(this.platform).length === 0) return;
|
||||
/**
|
||||
* Removes an installed plugin from platform.
|
||||
*
|
||||
* Since method accepts PluginInfo instance as input parameter instead of plugin
|
||||
* id, caller shoud take care of managing/storing PluginInfo instances for
|
||||
* future uninstalls.
|
||||
*
|
||||
* @param {PluginInfo} plugin A PluginInfo instance that represents plugin
|
||||
* that will be installed.
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError instance.
|
||||
*/
|
||||
removePlugin (plugin, uninstallOptions) {
|
||||
var project = AndroidProject.getProjectFile(this.root);
|
||||
|
||||
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
|
||||
require('./lib/builders/builders').getBuilder('gradle').prepBuildFiles();
|
||||
}.bind(this))
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
.thenResolve(true);
|
||||
};
|
||||
if (uninstallOptions && uninstallOptions.usePlatformWww === true) {
|
||||
uninstallOptions.usePlatformWww = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an application package for current platform.
|
||||
*
|
||||
* @param {Object} buildOptions A build options. This object's structure is
|
||||
* highly depends on platform's specific. The most common options are:
|
||||
* @param {Boolean} buildOptions.debug Indicates that packages should be
|
||||
* built with debug configuration. This is set to true by default unless the
|
||||
* 'release' option is not specified.
|
||||
* @param {Boolean} buildOptions.release Indicates that packages should be
|
||||
* built with release configuration. If not set to true, debug configuration
|
||||
* will be used.
|
||||
* @param {Boolean} buildOptions.device Specifies that built app is intended
|
||||
* to run on device
|
||||
* @param {Boolean} buildOptions.emulator: Specifies that built app is
|
||||
* intended to run on emulator
|
||||
* @param {String} buildOptions.target Specifies the device id that will be
|
||||
* used to run built application.
|
||||
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
|
||||
* dry-run call, so no build artifacts will be produced.
|
||||
* @param {String[]} buildOptions.archs Specifies chip architectures which
|
||||
* app packages should be built for. List of valid architectures is depends on
|
||||
* platform.
|
||||
* @param {String} buildOptions.buildConfig The path to build configuration
|
||||
* file. The format of this file is depends on platform.
|
||||
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
|
||||
* passed to `build` command. The purpose of this property is to pass a
|
||||
* platform-specific arguments, and eventually let platform define own
|
||||
* arguments processing logic.
|
||||
*
|
||||
* @return {Promise<Object[]>} A promise either fulfilled with an array of build
|
||||
* artifacts (application packages) if package was built successfully,
|
||||
* or rejected with CordovaError. The resultant build artifact objects is not
|
||||
* strictly typed and may conatin arbitrary set of fields as in sample below.
|
||||
*
|
||||
* {
|
||||
* architecture: 'x86',
|
||||
* buildType: 'debug',
|
||||
* path: '/path/to/build',
|
||||
* type: 'app'
|
||||
* }
|
||||
*
|
||||
* The return value in most cases will contain only one item but in some cases
|
||||
* there could be multiple items in output array, e.g. when multiple
|
||||
* arhcitectures is specified.
|
||||
*/
|
||||
Api.prototype.build = function (buildOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run()
|
||||
.then(function () {
|
||||
return require('./lib/build').run.call(self, buildOptions);
|
||||
})
|
||||
.then(function (buildResults) {
|
||||
// Cast build result to array of build artifacts
|
||||
return buildResults.apkPaths.map(function (apkPath) {
|
||||
return {
|
||||
buildType: buildResults.buildType,
|
||||
buildMethod: buildResults.buildMethod,
|
||||
path: apkPath,
|
||||
type: 'apk'
|
||||
};
|
||||
return PluginManager.get(this.platform, this.locations, project)
|
||||
.removePlugin(plugin, uninstallOptions)
|
||||
.then(function () {
|
||||
if (plugin.getFrameworks(this.platform).length === 0) return;
|
||||
|
||||
selfEvents.emit('verbose', 'Updating build files since android plugin contained <framework>');
|
||||
require('./lib/builders/builders').getBuilder().prepBuildFiles();
|
||||
}.bind(this))
|
||||
// CB-11022 Return truthy value to prevent running prepare after
|
||||
.then(() => true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an application package for current platform.
|
||||
*
|
||||
* @param {Object} buildOptions A build options. This object's structure is
|
||||
* highly depends on platform's specific. The most common options are:
|
||||
* @param {Boolean} buildOptions.debug Indicates that packages should be
|
||||
* built with debug configuration. This is set to true by default unless the
|
||||
* 'release' option is not specified.
|
||||
* @param {Boolean} buildOptions.release Indicates that packages should be
|
||||
* built with release configuration. If not set to true, debug configuration
|
||||
* will be used.
|
||||
* @param {Boolean} buildOptions.device Specifies that built app is intended
|
||||
* to run on device
|
||||
* @param {Boolean} buildOptions.emulator: Specifies that built app is
|
||||
* intended to run on emulator
|
||||
* @param {String} buildOptions.target Specifies the device id that will be
|
||||
* used to run built application.
|
||||
* @param {Boolean} buildOptions.nobuild Indicates that this should be a
|
||||
* dry-run call, so no build artifacts will be produced.
|
||||
* @param {String[]} buildOptions.archs Specifies chip architectures which
|
||||
* app packages should be built for. List of valid architectures is depends on
|
||||
* platform.
|
||||
* @param {String} buildOptions.buildConfig The path to build configuration
|
||||
* file. The format of this file is depends on platform.
|
||||
* @param {String[]} buildOptions.argv Raw array of command-line arguments,
|
||||
* passed to `build` command. The purpose of this property is to pass a
|
||||
* platform-specific arguments, and eventually let platform define own
|
||||
* arguments processing logic.
|
||||
*
|
||||
* @return {Promise<Object[]>} A promise either fulfilled with an array of build
|
||||
* artifacts (application packages) if package was built successfully,
|
||||
* or rejected with CordovaError. The resultant build artifact objects is not
|
||||
* strictly typed and may conatin arbitrary set of fields as in sample below.
|
||||
*
|
||||
* {
|
||||
* architecture: 'x86',
|
||||
* buildType: 'debug',
|
||||
* path: '/path/to/build',
|
||||
* type: 'app'
|
||||
* }
|
||||
*
|
||||
* The return value in most cases will contain only one item but in some cases
|
||||
* there could be multiple items in output array, e.g. when multiple
|
||||
* arhcitectures is specified.
|
||||
*/
|
||||
build (buildOptions) {
|
||||
var self = this;
|
||||
|
||||
return require('./lib/check_reqs').run().then(function () {
|
||||
return require('./lib/build').run.call(self, buildOptions);
|
||||
}).then(function (buildResults) {
|
||||
// Cast build result to array of build artifacts
|
||||
return buildResults.paths.map(function (apkPath) {
|
||||
return {
|
||||
buildType: buildResults.buildType,
|
||||
buildMethod: buildResults.buildMethod,
|
||||
path: apkPath,
|
||||
type: path.extname(apkPath).replace(/\./g, '')
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an application package for current platform and runs it on
|
||||
* specified/default device. If no 'device'/'emulator'/'target' options are
|
||||
* specified, then tries to run app on default device if connected, otherwise
|
||||
* runs the app on emulator.
|
||||
*
|
||||
* @param {Object} runOptions An options object. The structure is the same
|
||||
* as for build options.
|
||||
*
|
||||
* @return {Promise} A promise either fulfilled if package was built and ran
|
||||
* successfully, or rejected with CordovaError.
|
||||
*/
|
||||
Api.prototype.run = function(runOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run()
|
||||
.then(function () {
|
||||
return require('./lib/run').run.call(self, runOptions);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Builds an application package for current platform and runs it on
|
||||
* specified/default device. If no 'device'/'emulator'/'target' options are
|
||||
* specified, then tries to run app on default device if connected, otherwise
|
||||
* runs the app on emulator.
|
||||
*
|
||||
* @param {Object} runOptions An options object. The structure is the same
|
||||
* as for build options.
|
||||
*
|
||||
* @return {Promise} A promise either fulfilled if package was built and ran
|
||||
* successfully, or rejected with CordovaError.
|
||||
*/
|
||||
run (runOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run().then(function () {
|
||||
return require('./lib/run').run.call(self, runOptions);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans out the build artifacts from platform's directory.
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError.
|
||||
*/
|
||||
Api.prototype.clean = function(cleanOptions) {
|
||||
var self = this;
|
||||
return require('./lib/check_reqs').run()
|
||||
.then(function () {
|
||||
return require('./lib/build').runClean.call(self, cleanOptions);
|
||||
})
|
||||
.then(function () {
|
||||
return require('./lib/prepare').clean.call(self, cleanOptions);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Cleans out the build artifacts from platform's directory, and also
|
||||
* cleans out the platform www directory if called without options specified.
|
||||
*
|
||||
* @return {Promise} Return a promise either fulfilled, or rejected with
|
||||
* CordovaError.
|
||||
*/
|
||||
clean (cleanOptions) {
|
||||
var self = this;
|
||||
// This will lint, checking for null won't
|
||||
if (typeof cleanOptions === 'undefined') {
|
||||
cleanOptions = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a requirements check for current platform. Each platform defines its
|
||||
* own set of requirements, which should be resolved before platform can be
|
||||
* built successfully.
|
||||
*
|
||||
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
|
||||
* objects for current platform.
|
||||
*/
|
||||
Api.prototype.requirements = function() {
|
||||
return require('./lib/check_reqs').check_all();
|
||||
};
|
||||
return require('./lib/check_reqs').run().then(function () {
|
||||
return require('./lib/build').runClean.call(self, cleanOptions);
|
||||
}).then(function () {
|
||||
return require('./lib/prepare').clean.call(self, cleanOptions);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a requirements check for current platform. Each platform defines its
|
||||
* own set of requirements, which should be resolved before platform can be
|
||||
* built successfully.
|
||||
*
|
||||
* @return {Promise<Requirement[]>} Promise, resolved with set of Requirement
|
||||
* objects for current platform.
|
||||
*/
|
||||
requirements () {
|
||||
return require('./lib/check_reqs').check_all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs platform to specified directory and creates a platform project.
|
||||
*
|
||||
* @param {String} destination Destination directory, where insatll platform to
|
||||
* @param {ConfigParser} [config] ConfgiParser instance, used to retrieve
|
||||
* project creation options, such as package id and project name.
|
||||
* @param {Object} [options] An options object. The most common options are:
|
||||
* @param {String} [options.customTemplate] A path to custom template, that
|
||||
* should override the default one from platform.
|
||||
* @param {Boolean} [options.link] Flag that indicates that platform's
|
||||
* sources will be linked to installed platform instead of copying.
|
||||
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
|
||||
* logging purposes. If no EventEmitter provided, all events will be logged to
|
||||
* console
|
||||
*
|
||||
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
|
||||
* instance or rejected with CordovaError.
|
||||
*/
|
||||
static createPlatform (destination, config, options, events) {
|
||||
events = setupEvents(events);
|
||||
var result;
|
||||
try {
|
||||
result = require('../../lib/create').create(destination, config, options, events).then(function (destination) {
|
||||
return new Api(PLATFORM, destination, events);
|
||||
});
|
||||
} catch (e) {
|
||||
events.emit('error', 'createPlatform is not callable from the android project API.');
|
||||
throw (e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates already installed platform.
|
||||
*
|
||||
* @param {String} destination Destination directory, where platform installed
|
||||
* @param {Object} [options] An options object. The most common options are:
|
||||
* @param {String} [options.customTemplate] A path to custom template, that
|
||||
* should override the default one from platform.
|
||||
* @param {Boolean} [options.link] Flag that indicates that platform's
|
||||
* sources will be linked to installed platform instead of copying.
|
||||
* @param {EventEmitter} [events] An EventEmitter instance that will be used for
|
||||
* logging purposes. If no EventEmitter provided, all events will be logged to
|
||||
* console
|
||||
*
|
||||
* @return {Promise<PlatformApi>} Promise either fulfilled with PlatformApi
|
||||
* instance or rejected with CordovaError.
|
||||
*/
|
||||
static updatePlatform (destination, options, events) {
|
||||
events = setupEvents(events);
|
||||
var result;
|
||||
try {
|
||||
result = require('../../lib/create').update(destination, options, events).then(function (destination) {
|
||||
return new Api(PLATFORM, destination, events);
|
||||
});
|
||||
} catch (e) {
|
||||
events.emit('error', 'updatePlatform is not callable from the android project API, you will need to do this manually.');
|
||||
throw (e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static version () {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Api;
|
||||
|
||||
@@ -19,24 +19,25 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var args = process.argv;
|
||||
var args = process.argv;
|
||||
var Api = require('./Api');
|
||||
var nopt = require('nopt');
|
||||
var path = require('path');
|
||||
|
||||
// Support basic help commands
|
||||
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0)
|
||||
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(args[2]) >= 0) {
|
||||
require('./lib/build').help();
|
||||
}
|
||||
|
||||
// Do some basic argument parsing
|
||||
var buildOpts = nopt({
|
||||
'verbose' : Boolean,
|
||||
'silent' : Boolean,
|
||||
'debug' : Boolean,
|
||||
'release' : Boolean,
|
||||
'nobuild': Boolean,
|
||||
'buildConfig' : path
|
||||
}, { 'd' : '--verbose' });
|
||||
verbose: Boolean,
|
||||
silent: Boolean,
|
||||
debug: Boolean,
|
||||
release: Boolean,
|
||||
nobuild: Boolean,
|
||||
buildConfig: path
|
||||
}, { d: '--verbose' });
|
||||
|
||||
// Make buildOptions compatible with PlatformApi build method spec
|
||||
buildOpts.argv = buildOpts.argv.original;
|
||||
@@ -44,7 +45,7 @@ buildOpts.argv = buildOpts.argv.original;
|
||||
require('./loggingHelper').adjustLoggerLevel(buildOpts);
|
||||
|
||||
new Api().build(buildOpts)
|
||||
.catch(function(err) {
|
||||
console.error(err.stack);
|
||||
process.exit(2);
|
||||
});
|
||||
.catch(function (err) {
|
||||
console.error(err.stack);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
*/
|
||||
|
||||
var Api = require('./Api');
|
||||
var path = require('path');
|
||||
var path = require('path');
|
||||
var nopt = require('nopt');
|
||||
|
||||
// Support basic help commands
|
||||
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
|
||||
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
||||
console.log('Cleans the project directory.');
|
||||
process.exit(0);
|
||||
@@ -32,9 +32,9 @@ if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >=
|
||||
|
||||
// Do some basic argument parsing
|
||||
var opts = nopt({
|
||||
'verbose' : Boolean,
|
||||
'silent' : Boolean
|
||||
}, { 'd' : '--verbose' });
|
||||
verbose: Boolean,
|
||||
silent: Boolean
|
||||
}, { d: '--verbose' });
|
||||
|
||||
// Make buildOptions compatible with PlatformApi clean method spec
|
||||
opts.argv = opts.argv.original;
|
||||
@@ -45,7 +45,7 @@ opts.noPrepare = true;
|
||||
require('./loggingHelper').adjustLoggerLevel(opts);
|
||||
|
||||
new Api().clean(opts)
|
||||
.catch(function(err) {
|
||||
console.error(err.stack);
|
||||
process.exit(2);
|
||||
});
|
||||
.catch(function (err) {
|
||||
console.error(err.stack);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
32
bin/templates/cordova/lib/Adb.js
vendored
@@ -17,19 +17,18 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q');
|
||||
var os = require('os');
|
||||
var execa = require('execa');
|
||||
var events = require('cordova-common').events;
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var Adb = {};
|
||||
|
||||
function isDevice(line) {
|
||||
function isDevice (line) {
|
||||
return line.match(/\w+\tdevice/) && !line.match(/emulator/);
|
||||
}
|
||||
|
||||
function isEmulator(line) {
|
||||
function isEmulator (line) {
|
||||
return line.match(/device/) && line.match(/emulator/);
|
||||
}
|
||||
|
||||
@@ -44,8 +43,7 @@ function isEmulator(line) {
|
||||
* devices/emulators
|
||||
*/
|
||||
Adb.devices = function (opts) {
|
||||
return spawn('adb', ['devices'], {cwd: os.tmpdir()})
|
||||
.then(function(output) {
|
||||
return execa('adb', ['devices'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
|
||||
return output.split('\n').filter(function (line) {
|
||||
// Filter out either real devices or emulators, depending on options
|
||||
return (line && opts && opts.emulators) ? isEmulator(line) : isDevice(line);
|
||||
@@ -59,8 +57,7 @@ Adb.install = function (target, packagePath, opts) {
|
||||
events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
|
||||
var args = ['-s', target, 'install'];
|
||||
if (opts && opts.replace) args.push('-r');
|
||||
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()})
|
||||
.then(function(output) {
|
||||
return execa('adb', args.concat(packagePath), { cwd: os.tmpdir() }).then(({ stdout: output }) => {
|
||||
// 'adb install' seems to always returns no error, even if installation fails
|
||||
// so we catching output to detect installation failure
|
||||
if (output.match(/Failure/)) {
|
||||
@@ -72,33 +69,30 @@ Adb.install = function (target, packagePath, opts) {
|
||||
'\nEither uninstall an app or increment the versionCode.';
|
||||
}
|
||||
|
||||
return Q.reject(new CordovaError('Failed to install apk to device: ' + output));
|
||||
return Promise.reject(new CordovaError('Failed to install apk to device: ' + output));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Adb.uninstall = function (target, packageId) {
|
||||
events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
|
||||
return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()});
|
||||
return execa('adb', ['-s', target, 'uninstall', packageId], { cwd: os.tmpdir() }).then(({ stdout }) => stdout);
|
||||
};
|
||||
|
||||
Adb.shell = function (target, shellCommand) {
|
||||
events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
|
||||
var args = ['-s', target, 'shell'];
|
||||
shellCommand = shellCommand.split(/\s+/);
|
||||
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()})
|
||||
.catch(function (output) {
|
||||
return Q.reject(new CordovaError('Failed to execute shell command "' +
|
||||
shellCommand + '"" on device: ' + output));
|
||||
});
|
||||
return execa('adb', args.concat(shellCommand), { cwd: os.tmpdir() })
|
||||
.then(({ stdout }) => stdout)
|
||||
.catch(error => Promise.reject(new CordovaError(`Failed to execute shell command "${shellCommand}" on device: ${error}`)));
|
||||
};
|
||||
|
||||
Adb.start = function (target, activityName) {
|
||||
events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
|
||||
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
|
||||
.catch(function (output) {
|
||||
return Q.reject(new CordovaError('Failed to start application "' +
|
||||
activityName + '"" on device: ' + output));
|
||||
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName).catch((error) => {
|
||||
return Promise.reject(new CordovaError('Failed to start application "' +
|
||||
activityName + '"" on device: ' + error));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
233
bin/templates/cordova/lib/AndroidManifest.js
vendored
@@ -18,144 +18,111 @@
|
||||
*/
|
||||
|
||||
var fs = require('fs');
|
||||
var et = require('elementtree');
|
||||
var xml= require('cordova-common').xmlHelpers;
|
||||
var xml = require('cordova-common').xmlHelpers;
|
||||
|
||||
var DEFAULT_ORIENTATION = 'default';
|
||||
|
||||
/** Wraps an AndroidManifest file */
|
||||
function AndroidManifest(path) {
|
||||
this.path = path;
|
||||
this.doc = xml.parseElementtreeSync(path);
|
||||
if (this.doc.getroot().tag !== 'manifest') {
|
||||
throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
|
||||
class AndroidManifest {
|
||||
constructor (path) {
|
||||
this.path = path;
|
||||
this.doc = xml.parseElementtreeSync(path);
|
||||
if (this.doc.getroot().tag !== 'manifest') {
|
||||
throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
|
||||
}
|
||||
}
|
||||
|
||||
getVersionName () {
|
||||
return this.doc.getroot().attrib['android:versionName'];
|
||||
}
|
||||
|
||||
setVersionName (versionName) {
|
||||
this.doc.getroot().attrib['android:versionName'] = versionName;
|
||||
return this;
|
||||
}
|
||||
|
||||
getVersionCode () {
|
||||
return this.doc.getroot().attrib['android:versionCode'];
|
||||
}
|
||||
|
||||
setVersionCode (versionCode) {
|
||||
this.doc.getroot().attrib['android:versionCode'] = versionCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
getPackageId () {
|
||||
return this.doc.getroot().attrib.package;
|
||||
}
|
||||
|
||||
setPackageId (pkgId) {
|
||||
this.doc.getroot().attrib.package = pkgId;
|
||||
return this;
|
||||
}
|
||||
|
||||
getActivity () {
|
||||
var activity = this.doc.getroot().find('./application/activity');
|
||||
return {
|
||||
getName: function () {
|
||||
return activity.attrib['android:name'];
|
||||
},
|
||||
setName: function (name) {
|
||||
if (!name) {
|
||||
delete activity.attrib['android:name'];
|
||||
} else {
|
||||
activity.attrib['android:name'] = name;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
getOrientation: function () {
|
||||
return activity.attrib['android:screenOrientation'];
|
||||
},
|
||||
setOrientation: function (orientation) {
|
||||
if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) {
|
||||
delete activity.attrib['android:screenOrientation'];
|
||||
} else {
|
||||
activity.attrib['android:screenOrientation'] = orientation;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
getLaunchMode: function () {
|
||||
return activity.attrib['android:launchMode'];
|
||||
},
|
||||
setLaunchMode: function (launchMode) {
|
||||
if (!launchMode) {
|
||||
delete activity.attrib['android:launchMode'];
|
||||
} else {
|
||||
activity.attrib['android:launchMode'] = launchMode;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
getDebuggable () {
|
||||
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
|
||||
}
|
||||
|
||||
setDebuggable (value) {
|
||||
var application = this.doc.getroot().find('./application');
|
||||
if (value) {
|
||||
application.attrib['android:debuggable'] = 'true';
|
||||
} else {
|
||||
// The default value is "false", so we can remove attribute at all.
|
||||
delete application.attrib['android:debuggable'];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes manifest to disk syncronously. If filename is specified, then manifest
|
||||
* will be written to that file
|
||||
*
|
||||
* @param {String} [destPath] File to write manifest to. If omitted,
|
||||
* manifest will be written to file it has been read from.
|
||||
*/
|
||||
write (destPath) {
|
||||
fs.writeFileSync(destPath || this.path, this.doc.write({ indent: 4 }), 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
AndroidManifest.prototype.getVersionName = function() {
|
||||
return this.doc.getroot().attrib['android:versionName'];
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setVersionName = function(versionName) {
|
||||
this.doc.getroot().attrib['android:versionName'] = versionName;
|
||||
return this;
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.getVersionCode = function() {
|
||||
return this.doc.getroot().attrib['android:versionCode'];
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setVersionCode = function(versionCode) {
|
||||
this.doc.getroot().attrib['android:versionCode'] = versionCode;
|
||||
return this;
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.getPackageId = function() {
|
||||
/*jshint -W069 */
|
||||
return this.doc.getroot().attrib['package'];
|
||||
/*jshint +W069 */
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setPackageId = function(pkgId) {
|
||||
/*jshint -W069 */
|
||||
this.doc.getroot().attrib['package'] = pkgId;
|
||||
/*jshint +W069 */
|
||||
return this;
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.getActivity = function() {
|
||||
var activity = this.doc.getroot().find('./application/activity');
|
||||
return {
|
||||
getName: function () {
|
||||
return activity.attrib['android:name'];
|
||||
},
|
||||
setName: function (name) {
|
||||
if (!name) {
|
||||
delete activity.attrib['android:name'];
|
||||
} else {
|
||||
activity.attrib['android:name'] = name;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
getOrientation: function () {
|
||||
return activity.attrib['android:screenOrientation'];
|
||||
},
|
||||
setOrientation: function (orientation) {
|
||||
if (!orientation || orientation.toLowerCase() === DEFAULT_ORIENTATION) {
|
||||
delete activity.attrib['android:screenOrientation'];
|
||||
} else {
|
||||
activity.attrib['android:screenOrientation'] = orientation;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
getLaunchMode: function () {
|
||||
return activity.attrib['android:launchMode'];
|
||||
},
|
||||
setLaunchMode: function (launchMode) {
|
||||
if (!launchMode) {
|
||||
delete activity.attrib['android:launchMode'];
|
||||
} else {
|
||||
activity.attrib['android:launchMode'] = launchMode;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
['minSdkVersion', 'maxSdkVersion', 'targetSdkVersion']
|
||||
.forEach(function(sdkPrefName) {
|
||||
// Copy variable reference to avoid closure issues
|
||||
var prefName = sdkPrefName;
|
||||
|
||||
AndroidManifest.prototype['get' + capitalize(prefName)] = function() {
|
||||
var usesSdk = this.doc.getroot().find('./uses-sdk');
|
||||
return usesSdk && usesSdk.attrib['android:' + prefName];
|
||||
};
|
||||
|
||||
AndroidManifest.prototype['set' + capitalize(prefName)] = function(prefValue) {
|
||||
var usesSdk = this.doc.getroot().find('./uses-sdk');
|
||||
|
||||
if (!usesSdk && prefValue) { // if there is no required uses-sdk element, we should create it first
|
||||
usesSdk = new et.Element('uses-sdk');
|
||||
this.doc.getroot().append(usesSdk);
|
||||
}
|
||||
|
||||
if (prefValue) {
|
||||
usesSdk.attrib['android:' + prefName] = prefValue;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
AndroidManifest.prototype.getDebuggable = function() {
|
||||
return this.doc.getroot().find('./application').attrib['android:debuggable'] === 'true';
|
||||
};
|
||||
|
||||
AndroidManifest.prototype.setDebuggable = function(value) {
|
||||
var application = this.doc.getroot().find('./application');
|
||||
if (value) {
|
||||
application.attrib['android:debuggable'] = 'true';
|
||||
} else {
|
||||
// The default value is "false", so we can remove attribute at all.
|
||||
delete application.attrib['android:debuggable'];
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes manifest to disk syncronously. If filename is specified, then manifest
|
||||
* will be written to that file
|
||||
*
|
||||
* @param {String} [destPath] File to write manifest to. If omitted,
|
||||
* manifest will be written to file it has been read from.
|
||||
*/
|
||||
AndroidManifest.prototype.write = function(destPath) {
|
||||
fs.writeFileSync(destPath || this.path, this.doc.write({indent: 4}), 'utf-8');
|
||||
};
|
||||
|
||||
module.exports = AndroidManifest;
|
||||
|
||||
function capitalize (str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
255
bin/templates/cordova/lib/AndroidProject.js
vendored
@@ -25,16 +25,15 @@ var pluginHandlers = require('./pluginHandlers');
|
||||
|
||||
var projectFileCache = {};
|
||||
|
||||
function addToPropertyList(projectProperties, key, value) {
|
||||
function addToPropertyList (projectProperties, key, value) {
|
||||
var i = 1;
|
||||
while (projectProperties.get(key + '.' + i))
|
||||
i++;
|
||||
while (projectProperties.get(key + '.' + i)) { i++; }
|
||||
|
||||
projectProperties.set(key + '.' + i, value);
|
||||
projectProperties.dirty = true;
|
||||
}
|
||||
|
||||
function removeFromPropertyList(projectProperties, key, value) {
|
||||
function removeFromPropertyList (projectProperties, key, value) {
|
||||
var i = 1;
|
||||
var currentValue;
|
||||
while ((currentValue = projectProperties.get(key + '.' + i))) {
|
||||
@@ -53,141 +52,151 @@ function removeFromPropertyList(projectProperties, key, value) {
|
||||
|
||||
function getRelativeLibraryPath (parentDir, subDir) {
|
||||
var libraryPath = path.relative(parentDir, subDir);
|
||||
return (path.sep == '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
|
||||
return (path.sep === '\\') ? libraryPath.replace(/\\/g, '/') : libraryPath;
|
||||
}
|
||||
|
||||
function AndroidProject(projectDir) {
|
||||
this._propertiesEditors = {};
|
||||
this._subProjectDirs = {};
|
||||
this._dirty = false;
|
||||
this.projectDir = projectDir;
|
||||
this.platformWww = path.join(this.projectDir, 'platform_www');
|
||||
this.www = path.join(this.projectDir, 'assets/www');
|
||||
}
|
||||
|
||||
AndroidProject.getProjectFile = function (projectDir) {
|
||||
if (!projectFileCache[projectDir]) {
|
||||
projectFileCache[projectDir] = new AndroidProject(projectDir);
|
||||
class AndroidProject {
|
||||
constructor (projectDir) {
|
||||
this._propertiesEditors = {};
|
||||
this._subProjectDirs = {};
|
||||
this._dirty = false;
|
||||
this.projectDir = projectDir;
|
||||
this.platformWww = path.join(this.projectDir, 'platform_www');
|
||||
this.www = path.join(this.projectDir, 'app/src/main/assets/www');
|
||||
}
|
||||
|
||||
return projectFileCache[projectDir];
|
||||
};
|
||||
|
||||
AndroidProject.purgeCache = function (projectDir) {
|
||||
if (projectDir) {
|
||||
delete projectFileCache[projectDir];
|
||||
} else {
|
||||
projectFileCache = {};
|
||||
/**
|
||||
* Reads the package name out of the Android Manifest file
|
||||
*
|
||||
* @param {String} projectDir The absolute path to the directory containing the project
|
||||
* @return {String} The name of the package
|
||||
*/
|
||||
getPackageName () {
|
||||
var manifestPath = path.join(this.projectDir, 'app/src/main/AndroidManifest.xml');
|
||||
return new AndroidManifest(manifestPath).getPackageId();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads the package name out of the Android Manifest file
|
||||
*
|
||||
* @param {String} projectDir The absolute path to the directory containing the project
|
||||
*
|
||||
* @return {String} The name of the package
|
||||
*/
|
||||
AndroidProject.prototype.getPackageName = function() {
|
||||
return new AndroidManifest(path.join(this.projectDir, 'AndroidManifest.xml')).getPackageId();
|
||||
};
|
||||
|
||||
AndroidProject.prototype.getCustomSubprojectRelativeDir = function(plugin_id, src) {
|
||||
// All custom subprojects are prefixed with the last portion of the package id.
|
||||
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
|
||||
var packageName = this.getPackageName();
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
var prefix = packageName.substring(lastDotIndex + 1);
|
||||
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
|
||||
return subRelativeDir;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.addSubProject = function(parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var subProjectFile = path.resolve(subDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
|
||||
if (fs.existsSync(subProjectFile)) {
|
||||
var subProperties = this._getPropertiesFile(subProjectFile);
|
||||
subProperties.set('target', parentProperties.get('target'));
|
||||
subProperties.dirty = true;
|
||||
this._subProjectDirs[subDir] = true;
|
||||
getCustomSubprojectRelativeDir (plugin_id, src) {
|
||||
// All custom subprojects are prefixed with the last portion of the package id.
|
||||
// This is to avoid collisions when opening multiple projects in Eclipse that have subprojects with the same name.
|
||||
var packageName = this.getPackageName();
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
var prefix = packageName.substring(lastDotIndex + 1);
|
||||
var subRelativeDir = path.join(plugin_id, prefix + '-' + path.basename(src));
|
||||
return subRelativeDir;
|
||||
}
|
||||
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
|
||||
|
||||
this._dirty = true;
|
||||
};
|
||||
addSubProject (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var subProjectFile = path.resolve(subDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
// TODO: Setting the target needs to happen only for pre-3.7.0 projects
|
||||
if (fs.existsSync(subProjectFile)) {
|
||||
var subProperties = this._getPropertiesFile(subProjectFile);
|
||||
subProperties.set('target', parentProperties.get('target'));
|
||||
subProperties.dirty = true;
|
||||
this._subProjectDirs[subDir] = true;
|
||||
}
|
||||
addToPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
|
||||
|
||||
AndroidProject.prototype.removeSubProject = function(parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
|
||||
delete this._subProjectDirs[subDir];
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.addGradleReference = function(parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.removeGradleReference = function(parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.addSystemLibrary = function(parentDir, value) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
addToPropertyList(parentProperties, 'cordova.system.library', value);
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.removeSystemLibrary = function(parentDir, value) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
|
||||
this._dirty = true;
|
||||
};
|
||||
|
||||
AndroidProject.prototype.write = function() {
|
||||
if (!this._dirty) {
|
||||
return;
|
||||
this._dirty = true;
|
||||
}
|
||||
this._dirty = false;
|
||||
|
||||
for (var filename in this._propertiesEditors) {
|
||||
var editor = this._propertiesEditors[filename];
|
||||
if (editor.dirty) {
|
||||
fs.writeFileSync(filename, editor.toString());
|
||||
editor.dirty = false;
|
||||
removeSubProject (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'android.library.reference', getRelativeLibraryPath(parentDir, subDir));
|
||||
delete this._subProjectDirs[subDir];
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
addGradleReference (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
addToPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
removeGradleReference (parentDir, subDir) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'cordova.gradle.include', getRelativeLibraryPath(parentDir, subDir));
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
addSystemLibrary (parentDir, value) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
addToPropertyList(parentProperties, 'cordova.system.library', value);
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
removeSystemLibrary (parentDir, value) {
|
||||
var parentProjectFile = path.resolve(parentDir, 'project.properties');
|
||||
var parentProperties = this._getPropertiesFile(parentProjectFile);
|
||||
removeFromPropertyList(parentProperties, 'cordova.system.library', value);
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
write () {
|
||||
if (!this._dirty) {
|
||||
return;
|
||||
}
|
||||
this._dirty = false;
|
||||
|
||||
for (var filename in this._propertiesEditors) {
|
||||
var editor = this._propertiesEditors[filename];
|
||||
if (editor.dirty) {
|
||||
fs.writeFileSync(filename, editor.toString());
|
||||
editor.dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AndroidProject.prototype._getPropertiesFile = function (filename) {
|
||||
if (!this._propertiesEditors[filename]) {
|
||||
if (fs.existsSync(filename)) {
|
||||
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
|
||||
getInstaller (type) {
|
||||
return pluginHandlers.getInstaller(type);
|
||||
}
|
||||
|
||||
getUninstaller (type) {
|
||||
return pluginHandlers.getUninstaller(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* This checks if an Android project is clean or has old build artifacts
|
||||
*/
|
||||
isClean () {
|
||||
var build_path = path.join(this.projectDir, 'build');
|
||||
// If the build directory doesn't exist, it's clean
|
||||
return !(fs.existsSync(build_path));
|
||||
}
|
||||
|
||||
_getPropertiesFile (filename) {
|
||||
if (!this._propertiesEditors[filename]) {
|
||||
if (fs.existsSync(filename)) {
|
||||
this._propertiesEditors[filename] = properties_parser.createEditor(filename);
|
||||
} else {
|
||||
this._propertiesEditors[filename] = properties_parser.createEditor();
|
||||
}
|
||||
}
|
||||
|
||||
return this._propertiesEditors[filename];
|
||||
}
|
||||
|
||||
static getProjectFile (projectDir) {
|
||||
if (!projectFileCache[projectDir]) {
|
||||
projectFileCache[projectDir] = new AndroidProject(projectDir);
|
||||
}
|
||||
|
||||
return projectFileCache[projectDir];
|
||||
}
|
||||
|
||||
static purgeCache (projectDir) {
|
||||
if (projectDir) {
|
||||
delete projectFileCache[projectDir];
|
||||
} else {
|
||||
this._propertiesEditors[filename] = properties_parser.createEditor();
|
||||
projectFileCache = {};
|
||||
}
|
||||
}
|
||||
|
||||
return this._propertiesEditors[filename];
|
||||
};
|
||||
|
||||
AndroidProject.prototype.getInstaller = function (type) {
|
||||
return pluginHandlers.getInstaller(type);
|
||||
};
|
||||
|
||||
AndroidProject.prototype.getUninstaller = function (type) {
|
||||
return pluginHandlers.getUninstaller(type);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
module.exports = AndroidProject;
|
||||
|
||||
25
bin/templates/cordova/lib/PackageType.js
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const PackageType = {
|
||||
APK: 'apk',
|
||||
BUNDLE: 'bundle'
|
||||
};
|
||||
|
||||
module.exports = PackageType;
|
||||
101
bin/templates/cordova/lib/android_sdk.js
vendored
Executable file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const execa = require('execa');
|
||||
|
||||
var suffix_number_regex = /(\d+)$/;
|
||||
// Used for sorting Android targets, example strings to sort:
|
||||
// android-19
|
||||
// android-L
|
||||
// Google Inc.:Google APIs:20
|
||||
// Google Inc.:Glass Development Kit Preview:20
|
||||
// The idea is to sort based on largest "suffix" number - meaning the bigger
|
||||
// the number at the end, the more recent the target, the closer to the
|
||||
// start of the array.
|
||||
function sort_by_largest_numerical_suffix (a, b) {
|
||||
let suffix_a = a.match(suffix_number_regex);
|
||||
let suffix_b = b.match(suffix_number_regex);
|
||||
// If no number is detected (eg: preview version like android-R),
|
||||
// designate a suffix of 0 so it gets moved to the end
|
||||
suffix_a = suffix_a || ['0', '0'];
|
||||
suffix_b = suffix_b || ['0', '0'];
|
||||
// Return < zero, or > zero, based on which suffix is larger.
|
||||
return (parseInt(suffix_a[1]) > parseInt(suffix_b[1]) ? -1 : 1);
|
||||
}
|
||||
|
||||
module.exports.print_newest_available_sdk_target = function () {
|
||||
return module.exports.list_targets().then(function (targets) {
|
||||
targets.sort(sort_by_largest_numerical_suffix);
|
||||
console.log(targets[0]);
|
||||
});
|
||||
};
|
||||
|
||||
// Versions should not be represented as float, so we disable quote-props here
|
||||
/* eslint-disable quote-props */
|
||||
module.exports.version_string_to_api_level = {
|
||||
'4.0': 14,
|
||||
'4.0.3': 15,
|
||||
'4.1': 16,
|
||||
'4.2': 17,
|
||||
'4.3': 18,
|
||||
'4.4': 19,
|
||||
'4.4W': 20,
|
||||
'5.0': 21,
|
||||
'5.1': 22,
|
||||
'6.0': 23,
|
||||
'7.0': 24,
|
||||
'7.1.1': 25,
|
||||
'8.0': 26
|
||||
};
|
||||
/* eslint-enable quote-props */
|
||||
|
||||
function parse_targets (output) {
|
||||
var target_out = output.split('\n');
|
||||
var targets = [];
|
||||
for (var i = target_out.length - 1; i >= 0; i--) {
|
||||
if (target_out[i].match(/id:/)) { // if "id:" is in the line...
|
||||
targets.push(target_out[i].match(/"(.+)"/)[1]); // .. match whatever is in quotes.
|
||||
}
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
module.exports.list_targets_with_android = function () {
|
||||
return execa('android', ['list', 'target']).then(result => parse_targets(result.stdout));
|
||||
};
|
||||
|
||||
module.exports.list_targets_with_avdmanager = function () {
|
||||
return execa('avdmanager', ['list', 'target']).then(result => parse_targets(result.stdout));
|
||||
};
|
||||
|
||||
module.exports.list_targets = function () {
|
||||
return module.exports.list_targets_with_avdmanager().catch(function (err) {
|
||||
// If there's an error, like avdmanager could not be found, we can try
|
||||
// as a last resort, to run `android`, in case this is a super old
|
||||
// SDK installation.
|
||||
if (err && (err.code === 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) {
|
||||
return module.exports.list_targets_with_android();
|
||||
} else throw err;
|
||||
}).then(function (targets) {
|
||||
if (targets.length === 0) {
|
||||
return Promise.reject(new Error('No android targets (SDKs) installed!'));
|
||||
}
|
||||
return targets;
|
||||
});
|
||||
};
|
||||
231
bin/templates/cordova/lib/build.js
vendored
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,89 +17,82 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q'),
|
||||
path = require('path'),
|
||||
fs = require('fs'),
|
||||
nopt = require('nopt');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var nopt = require('nopt');
|
||||
|
||||
var Adb = require('./Adb');
|
||||
|
||||
var builders = require('./builders/builders');
|
||||
var events = require('cordova-common').events;
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
const execa = require('execa');
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var PackageType = require('./PackageType');
|
||||
|
||||
function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
module.exports.parseBuildOptions = parseOpts;
|
||||
function parseOpts (options, resolvedTarget, projectRoot) {
|
||||
options = options || {};
|
||||
options.argv = nopt({
|
||||
gradle: Boolean,
|
||||
ant: Boolean,
|
||||
prepenv: Boolean,
|
||||
versionCode: String,
|
||||
minSdkVersion: String,
|
||||
maxSdkVersion: String,
|
||||
targetSdkVersion: String,
|
||||
gradleArg: [String, Array],
|
||||
keystore: path,
|
||||
alias: String,
|
||||
storePassword: String,
|
||||
password: String,
|
||||
keystoreType: String
|
||||
keystoreType: String,
|
||||
packageType: String
|
||||
}, {}, options.argv, 0);
|
||||
|
||||
// Android Studio Build method is the default
|
||||
var ret = {
|
||||
buildType: options.release ? 'release' : 'debug',
|
||||
buildMethod: process.env.ANDROID_BUILD || 'gradle',
|
||||
prepEnv: options.argv.prepenv,
|
||||
arch: resolvedTarget && resolvedTarget.arch,
|
||||
extraArgs: []
|
||||
};
|
||||
|
||||
if (options.argv.ant || options.argv.gradle)
|
||||
ret.buildMethod = options.argv.ant ? 'ant' : 'gradle';
|
||||
|
||||
if (options.nobuild) ret.buildMethod = 'none';
|
||||
|
||||
if (options.argv.versionCode)
|
||||
ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode);
|
||||
|
||||
if (options.argv.minSdkVersion)
|
||||
ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion);
|
||||
|
||||
if (options.argv.versionCode) { ret.extraArgs.push('-PcdvVersionCode=' + options.argv.versionCode); }
|
||||
if (options.argv.minSdkVersion) { ret.extraArgs.push('-PcdvMinSdkVersion=' + options.argv.minSdkVersion); }
|
||||
if (options.argv.maxSdkVersion) { ret.extraArgs.push('-PcdvMaxSdkVersion=' + options.argv.maxSdkVersion); }
|
||||
if (options.argv.targetSdkVersion) { ret.extraArgs.push('-PcdvTargetSdkVersion=' + options.argv.targetSdkVersion); }
|
||||
if (options.argv.gradleArg) {
|
||||
ret.extraArgs = ret.extraArgs.concat(options.argv.gradleArg);
|
||||
}
|
||||
|
||||
var packageArgs = {};
|
||||
|
||||
if (options.argv.keystore)
|
||||
packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore));
|
||||
if (options.argv.keystore) { packageArgs.keystore = path.relative(projectRoot, path.resolve(options.argv.keystore)); }
|
||||
|
||||
['alias','storePassword','password','keystoreType'].forEach(function (flagName) {
|
||||
if (options.argv[flagName])
|
||||
packageArgs[flagName] = options.argv[flagName];
|
||||
['alias', 'storePassword', 'password', 'keystoreType', 'packageType'].forEach(function (flagName) {
|
||||
if (options.argv[flagName]) { packageArgs[flagName] = options.argv[flagName]; }
|
||||
});
|
||||
|
||||
var buildConfig = options.buildConfig;
|
||||
|
||||
// If some values are not specified as command line arguments - use build config to supplement them.
|
||||
// Command line arguemnts have precedence over build config.
|
||||
// Command line arguments have precedence over build config.
|
||||
if (buildConfig) {
|
||||
if (!fs.existsSync(buildConfig)) {
|
||||
throw new Error('Specified build config file does not exist: ' + buildConfig);
|
||||
}
|
||||
events.emit('log', 'Reading build config file: '+ path.resolve(buildConfig));
|
||||
events.emit('log', 'Reading build config file: ' + path.resolve(buildConfig));
|
||||
var buildjson = fs.readFileSync(buildConfig, 'utf8');
|
||||
var config = JSON.parse(buildjson.replace(/^\ufeff/, '')); // Remove BOM
|
||||
if (config.android && config.android[ret.buildType]) {
|
||||
var androidInfo = config.android[ret.buildType];
|
||||
if(androidInfo.keystore && !packageArgs.keystore) {
|
||||
if(androidInfo.keystore.substr(0,1) === '~') {
|
||||
if (androidInfo.keystore && !packageArgs.keystore) {
|
||||
if (androidInfo.keystore.substr(0, 1) === '~') {
|
||||
androidInfo.keystore = process.env.HOME + androidInfo.keystore.substr(1);
|
||||
}
|
||||
packageArgs.keystore = path.resolve(path.dirname(buildConfig), androidInfo.keystore);
|
||||
events.emit('log', 'Reading the keystore from: ' + packageArgs.keystore);
|
||||
}
|
||||
|
||||
['alias', 'storePassword', 'password','keystoreType'].forEach(function (key){
|
||||
['alias', 'storePassword', 'password', 'keystoreType', 'packageType'].forEach(function (key) {
|
||||
packageArgs[key] = packageArgs[key] || androidInfo[key];
|
||||
});
|
||||
}
|
||||
@@ -112,12 +103,39 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
packageArgs.password, packageArgs.keystoreType);
|
||||
}
|
||||
|
||||
if(!ret.packageInfo) {
|
||||
if(Object.keys(packageArgs).length > 0) {
|
||||
if (!ret.packageInfo) {
|
||||
// The following loop is to decide whether to print a warning about generating a signed archive
|
||||
// We only want to produce a warning if they are using a config property that is related to signing, but
|
||||
// missing the required properties for signing. We don't want to produce a warning if they are simply
|
||||
// using a build property that isn't related to signing, such as --packageType
|
||||
let shouldWarn = false;
|
||||
const signingKeys = ['keystore', 'alias', 'storePassword', 'password', 'keystoreType'];
|
||||
|
||||
for (const key in packageArgs) {
|
||||
if (!shouldWarn && signingKeys.indexOf(key) > -1) {
|
||||
// If we enter this condition, we have a key used for signing a build,
|
||||
// but we are missing some required signing properties
|
||||
shouldWarn = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldWarn) {
|
||||
events.emit('warn', '\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
|
||||
}
|
||||
}
|
||||
|
||||
if (packageArgs.packageType) {
|
||||
const VALID_PACKAGE_TYPES = [PackageType.APK, PackageType.BUNDLE];
|
||||
if (VALID_PACKAGE_TYPES.indexOf(packageArgs.packageType) === -1) {
|
||||
events.emit('warn', '"' + packageArgs.packageType + '" is an invalid packageType. Valid values are: ' + VALID_PACKAGE_TYPES.join(', ') + '\nDefaulting packageType to ' + PackageType.APK);
|
||||
ret.packageType = PackageType.APK;
|
||||
} else {
|
||||
ret.packageType = packageArgs.packageType;
|
||||
}
|
||||
} else {
|
||||
ret.packageType = PackageType.APK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -125,11 +143,11 @@ function parseOpts(options, resolvedTarget, projectRoot) {
|
||||
* Builds the project with the specifed options
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.runClean = function(options) {
|
||||
module.exports.runClean = function (options) {
|
||||
var opts = parseOpts(options, null, this.root);
|
||||
var builder = builders.getBuilder(opts.buildMethod);
|
||||
return builder.prepEnv(opts)
|
||||
.then(function() {
|
||||
var builder = builders.getBuilder();
|
||||
|
||||
return builder.prepEnv(opts).then(function () {
|
||||
return builder.clean(opts);
|
||||
});
|
||||
};
|
||||
@@ -146,23 +164,28 @@ module.exports.runClean = function(options) {
|
||||
* @return {Promise<Object>} Promise, resolved with built packages
|
||||
* information.
|
||||
*/
|
||||
module.exports.run = function(options, optResolvedTarget) {
|
||||
module.exports.run = function (options, optResolvedTarget) {
|
||||
var opts = parseOpts(options, optResolvedTarget, this.root);
|
||||
var builder = builders.getBuilder(opts.buildMethod);
|
||||
return builder.prepEnv(opts)
|
||||
.then(function() {
|
||||
var builder = builders.getBuilder();
|
||||
|
||||
return builder.prepEnv(opts).then(function () {
|
||||
if (opts.prepEnv) {
|
||||
events.emit('verbose', 'Build file successfully prepared.');
|
||||
return;
|
||||
}
|
||||
return builder.build(opts)
|
||||
.then(function() {
|
||||
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
|
||||
events.emit('log', 'Built the following apk(s): \n\t' + apkPaths.join('\n\t'));
|
||||
return builder.build(opts).then(function () {
|
||||
var paths;
|
||||
if (opts.packageType === PackageType.BUNDLE) {
|
||||
paths = builder.findOutputBundles(opts.buildType);
|
||||
events.emit('log', 'Built the following bundle(s): \n\t' + paths.join('\n\t'));
|
||||
} else {
|
||||
paths = builder.findOutputApks(opts.buildType, opts.arch);
|
||||
events.emit('log', 'Built the following apk(s): \n\t' + paths.join('\n\t'));
|
||||
}
|
||||
|
||||
return {
|
||||
apkPaths: apkPaths,
|
||||
buildType: opts.buildType,
|
||||
buildMethod: opts.buildMethod
|
||||
paths: paths,
|
||||
buildType: opts.buildType
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -172,50 +195,53 @@ module.exports.run = function(options, optResolvedTarget) {
|
||||
* Detects the architecture of a device/emulator
|
||||
* Returns "arm" or "x86".
|
||||
*/
|
||||
module.exports.detectArchitecture = function(target) {
|
||||
function helper() {
|
||||
return Adb.shell(target, 'cat /proc/cpuinfo')
|
||||
.then(function(output) {
|
||||
module.exports.detectArchitecture = function (target) {
|
||||
function helper () {
|
||||
return Adb.shell(target, 'cat /proc/cpuinfo').then(function (output) {
|
||||
return /intel/i.exec(output) ? 'x86' : 'arm';
|
||||
});
|
||||
}
|
||||
function timeout (ms, err) {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => reject(err), ms);
|
||||
});
|
||||
}
|
||||
// It sometimes happens (at least on OS X), that this command will hang forever.
|
||||
// To fix it, either unplug & replug device, or restart adb server.
|
||||
return helper()
|
||||
.timeout(1000, new CordovaError('Device communication timed out. Try unplugging & replugging the device.'))
|
||||
.then(null, function(err) {
|
||||
return Promise.race([
|
||||
helper(),
|
||||
timeout(5000, new CordovaError(
|
||||
'Device communication timed out. Try unplugging & replugging the device.'
|
||||
))
|
||||
]).catch(err => {
|
||||
if (/timed out/.exec('' + err)) {
|
||||
// adb kill-server doesn't seem to do the trick.
|
||||
// Could probably find a x-platform version of killall, but I'm not actually
|
||||
// sure that this scenario even happens on non-OSX machines.
|
||||
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
|
||||
return spawn('killall', ['adb'])
|
||||
.then(function() {
|
||||
return helper()
|
||||
.then(null, function() {
|
||||
return execa('killall', ['adb']).then(function () {
|
||||
return helper().then(null, function () {
|
||||
// The double kill is sadly often necessary, at least on mac.
|
||||
events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
|
||||
return spawn('killall', ['adb'])
|
||||
.then(function() {
|
||||
return helper()
|
||||
.then(null, function() {
|
||||
return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
|
||||
return execa('killall', ['adb']).then(function () {
|
||||
return helper().then(null, function () {
|
||||
return Promise.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}, function() {
|
||||
}, function () {
|
||||
// For non-killall OS's.
|
||||
return Q.reject(err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
||||
var paths = buildResults.apkPaths.filter(function(p) {
|
||||
module.exports.findBestApkForArchitecture = function (buildResults, arch) {
|
||||
var paths = buildResults.apkPaths.filter(function (p) {
|
||||
var apkName = path.basename(p);
|
||||
if (buildResults.buildType == 'debug') {
|
||||
if (buildResults.buildType === 'debug') {
|
||||
return /-debug/.exec(apkName);
|
||||
}
|
||||
return !/-debug/.exec(apkName);
|
||||
@@ -235,61 +261,40 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
|
||||
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
|
||||
};
|
||||
|
||||
function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
|
||||
this.keystore = {
|
||||
'name': 'key.store',
|
||||
'value': keystore
|
||||
};
|
||||
this.alias = {
|
||||
'name': 'key.alias',
|
||||
'value': alias
|
||||
};
|
||||
if (storePassword) {
|
||||
this.storePassword = {
|
||||
'name': 'key.store.password',
|
||||
'value': storePassword
|
||||
};
|
||||
}
|
||||
if (password) {
|
||||
this.password = {
|
||||
'name': 'key.alias.password',
|
||||
'value': password
|
||||
};
|
||||
}
|
||||
if (keystoreType) {
|
||||
this.keystoreType = {
|
||||
'name': 'key.store.type',
|
||||
'value': keystoreType
|
||||
};
|
||||
}
|
||||
function PackageInfo (keystore, alias, storePassword, password, keystoreType) {
|
||||
const createNameKeyObject = (name, value) => ({ name, value: value.replace(/\\/g, '\\\\') });
|
||||
|
||||
this.data = [
|
||||
createNameKeyObject('key.store', keystore),
|
||||
createNameKeyObject('key.alias', alias)
|
||||
];
|
||||
|
||||
if (storePassword) this.data.push(createNameKeyObject('key.store.password', storePassword));
|
||||
if (password) this.data.push(createNameKeyObject('key.alias.password', password));
|
||||
if (keystoreType) this.data.push(createNameKeyObject('key.store.type', keystoreType));
|
||||
}
|
||||
|
||||
PackageInfo.prototype = {
|
||||
toProperties: function() {
|
||||
var self = this;
|
||||
var result = '';
|
||||
Object.keys(self).forEach(function(key) {
|
||||
result += self[key].name;
|
||||
result += '=';
|
||||
result += self[key].value.replace(/\\/g, '\\\\');
|
||||
result += '\n';
|
||||
});
|
||||
return result;
|
||||
appendToProperties: function (propertiesParser) {
|
||||
for (const { name, value } of this.data) propertiesParser.set(name, value);
|
||||
|
||||
propertiesParser.save();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
module.exports.help = function () {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join('../build')) + ' [flags] [Signed APK flags]');
|
||||
console.log('Flags:');
|
||||
console.log(' \'--debug\': will build project in debug mode (default)');
|
||||
console.log(' \'--release\': will build project for release');
|
||||
console.log(' \'--ant\': will build project with ant');
|
||||
console.log(' \'--gradle\': will build project with gradle (default)');
|
||||
console.log(' \'--nobuild\': will skip build process (useful when using run command)');
|
||||
console.log(' \'--prepenv\': don\'t build, but copy in build scripts where necessary');
|
||||
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs. Requires --gradle.');
|
||||
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs. Requires --gradle.');
|
||||
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs.');
|
||||
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build.');
|
||||
console.log(' \'--maxSdkVersion=#\': Override maxSdkVersion for this build. (Not Recommended)');
|
||||
console.log(' \'--targetSdkVersion=#\': Override targetSdkVersion for this build.');
|
||||
console.log(' \'--gradleArg=<gradle command line arg>\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true');
|
||||
console.log(' \'--packageType=<apk|bundle>\': Builds an APK or a bundle');
|
||||
console.log('');
|
||||
console.log('Signed APK flags (overwrites debug/release-signing.proprties) :');
|
||||
console.log(' \'--keystore=<path to keystore>\': Key store used to build a signed archive. (Required)');
|
||||
|
||||
156
bin/templates/cordova/lib/builders/AntBuilder.js
vendored
@@ -1,156 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var util = require('util');
|
||||
var shell = require('shelljs');
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var check_reqs = require('../check_reqs');
|
||||
|
||||
var SIGNING_PROPERTIES = '-signing.properties';
|
||||
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||
var TEMPLATE =
|
||||
'# This file is automatically generated.\n' +
|
||||
'# Do not modify this file -- ' + MARKER + '\n';
|
||||
|
||||
var GenericBuilder = require('./GenericBuilder');
|
||||
|
||||
function AntBuilder (projectRoot) {
|
||||
GenericBuilder.call(this, projectRoot);
|
||||
|
||||
this.binDirs = {ant: this.binDirs.ant};
|
||||
}
|
||||
|
||||
util.inherits(AntBuilder, GenericBuilder);
|
||||
|
||||
AntBuilder.prototype.getArgs = function(cmd, opts) {
|
||||
var args = [cmd, '-f', path.join(this.root, 'build.xml')];
|
||||
// custom_rules.xml is required for incremental builds.
|
||||
if (hasCustomRules(this.root)) {
|
||||
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
||||
}
|
||||
if(opts.packageInfo) {
|
||||
args.push('-propertyfile=' + path.join(this.root, opts.buildType + SIGNING_PROPERTIES));
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
AntBuilder.prototype.prepEnv = function(opts) {
|
||||
var self = this;
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
// Copy in build.xml on each build so that:
|
||||
// A) we don't require the Android SDK at project creation time, and
|
||||
// B) we always use the SDK's latest version of it.
|
||||
/*jshint -W069 */
|
||||
var sdkDir = process.env['ANDROID_HOME'];
|
||||
/*jshint +W069 */
|
||||
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
|
||||
function writeBuildXml(projectPath) {
|
||||
var newData = buildTemplate.replace('PROJECT_NAME', self.extractRealProjectNameFromManifest());
|
||||
fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
|
||||
if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
|
||||
fs.writeFileSync(path.join(projectPath, 'local.properties'), TEMPLATE);
|
||||
}
|
||||
}
|
||||
writeBuildXml(self.root);
|
||||
var propertiesObj = self.readProjectProperties();
|
||||
var subProjects = propertiesObj.libs;
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
writeBuildXml(path.join(self.root, subProjects[i]));
|
||||
}
|
||||
if (propertiesObj.systemLibs.length > 0) {
|
||||
throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Use gradle instead.');
|
||||
}
|
||||
|
||||
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
|
||||
var propertiesFilePath = path.join(self.root, propertiesFile);
|
||||
if (opts.packageInfo) {
|
||||
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
|
||||
} else if(isAutoGenerated(propertiesFilePath)) {
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Builds the project with ant.
|
||||
* Returns a promise.
|
||||
*/
|
||||
AntBuilder.prototype.build = function(opts) {
|
||||
// Without our custom_rules.xml, we need to clean before building.
|
||||
var ret = Q();
|
||||
if (!hasCustomRules(this.root)) {
|
||||
// clean will call check_ant() for us.
|
||||
ret = this.clean(opts);
|
||||
}
|
||||
|
||||
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
return spawn('ant', args, {stdio: 'pipe'});
|
||||
}).progress(function (stdio){
|
||||
if (stdio.stderr) {
|
||||
process.stderr.write(stdio.stderr);
|
||||
} else {
|
||||
process.stdout.write(stdio.stdout);
|
||||
}
|
||||
}).catch(function (error) {
|
||||
if (error.toString().indexOf('Unable to resolve project target') >= 0) {
|
||||
return check_reqs.check_android_target(error).then(function() {
|
||||
// If due to some odd reason - check_android_target succeeds
|
||||
// we should still fail here.
|
||||
return Q.reject(error);
|
||||
});
|
||||
}
|
||||
return Q.reject(error);
|
||||
});
|
||||
};
|
||||
|
||||
AntBuilder.prototype.clean = function(opts) {
|
||||
var args = this.getArgs('clean', opts);
|
||||
var self = this;
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
return spawn('ant', args, {stdio: 'inherit'});
|
||||
})
|
||||
.then(function () {
|
||||
shell.rm('-rf', path.join(self.root, 'out'));
|
||||
|
||||
['debug', 'release'].forEach(function(config) {
|
||||
var propertiesFilePath = path.join(self.root, config + SIGNING_PROPERTIES);
|
||||
if(isAutoGenerated(propertiesFilePath)){
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = AntBuilder;
|
||||
|
||||
function hasCustomRules(projectRoot) {
|
||||
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
|
||||
}
|
||||
|
||||
function isAutoGenerated(file) {
|
||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
||||
}
|
||||
147
bin/templates/cordova/lib/builders/GenericBuilder.js
vendored
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var events = require('cordova-common').events;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
function GenericBuilder (projectDir) {
|
||||
this.root = projectDir || path.resolve(__dirname, '../../..');
|
||||
this.binDirs = {
|
||||
ant: path.join(this.root, hasCustomRules(this.root) ? 'ant-build' : 'bin'),
|
||||
gradle: path.join(this.root, 'build', 'outputs', 'apk')
|
||||
};
|
||||
}
|
||||
|
||||
function hasCustomRules(projectRoot) {
|
||||
return fs.existsSync(path.join(projectRoot, 'custom_rules.xml'));
|
||||
}
|
||||
|
||||
GenericBuilder.prototype.prepEnv = function() {
|
||||
return Q();
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.build = function() {
|
||||
events.emit('log', 'Skipping build...');
|
||||
return Q(null);
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.clean = function() {
|
||||
return Q();
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.findOutputApks = function(build_type, arch) {
|
||||
var self = this;
|
||||
return Object.keys(this.binDirs)
|
||||
.reduce(function (result, builderName) {
|
||||
var binDir = self.binDirs[builderName];
|
||||
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
|
||||
}, [])
|
||||
.sort(apkSorter);
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.readProjectProperties = function () {
|
||||
function findAllUniq(data, r) {
|
||||
var s = {};
|
||||
var m;
|
||||
while ((m = r.exec(data))) {
|
||||
s[m[1]] = 1;
|
||||
}
|
||||
return Object.keys(s);
|
||||
}
|
||||
|
||||
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
|
||||
return {
|
||||
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
|
||||
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
|
||||
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
|
||||
};
|
||||
};
|
||||
|
||||
GenericBuilder.prototype.extractRealProjectNameFromManifest = function () {
|
||||
var manifestPath = path.join(this.root, 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
||||
}
|
||||
|
||||
var packageName=m[1];
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
return packageName.substring(lastDotIndex + 1);
|
||||
};
|
||||
|
||||
module.exports = GenericBuilder;
|
||||
|
||||
function apkSorter(fileA, fileB) {
|
||||
// De-prioritize unsigned builds
|
||||
var unsignedRE = /-unsigned/;
|
||||
if (unsignedRE.exec(fileA)) {
|
||||
return 1;
|
||||
} else if (unsignedRE.exec(fileB)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var timeDiff = fs.statSync(fileA).mtime - fs.statSync(fileB).mtime;
|
||||
return timeDiff === 0 ? fileA.length - fileB.length : timeDiff;
|
||||
}
|
||||
|
||||
function findOutputApksHelper(dir, build_type, arch) {
|
||||
var shellSilent = shell.config.silent;
|
||||
shell.config.silent = true;
|
||||
|
||||
var ret = shell.ls(path.join(dir, '*.apk'))
|
||||
.filter(function(candidate) {
|
||||
var apkName = path.basename(candidate);
|
||||
// Need to choose between release and debug .apk.
|
||||
if (build_type === 'debug') {
|
||||
return /-debug/.exec(apkName) && !/-unaligned|-unsigned/.exec(apkName);
|
||||
}
|
||||
if (build_type === 'release') {
|
||||
return /-release/.exec(apkName) && !/-unaligned/.exec(apkName);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sort(apkSorter);
|
||||
|
||||
shellSilent = shellSilent;
|
||||
|
||||
if (ret.length === 0) {
|
||||
return ret;
|
||||
}
|
||||
// Assume arch-specific build if newest apk has -x86 or -arm.
|
||||
var archSpecific = !!/-x86|-arm/.exec(path.basename(ret[0]));
|
||||
// And show only arch-specific ones (or non-arch-specific)
|
||||
ret = ret.filter(function(p) {
|
||||
/*jshint -W018 */
|
||||
return !!/-x86|-arm/.exec(path.basename(p)) == archSpecific;
|
||||
/*jshint +W018 */
|
||||
});
|
||||
|
||||
if (archSpecific && ret.length > 1 && arch) {
|
||||
ret = ret.filter(function(p) {
|
||||
return path.basename(p).indexOf('-' + arch) != -1;
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
241
bin/templates/cordova/lib/builders/GradleBuilder.js
vendored
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var check_reqs = require('../check_reqs');
|
||||
|
||||
var GenericBuilder = require('./GenericBuilder');
|
||||
|
||||
var MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||
var SIGNING_PROPERTIES = '-signing.properties';
|
||||
var TEMPLATE =
|
||||
'# This file is automatically generated.\n' +
|
||||
'# Do not modify this file -- ' + MARKER + '\n';
|
||||
|
||||
function GradleBuilder (projectRoot) {
|
||||
GenericBuilder.call(this, projectRoot);
|
||||
|
||||
this.binDirs = {gradle: this.binDirs.gradle};
|
||||
}
|
||||
|
||||
util.inherits(GradleBuilder, GenericBuilder);
|
||||
|
||||
GradleBuilder.prototype.getArgs = function(cmd, opts) {
|
||||
if (cmd == 'release') {
|
||||
cmd = 'cdvBuildRelease';
|
||||
} else if (cmd == 'debug') {
|
||||
cmd = 'cdvBuildDebug';
|
||||
}
|
||||
var args = [cmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
if (opts.arch) {
|
||||
args.push('-PcdvBuildArch=' + opts.arch);
|
||||
}
|
||||
|
||||
// 10 seconds -> 6 seconds
|
||||
args.push('-Dorg.gradle.daemon=true');
|
||||
// allow NDK to be used - required by Gradle 1.5 plugin
|
||||
args.push('-Pandroid.useDeprecatedNdk=true');
|
||||
args.push.apply(args, opts.extraArgs);
|
||||
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
||||
// args.push('-Dorg.gradle.parallel=true');
|
||||
return args;
|
||||
};
|
||||
|
||||
// Makes the project buildable, minus the gradle wrapper.
|
||||
GradleBuilder.prototype.prepBuildFiles = function() {
|
||||
// Update the version of build.gradle in each dependent library.
|
||||
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
||||
var propertiesObj = this.readProjectProperties();
|
||||
var subProjects = propertiesObj.libs;
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
if (subProjects[i] !== 'CordovaLib') {
|
||||
shell.cp('-f', pluginBuildGradle, path.join(this.root, subProjects[i], 'build.gradle'));
|
||||
}
|
||||
}
|
||||
|
||||
var name = this.extractRealProjectNameFromManifest();
|
||||
//Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
||||
var settingsGradlePaths = subProjects.map(function(p){
|
||||
var realDir=p.replace(/[/\\]/g, ':');
|
||||
var libName=realDir.replace(name+'-','');
|
||||
var str='include ":'+libName+'"\n';
|
||||
if(realDir.indexOf(name+'-')!==-1)
|
||||
str+='project(":'+libName+'").projectDir = new File("'+p+'")\n';
|
||||
return str;
|
||||
});
|
||||
|
||||
// Write the settings.gradle file.
|
||||
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
|
||||
'// GENERATED FILE - DO NOT EDIT\n' +
|
||||
'include ":"\n' + settingsGradlePaths.join(''));
|
||||
// Update dependencies within build.gradle.
|
||||
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
|
||||
var depsList = '';
|
||||
subProjects.forEach(function(p) {
|
||||
var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
|
||||
depsList += ' debugCompile project(path: "' + libName + '", configuration: "debug")\n';
|
||||
depsList += ' releaseCompile project(path: "' + libName + '", configuration: "release")\n';
|
||||
});
|
||||
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
|
||||
var SYSTEM_LIBRARY_MAPPINGS = [
|
||||
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
|
||||
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
|
||||
];
|
||||
propertiesObj.systemLibs.forEach(function(p) {
|
||||
var mavenRef;
|
||||
// It's already in gradle form if it has two ':'s
|
||||
if (/:.*:/.exec(p)) {
|
||||
mavenRef = p;
|
||||
} else {
|
||||
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
|
||||
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
|
||||
if (pair[0].exec(p)) {
|
||||
mavenRef = p.replace(pair[0], pair[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mavenRef) {
|
||||
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
|
||||
}
|
||||
}
|
||||
depsList += ' compile "' + mavenRef + '"\n';
|
||||
});
|
||||
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
|
||||
var includeList = '';
|
||||
propertiesObj.gradleIncludes.forEach(function(includePath) {
|
||||
includeList += 'apply from: "' + includePath + '"\n';
|
||||
});
|
||||
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
|
||||
fs.writeFileSync(path.join(this.root, 'build.gradle'), buildGradle);
|
||||
};
|
||||
|
||||
GradleBuilder.prototype.prepEnv = function(opts) {
|
||||
var self = this;
|
||||
return check_reqs.check_gradle()
|
||||
.then(function() {
|
||||
return self.prepBuildFiles();
|
||||
}).then(function() {
|
||||
// Copy the gradle wrapper on each build so that:
|
||||
// A) we don't require the Android SDK at project creation time, and
|
||||
// B) we always use the SDK's latest version of it.
|
||||
// check_reqs ensures that this is set.
|
||||
/*jshint -W069 */
|
||||
var sdkDir = process.env['ANDROID_HOME'];
|
||||
/*jshint +W069 */
|
||||
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
||||
if (process.platform == 'win32') {
|
||||
shell.rm('-f', path.join(self.root, 'gradlew.bat'));
|
||||
shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
|
||||
} else {
|
||||
shell.rm('-f', path.join(self.root, 'gradlew'));
|
||||
shell.cp(path.join(wrapperDir, 'gradlew'), self.root);
|
||||
}
|
||||
shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
|
||||
shell.mkdir('-p', path.join(self.root, 'gradle'));
|
||||
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
|
||||
|
||||
// If the gradle distribution URL is set, make sure it points to version we want.
|
||||
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
|
||||
// For some reason, using ^ and $ don't work. This does the job, though.
|
||||
var distributionUrlRegex = /distributionUrl.*zip/;
|
||||
/*jshint -W069 */
|
||||
var distributionUrl = process.env['CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL'] || 'http\\://services.gradle.org/distributions/gradle-2.13-all.zip';
|
||||
/*jshint +W069 */
|
||||
var gradleWrapperPropertiesPath = path.join(self.root, 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
||||
shell.chmod('u+w', gradleWrapperPropertiesPath);
|
||||
shell.sed('-i', distributionUrlRegex, 'distributionUrl='+distributionUrl, gradleWrapperPropertiesPath);
|
||||
|
||||
var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
|
||||
var propertiesFilePath = path.join(self.root, propertiesFile);
|
||||
if (opts.packageInfo) {
|
||||
fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
|
||||
} else if (isAutoGenerated(propertiesFilePath)) {
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Builds the project with gradle.
|
||||
* Returns a promise.
|
||||
*/
|
||||
GradleBuilder.prototype.build = function(opts) {
|
||||
var wrapper = path.join(this.root, 'gradlew');
|
||||
var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
|
||||
|
||||
return spawn(wrapper, args, {stdio: 'pipe'})
|
||||
.progress(function (stdio){
|
||||
if (stdio.stderr) {
|
||||
/*
|
||||
* Workaround for the issue with Java printing some unwanted information to
|
||||
* stderr instead of stdout.
|
||||
* This function suppresses 'Picked up _JAVA_OPTIONS' message from being
|
||||
* printed to stderr. See https://issues.apache.org/jira/browse/CB-9971 for
|
||||
* explanation.
|
||||
*/
|
||||
var suppressThisLine = /^Picked up _JAVA_OPTIONS: /i.test(stdio.stderr.toString());
|
||||
if (suppressThisLine) {
|
||||
return;
|
||||
}
|
||||
process.stderr.write(stdio.stderr);
|
||||
} else {
|
||||
process.stdout.write(stdio.stdout);
|
||||
}
|
||||
}).catch(function (error) {
|
||||
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
|
||||
return check_reqs.check_android_target(error).then(function() {
|
||||
// If due to some odd reason - check_android_target succeeds
|
||||
// we should still fail here.
|
||||
return Q.reject(error);
|
||||
});
|
||||
}
|
||||
return Q.reject(error);
|
||||
});
|
||||
};
|
||||
|
||||
GradleBuilder.prototype.clean = function(opts) {
|
||||
var builder = this;
|
||||
var wrapper = path.join(this.root, 'gradlew');
|
||||
var args = builder.getArgs('clean', opts);
|
||||
return Q().then(function() {
|
||||
return spawn(wrapper, args, {stdio: 'inherit'});
|
||||
})
|
||||
.then(function () {
|
||||
shell.rm('-rf', path.join(builder.root, 'out'));
|
||||
|
||||
['debug', 'release'].forEach(function(config) {
|
||||
var propertiesFilePath = path.join(builder.root, config + SIGNING_PROPERTIES);
|
||||
if(isAutoGenerated(propertiesFilePath)){
|
||||
shell.rm('-f', propertiesFilePath);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = GradleBuilder;
|
||||
|
||||
function isAutoGenerated(file) {
|
||||
return fs.existsSync(file) && fs.readFileSync(file, 'utf8').indexOf(MARKER) > 0;
|
||||
}
|
||||
380
bin/templates/cordova/lib/builders/ProjectBuilder.js
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
const execa = require('execa');
|
||||
var events = require('cordova-common').events;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var check_reqs = require('../check_reqs');
|
||||
var PackageType = require('../PackageType');
|
||||
const { createEditor } = require('properties-parser');
|
||||
|
||||
const MARKER = 'YOUR CHANGES WILL BE ERASED!';
|
||||
const SIGNING_PROPERTIES = '-signing.properties';
|
||||
const TEMPLATE =
|
||||
'# This file is automatically generated.\n' +
|
||||
'# Do not modify this file -- ' + MARKER + '\n';
|
||||
|
||||
const archSpecificRegex = /-x86|-arm/;
|
||||
const unsignedBuildRegex = /-unsigned/;
|
||||
|
||||
const fileSorter = (filePathA, filePathB) => {
|
||||
const archSpecificA = archSpecificRegex.test(filePathA);
|
||||
const archSpecificB = archSpecificRegex.test(filePathB);
|
||||
|
||||
// If they are not equal, then sort by specific archs after generic ones
|
||||
if (archSpecificA !== archSpecificB) {
|
||||
return archSpecificA < archSpecificB ? -1 : 1;
|
||||
}
|
||||
|
||||
// Otherwise, move onto the next sort item, which is by sorting unsigned bulds after signed ones
|
||||
const unsignedA = unsignedBuildRegex.test(filePathA);
|
||||
const unsignedB = unsignedBuildRegex.test(filePathB);
|
||||
|
||||
if (unsignedA !== unsignedB) {
|
||||
return unsignedA < unsignedB ? -1 : 1;
|
||||
}
|
||||
|
||||
// Then, sort by modification time, latest first
|
||||
const modTimeA = fs.statSync(filePathA).mtime.getTime();
|
||||
const modTimeB = fs.statSync(filePathB).mtime.getTime();
|
||||
|
||||
if (modTimeA !== modTimeB) {
|
||||
return modTimeA < modTimeB ? 1 : -1;
|
||||
}
|
||||
|
||||
// Finally, if all above is the same, sort by file name length, ascending
|
||||
return filePathB.length < filePathA.length ? -1 : 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* If the provided directory does not exist or extension is missing, return an empty array.
|
||||
* If the director exists, loop the directories and collect list of files matching the extension.
|
||||
*
|
||||
* @param {String} dir Directory to scan
|
||||
* @param {String} extension
|
||||
*/
|
||||
function recursivelyFindFiles (dir, extension) {
|
||||
if (!fs.existsSync(dir) || !extension) return [];
|
||||
|
||||
const files = fs.readdirSync(dir, { withFileTypes: true })
|
||||
.map(entry => {
|
||||
const item = path.resolve(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) return recursivelyFindFiles(item, extension);
|
||||
if (path.extname(entry.name) === `.${extension}`) return item;
|
||||
return false;
|
||||
});
|
||||
|
||||
return Array.prototype.concat(...files)
|
||||
.filter(file => file !== false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {String} dir
|
||||
* @param {String} build_type
|
||||
* @param {String} arch
|
||||
* @param {String} extension
|
||||
*/
|
||||
function findOutputFilesHelper (dir, build_type, arch, extension) {
|
||||
let files = recursivelyFindFiles(path.resolve(dir, build_type), extension);
|
||||
|
||||
if (files.length === 0) return files;
|
||||
|
||||
// Assume arch-specific build if newest apk has -x86 or -arm.
|
||||
const archSpecific = !!/-x86|-arm/.exec(path.basename(files[0]));
|
||||
|
||||
// And show only arch-specific ones (or non-arch-specific)
|
||||
files = files.filter(p => !!/-x86|-arm/.exec(path.basename(p)) === archSpecific);
|
||||
|
||||
if (archSpecific && files.length > 1 && arch) {
|
||||
files = files.filter(p => path.basename(p).indexOf('-' + arch) !== -1);
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
class ProjectBuilder {
|
||||
constructor (rootDirectory) {
|
||||
this.root = rootDirectory || path.resolve(__dirname, '../../..');
|
||||
this.apkDir = path.join(this.root, 'app', 'build', 'outputs', 'apk');
|
||||
this.aabDir = path.join(this.root, 'app', 'build', 'outputs', 'bundle');
|
||||
}
|
||||
|
||||
getArgs (cmd, opts) {
|
||||
let args;
|
||||
let buildCmd = cmd;
|
||||
if (opts.packageType === PackageType.BUNDLE) {
|
||||
if (cmd === 'release') {
|
||||
buildCmd = ':app:bundleRelease';
|
||||
} else if (cmd === 'debug') {
|
||||
buildCmd = ':app:bundleDebug';
|
||||
}
|
||||
|
||||
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
} else {
|
||||
if (cmd === 'release') {
|
||||
buildCmd = 'cdvBuildRelease';
|
||||
} else if (cmd === 'debug') {
|
||||
buildCmd = 'cdvBuildDebug';
|
||||
}
|
||||
|
||||
args = [buildCmd, '-b', path.join(this.root, 'build.gradle')];
|
||||
|
||||
if (opts.arch) {
|
||||
args.push('-PcdvBuildArch=' + opts.arch);
|
||||
}
|
||||
}
|
||||
|
||||
args.push.apply(args, opts.extraArgs);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/*
|
||||
* This returns a promise
|
||||
*/
|
||||
runGradleWrapper (gradle_cmd) {
|
||||
var gradlePath = path.join(this.root, 'gradlew');
|
||||
var wrapperGradle = path.join(this.root, 'wrapper.gradle');
|
||||
if (fs.existsSync(gradlePath)) {
|
||||
// Literally do nothing, for some reason this works, while !fs.existsSync didn't on Windows
|
||||
} else {
|
||||
return execa(gradle_cmd, ['-p', this.root, 'wrapper', '-b', wrapperGradle], { stdio: 'inherit' });
|
||||
}
|
||||
}
|
||||
|
||||
readProjectProperties () {
|
||||
function findAllUniq (data, r) {
|
||||
var s = {};
|
||||
var m;
|
||||
while ((m = r.exec(data))) {
|
||||
s[m[1]] = 1;
|
||||
}
|
||||
return Object.keys(s);
|
||||
}
|
||||
|
||||
var data = fs.readFileSync(path.join(this.root, 'project.properties'), 'utf8');
|
||||
return {
|
||||
libs: findAllUniq(data, /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg),
|
||||
gradleIncludes: findAllUniq(data, /^\s*cordova\.gradle\.include\.\d+=(.*)(?:\s|$)/mg),
|
||||
systemLibs: findAllUniq(data, /^\s*cordova\.system\.library\.\d+=(.*)(?:\s|$)/mg)
|
||||
};
|
||||
}
|
||||
|
||||
extractRealProjectNameFromManifest () {
|
||||
var manifestPath = path.join(this.root, 'app', 'src', 'main', 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<manifest[\s\S]*?package\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new CordovaError('Could not find package name in ' + manifestPath);
|
||||
}
|
||||
|
||||
var packageName = m[1];
|
||||
var lastDotIndex = packageName.lastIndexOf('.');
|
||||
return packageName.substring(lastDotIndex + 1);
|
||||
}
|
||||
|
||||
// Makes the project buildable, minus the gradle wrapper.
|
||||
prepBuildFiles () {
|
||||
// Update the version of build.gradle in each dependent library.
|
||||
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
|
||||
var propertiesObj = this.readProjectProperties();
|
||||
var subProjects = propertiesObj.libs;
|
||||
|
||||
// Check and copy the gradle file into the subproject
|
||||
// Called by the loop before this function def
|
||||
|
||||
var checkAndCopy = function (subProject, root) {
|
||||
var subProjectGradle = path.join(root, subProject, 'build.gradle');
|
||||
// This is the future-proof way of checking if a file exists
|
||||
// This must be synchronous to satisfy a Travis test
|
||||
try {
|
||||
fs.accessSync(subProjectGradle, fs.F_OK);
|
||||
} catch (e) {
|
||||
fs.copySync(pluginBuildGradle, subProjectGradle);
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < subProjects.length; ++i) {
|
||||
if (subProjects[i] !== 'CordovaLib') {
|
||||
checkAndCopy(subProjects[i], this.root);
|
||||
}
|
||||
}
|
||||
var name = this.extractRealProjectNameFromManifest();
|
||||
// Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
|
||||
var settingsGradlePaths = subProjects.map(function (p) {
|
||||
var realDir = p.replace(/[/\\]/g, ':');
|
||||
var libName = realDir.replace(name + '-', '');
|
||||
var str = 'include ":' + libName + '"\n';
|
||||
if (realDir.indexOf(name + '-') !== -1) {
|
||||
str += 'project(":' + libName + '").projectDir = new File("' + p + '")\n';
|
||||
}
|
||||
return str;
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.join(this.root, 'settings.gradle'),
|
||||
'// GENERATED FILE - DO NOT EDIT\n' +
|
||||
'include ":"\n' + settingsGradlePaths.join(''));
|
||||
|
||||
// Update dependencies within build.gradle.
|
||||
var buildGradle = fs.readFileSync(path.join(this.root, 'app', 'build.gradle'), 'utf8');
|
||||
var depsList = '';
|
||||
var root = this.root;
|
||||
var insertExclude = function (p) {
|
||||
var gradlePath = path.join(root, p, 'build.gradle');
|
||||
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
|
||||
if (projectGradleFile.indexOf('CordovaLib') !== -1) {
|
||||
depsList += '{\n exclude module:("CordovaLib")\n }\n';
|
||||
} else {
|
||||
depsList += '\n';
|
||||
}
|
||||
};
|
||||
subProjects.forEach(function (p) {
|
||||
events.emit('log', 'Subproject Path: ' + p);
|
||||
var libName = p.replace(/[/\\]/g, ':').replace(name + '-', '');
|
||||
if (libName !== 'app') {
|
||||
depsList += ' implementation(project(path: ":' + libName + '"))';
|
||||
insertExclude(p);
|
||||
}
|
||||
});
|
||||
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
|
||||
var SYSTEM_LIBRARY_MAPPINGS = [
|
||||
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
|
||||
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
|
||||
];
|
||||
|
||||
propertiesObj.systemLibs.forEach(function (p) {
|
||||
var mavenRef;
|
||||
// It's already in gradle form if it has two ':'s
|
||||
if (/:.*:/.exec(p)) {
|
||||
mavenRef = p;
|
||||
} else {
|
||||
for (var i = 0; i < SYSTEM_LIBRARY_MAPPINGS.length; ++i) {
|
||||
var pair = SYSTEM_LIBRARY_MAPPINGS[i];
|
||||
if (pair[0].exec(p)) {
|
||||
mavenRef = p.replace(pair[0], pair[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mavenRef) {
|
||||
throw new CordovaError('Unsupported system library (does not work with gradle): ' + p);
|
||||
}
|
||||
}
|
||||
depsList += ' implementation "' + mavenRef + '"\n';
|
||||
});
|
||||
|
||||
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
|
||||
var includeList = '';
|
||||
|
||||
propertiesObj.gradleIncludes.forEach(function (includePath) {
|
||||
includeList += 'apply from: "../' + includePath + '"\n';
|
||||
});
|
||||
buildGradle = buildGradle.replace(/(PLUGIN GRADLE EXTENSIONS START)[\s\S]*(\/\/ PLUGIN GRADLE EXTENSIONS END)/, '$1\n' + includeList + '$2');
|
||||
// This needs to be stored in the app gradle, not the root grade
|
||||
fs.writeFileSync(path.join(this.root, 'app', 'build.gradle'), buildGradle);
|
||||
}
|
||||
|
||||
prepEnv (opts) {
|
||||
var self = this;
|
||||
return check_reqs.check_gradle()
|
||||
.then(function (gradlePath) {
|
||||
return self.runGradleWrapper(gradlePath);
|
||||
}).then(function () {
|
||||
return self.prepBuildFiles();
|
||||
}).then(() => {
|
||||
// update/set the distributionUrl in the gradle-wrapper.properties
|
||||
const gradleWrapperPropertiesPath = path.join(self.root, 'gradle/wrapper/gradle-wrapper.properties');
|
||||
const gradleWrapperProperties = createEditor(gradleWrapperPropertiesPath);
|
||||
const distributionUrl = process.env.CORDOVA_ANDROID_GRADLE_DISTRIBUTION_URL || 'https://services.gradle.org/distributions/gradle-6.5-all.zip';
|
||||
gradleWrapperProperties.set('distributionUrl', distributionUrl);
|
||||
gradleWrapperProperties.save();
|
||||
|
||||
events.emit('verbose', `Gradle Distribution URL: ${distributionUrl}`);
|
||||
})
|
||||
.then(() => {
|
||||
const signingPropertiesPath = path.join(self.root, `${opts.buildType}${SIGNING_PROPERTIES}`);
|
||||
|
||||
if (fs.existsSync(signingPropertiesPath)) fs.removeSync(signingPropertiesPath);
|
||||
if (opts.packageInfo) {
|
||||
fs.ensureFileSync(signingPropertiesPath);
|
||||
const signingProperties = createEditor(signingPropertiesPath);
|
||||
signingProperties.addHeadComment(TEMPLATE);
|
||||
opts.packageInfo.appendToProperties(signingProperties);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds the project with gradle.
|
||||
* Returns a promise.
|
||||
*/
|
||||
build (opts) {
|
||||
var wrapper = path.join(this.root, 'gradlew');
|
||||
var args = this.getArgs(opts.buildType === 'debug' ? 'debug' : 'release', opts);
|
||||
|
||||
return execa(wrapper, args, { stdio: 'inherit' })
|
||||
.catch(function (error) {
|
||||
if (error.toString().indexOf('failed to find target with hash string') >= 0) {
|
||||
return check_reqs.check_android_target(error).then(function () {
|
||||
// If due to some odd reason - check_android_target succeeds
|
||||
// we should still fail here.
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
clean (opts) {
|
||||
const wrapper = path.join(this.root, 'gradlew');
|
||||
const args = this.getArgs('clean', opts);
|
||||
return execa(wrapper, args, { stdio: 'inherit' })
|
||||
.then(() => {
|
||||
fs.removeSync(path.join(this.root, 'out'));
|
||||
|
||||
['debug', 'release'].map(config => path.join(this.root, `${config}${SIGNING_PROPERTIES}`))
|
||||
.forEach(file => {
|
||||
const hasFile = fs.existsSync(file);
|
||||
const hasMarker = hasFile && fs.readFileSync(file, 'utf8')
|
||||
.includes(MARKER);
|
||||
|
||||
if (hasFile && hasMarker) fs.removeSync(file);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
findOutputApks (build_type, arch) {
|
||||
return findOutputFilesHelper(this.apkDir, build_type, arch, 'apk').sort(fileSorter);
|
||||
}
|
||||
|
||||
findOutputBundles (build_type) {
|
||||
return findOutputFilesHelper(this.aabDir, build_type, false, 'aab').sort(fileSorter);
|
||||
}
|
||||
|
||||
fetchBuildResults (build_type, arch) {
|
||||
return {
|
||||
apkPaths: this.findOutputApks(build_type, arch),
|
||||
buildType: build_type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ProjectBuilder;
|
||||
55
bin/templates/cordova/lib/builders/builders.js
vendored
@@ -1,47 +1,34 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var knownBuilders = {
|
||||
ant: 'AntBuilder',
|
||||
gradle: 'GradleBuilder',
|
||||
none: 'GenericBuilder'
|
||||
};
|
||||
const CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
/**
|
||||
* Helper method that instantiates and returns a builder for specified build
|
||||
* type.
|
||||
* Helper method that instantiates and returns a builder for specified build type.
|
||||
*
|
||||
* @param {String} builderType Builder name to construct and return. Must
|
||||
* be one of 'ant', 'gradle' or 'none'
|
||||
*
|
||||
* @return {Builder} A builder instance for specified build type.
|
||||
* @return {Builder} A builder instance for specified build type.
|
||||
*/
|
||||
module.exports.getBuilder = function (builderType, projectRoot) {
|
||||
if (!knownBuilders[builderType])
|
||||
throw new CordovaError('Builder ' + builderType + ' is not supported.');
|
||||
|
||||
module.exports.getBuilder = function (projectPath) {
|
||||
try {
|
||||
var Builder = require('./' + knownBuilders[builderType]);
|
||||
return new Builder(projectRoot);
|
||||
const Builder = require('./ProjectBuilder');
|
||||
return new Builder(projectPath);
|
||||
} catch (err) {
|
||||
throw new CordovaError('Failed to instantiate ' + knownBuilders[builderType] + ' builder: ' + err);
|
||||
throw new CordovaError('Failed to instantiate ProjectBuilder builder: ' + err);
|
||||
}
|
||||
};
|
||||
|
||||
480
bin/templates/cordova/lib/check_reqs.js
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const execa = require('execa');
|
||||
var path = require('path');
|
||||
var fs = require('fs-extra');
|
||||
var os = require('os');
|
||||
var which = require('which');
|
||||
var REPO_ROOT = path.join(__dirname, '..', '..', '..', '..');
|
||||
var PROJECT_ROOT = path.join(__dirname, '..', '..');
|
||||
const { CordovaError, ConfigParser, events } = require('cordova-common');
|
||||
var android_sdk = require('./android_sdk');
|
||||
const { createEditor } = require('properties-parser');
|
||||
|
||||
function forgivingWhichSync (cmd) {
|
||||
const whichResult = which.sync(cmd, { nothrow: true });
|
||||
|
||||
// On null, returns empty string to maintain backwards compatibility
|
||||
// realpathSync follows symlinks
|
||||
return whichResult === null ? '' : fs.realpathSync(whichResult);
|
||||
}
|
||||
|
||||
function getJDKDirectory (directory) {
|
||||
const p = path.resolve(directory, 'java');
|
||||
if (fs.existsSync(p)) {
|
||||
const directories = fs.readdirSync(p);
|
||||
for (let i = 0; i < directories.length; i++) {
|
||||
const dir = directories[i];
|
||||
if (/^(jdk)+./.test(dir)) {
|
||||
return path.resolve(directory, 'java', dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports.isWindows = function () {
|
||||
return (os.platform() === 'win32');
|
||||
};
|
||||
|
||||
module.exports.isDarwin = function () {
|
||||
return (os.platform() === 'darwin');
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get valid target from framework/project.properties if run from this repo
|
||||
* Otherwise get target from project.properties file within a generated cordova-android project
|
||||
* @returns {string} The android target in format "android-${target}"
|
||||
*/
|
||||
module.exports.get_target = function () {
|
||||
const projectPropertiesPaths = [
|
||||
path.join(REPO_ROOT, 'framework', 'project.properties'),
|
||||
path.join(PROJECT_ROOT, 'project.properties')
|
||||
];
|
||||
|
||||
// Get the minimum required target API from the framework.
|
||||
let target = projectPropertiesPaths.filter(filePath => fs.existsSync(filePath))
|
||||
.map(filePath => createEditor(filePath).get('target'))
|
||||
.pop();
|
||||
|
||||
if (!target) {
|
||||
throw new Error(`We could not locate the target from the "project.properties" at either "${projectPropertiesPaths.join('", "')}".`);
|
||||
}
|
||||
|
||||
// If the repo config.xml file exists, find the desired targetSdkVersion.
|
||||
const configFile = path.join(REPO_ROOT, 'config.xml');
|
||||
if (!fs.existsSync(configFile)) return target;
|
||||
|
||||
const configParser = new ConfigParser(configFile);
|
||||
const desiredAPI = parseInt(configParser.getPreference('android-targetSdkVersion', 'android'), 10);
|
||||
|
||||
if (!isNaN(desiredAPI)) {
|
||||
const minimumAPI = parseInt(target.split('-').pop(), 10);
|
||||
|
||||
if (desiredAPI >= minimumAPI) {
|
||||
target = `android-${desiredAPI}`;
|
||||
} else {
|
||||
events.emit('warn', `android-targetSdkVersion should be greater than or equal to ${minimumAPI}.`);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
module.exports.check_ant = function () {
|
||||
return execa('ant', ['-version']).then(({ stdout: output }) => {
|
||||
// Parse Ant version from command output
|
||||
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
|
||||
}).catch(function (err) {
|
||||
if (err) {
|
||||
throw new CordovaError('Failed to run `ant -version`. Make sure you have `ant` on your $PATH.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.get_gradle_wrapper = function () {
|
||||
var androidStudioPath;
|
||||
var i = 0;
|
||||
var foundStudio = false;
|
||||
var program_dir;
|
||||
// OK, This hack only works on Windows, not on Mac OS or Linux. We will be deleting this eventually!
|
||||
if (module.exports.isWindows()) {
|
||||
var result = execa.sync(path.join(__dirname, 'getASPath.bat'));
|
||||
// console.log('result.stdout =' + result.stdout.toString());
|
||||
// console.log('result.stderr =' + result.stderr.toString());
|
||||
|
||||
if (result.stderr.toString().length > 0) {
|
||||
var androidPath = path.join(process.env.ProgramFiles, 'Android') + '/';
|
||||
if (fs.existsSync(androidPath)) {
|
||||
program_dir = fs.readdirSync(androidPath);
|
||||
while (i < program_dir.length && !foundStudio) {
|
||||
if (program_dir[i].startsWith('Android Studio')) {
|
||||
foundStudio = true;
|
||||
androidStudioPath = path.join(process.env.ProgramFiles, 'Android', program_dir[i], 'gradle');
|
||||
} else { ++i; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// console.log('got android studio path from registry');
|
||||
// remove the (os independent) new line char at the end of stdout
|
||||
// add gradle to match the above.
|
||||
androidStudioPath = path.join(result.stdout.toString().split('\r\n')[0], 'gradle');
|
||||
}
|
||||
}
|
||||
|
||||
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
|
||||
var dirs = fs.readdirSync(androidStudioPath);
|
||||
if (dirs[0].split('-')[0] === 'gradle') {
|
||||
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
|
||||
}
|
||||
} else {
|
||||
// OK, let's try to check for Gradle!
|
||||
return forgivingWhichSync('gradle');
|
||||
}
|
||||
};
|
||||
|
||||
// Returns a promise. Called only by build and clean commands.
|
||||
module.exports.check_gradle = function () {
|
||||
var sdkDir = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
|
||||
if (!sdkDir) {
|
||||
return Promise.reject(new CordovaError('Could not find gradle wrapper within Android SDK. Could not find Android SDK directory.\n' +
|
||||
'Might need to install Android SDK or set up \'ANDROID_SDK_ROOT\' env variable.'));
|
||||
}
|
||||
|
||||
var gradlePath = module.exports.get_gradle_wrapper();
|
||||
|
||||
if (gradlePath.length !== 0) return Promise.resolve(gradlePath);
|
||||
|
||||
return Promise.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
|
||||
'or on your system to install the gradle wrapper. Please include gradle \n' +
|
||||
'in your path, or install Android Studio'));
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_java = function () {
|
||||
var javacPath = forgivingWhichSync('javac');
|
||||
var hasJavaHome = !!process.env.JAVA_HOME;
|
||||
return Promise.resolve().then(function () {
|
||||
if (hasJavaHome) {
|
||||
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
|
||||
if (!javacPath) {
|
||||
process.env.PATH += path.delimiter + path.join(process.env.JAVA_HOME, 'bin');
|
||||
}
|
||||
} else {
|
||||
if (javacPath) {
|
||||
// OS X has a command for finding JAVA_HOME.
|
||||
var find_java = '/usr/libexec/java_home';
|
||||
var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.';
|
||||
if (fs.existsSync(find_java)) {
|
||||
return execa(find_java).then(({ stdout }) => {
|
||||
process.env.JAVA_HOME = stdout;
|
||||
}).catch(function (err) {
|
||||
if (err) {
|
||||
throw new CordovaError(default_java_error_msg);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// See if we can derive it from javac's location.
|
||||
var maybeJavaHome = path.dirname(path.dirname(javacPath));
|
||||
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
|
||||
process.env.JAVA_HOME = maybeJavaHome;
|
||||
} else {
|
||||
throw new CordovaError(default_java_error_msg);
|
||||
}
|
||||
}
|
||||
} else if (module.exports.isWindows()) {
|
||||
const programFilesEnv = path.resolve(process.env.ProgramFiles);
|
||||
const programFiles = 'C:\\Program Files\\';
|
||||
const programFilesx86 = 'C:\\Program Files (x86)\\';
|
||||
|
||||
let firstJdkDir =
|
||||
getJDKDirectory(programFilesEnv) ||
|
||||
getJDKDirectory(programFiles) ||
|
||||
getJDKDirectory(programFilesx86);
|
||||
|
||||
if (firstJdkDir) {
|
||||
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
|
||||
if (!javacPath) {
|
||||
process.env.PATH += path.delimiter + path.join(firstJdkDir, 'bin');
|
||||
}
|
||||
process.env.JAVA_HOME = firstJdkDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(function () {
|
||||
return execa('javac', ['-version'], { all: true })
|
||||
.then(({ all: output }) => {
|
||||
// Java <= 8 writes version info to stderr, Java >= 9 to stdout
|
||||
const match = /javac\s+([\d.]+)/i.exec(output);
|
||||
return match && match[1];
|
||||
}, () => {
|
||||
var msg =
|
||||
'Failed to run "javac -version", make sure that you have a JDK version 8 installed.\n' +
|
||||
'You can get it from the following location:\n' +
|
||||
'https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html';
|
||||
if (process.env.JAVA_HOME) {
|
||||
msg += '\n\n';
|
||||
msg += 'Your JAVA_HOME is invalid: ' + process.env.JAVA_HOME;
|
||||
}
|
||||
throw new CordovaError(msg);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_android = function () {
|
||||
return Promise.resolve().then(function () {
|
||||
function maybeSetAndroidHome (value) {
|
||||
if (!hasAndroidHome && fs.existsSync(value)) {
|
||||
hasAndroidHome = true;
|
||||
process.env.ANDROID_SDK_ROOT = value;
|
||||
}
|
||||
}
|
||||
|
||||
var androidCmdPath = forgivingWhichSync('android');
|
||||
var adbInPath = forgivingWhichSync('adb');
|
||||
var avdmanagerInPath = forgivingWhichSync('avdmanager');
|
||||
var hasAndroidHome = false;
|
||||
|
||||
if (process.env.ANDROID_SDK_ROOT) {
|
||||
maybeSetAndroidHome(path.resolve(process.env.ANDROID_SDK_ROOT));
|
||||
}
|
||||
|
||||
// First ensure ANDROID_HOME is set
|
||||
// If we have no hints (nothing in PATH), try a few default locations
|
||||
if (!hasAndroidHome && !androidCmdPath && !adbInPath && !avdmanagerInPath) {
|
||||
if (process.env.ANDROID_HOME) {
|
||||
// Fallback to deprecated `ANDROID_HOME` variable
|
||||
maybeSetAndroidHome(path.join(process.env.ANDROID_HOME));
|
||||
}
|
||||
if (module.exports.isWindows()) {
|
||||
// Android Studio 1.0 installer
|
||||
if (process.env.LOCALAPPDATA) {
|
||||
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'sdk'));
|
||||
}
|
||||
if (process.env.ProgramFiles) {
|
||||
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'sdk'));
|
||||
}
|
||||
|
||||
// Android Studio pre-1.0 installer
|
||||
if (process.env.LOCALAPPDATA) {
|
||||
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'android-studio', 'sdk'));
|
||||
}
|
||||
if (process.env.ProgramFiles) {
|
||||
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'android-studio', 'sdk'));
|
||||
}
|
||||
|
||||
// Stand-alone installer
|
||||
if (process.env.LOCALAPPDATA) {
|
||||
maybeSetAndroidHome(path.join(process.env.LOCALAPPDATA, 'Android', 'android-sdk'));
|
||||
}
|
||||
if (process.env.ProgramFiles) {
|
||||
maybeSetAndroidHome(path.join(process.env.ProgramFiles, 'Android', 'android-sdk'));
|
||||
}
|
||||
} else if (module.exports.isDarwin()) {
|
||||
// Android Studio 1.0 installer
|
||||
if (process.env.HOME) {
|
||||
maybeSetAndroidHome(path.join(process.env.HOME, 'Library', 'Android', 'sdk'));
|
||||
}
|
||||
// Android Studio pre-1.0 installer
|
||||
maybeSetAndroidHome('/Applications/Android Studio.app/sdk');
|
||||
// Stand-alone zip file that user might think to put under /Applications
|
||||
maybeSetAndroidHome('/Applications/android-sdk-macosx');
|
||||
maybeSetAndroidHome('/Applications/android-sdk');
|
||||
}
|
||||
if (process.env.HOME) {
|
||||
// Stand-alone zip file that user might think to put under their home directory
|
||||
maybeSetAndroidHome(path.join(process.env.HOME, 'android-sdk-macosx'));
|
||||
maybeSetAndroidHome(path.join(process.env.HOME, 'android-sdk'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasAndroidHome) {
|
||||
// If we dont have ANDROID_SDK_ROOT, but we do have some tools on the PATH, try to infer from the tooling PATH.
|
||||
var parentDir, grandParentDir;
|
||||
if (androidCmdPath) {
|
||||
parentDir = path.dirname(androidCmdPath);
|
||||
grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) === 'tools' || fs.existsSync(path.join(grandParentDir, 'tools', 'android'))) {
|
||||
maybeSetAndroidHome(grandParentDir);
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
|
||||
'Detected \'android\' command at ' + parentDir + ' but no \'tools\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools directory.');
|
||||
}
|
||||
}
|
||||
if (adbInPath) {
|
||||
parentDir = path.dirname(adbInPath);
|
||||
grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) === 'platform-tools') {
|
||||
maybeSetAndroidHome(grandParentDir);
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
|
||||
'Detected \'adb\' command at ' + parentDir + ' but no \'platform-tools\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'platform-tools directory.');
|
||||
}
|
||||
}
|
||||
if (avdmanagerInPath) {
|
||||
parentDir = path.dirname(avdmanagerInPath);
|
||||
grandParentDir = path.dirname(parentDir);
|
||||
if (path.basename(parentDir) === 'bin' && path.basename(grandParentDir) === 'tools') {
|
||||
maybeSetAndroidHome(path.dirname(grandParentDir));
|
||||
} else {
|
||||
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
|
||||
'Detected \'avdmanager\' command at ' + parentDir + ' but no \'tools' + path.sep + 'bin\' directory found near.\n' +
|
||||
'Try reinstall Android SDK or update your PATH to include valid path to SDK' + path.sep + 'tools' + path.sep + 'bin directory.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!process.env.ANDROID_SDK_ROOT) {
|
||||
throw new CordovaError('Failed to find \'ANDROID_SDK_ROOT\' environment variable. Try setting it manually.\n' +
|
||||
'Failed to find \'android\' command in your \'PATH\'. Try update your \'PATH\' to include path to valid SDK directory.');
|
||||
}
|
||||
if (!fs.existsSync(process.env.ANDROID_SDK_ROOT)) {
|
||||
throw new CordovaError('\'ANDROID_SDK_ROOT\' environment variable is set to non-existent path: ' + process.env.ANDROID_SDK_ROOT +
|
||||
'\nTry update it manually to point to valid SDK directory.');
|
||||
}
|
||||
// Next let's make sure relevant parts of the SDK tooling is in our PATH
|
||||
if (hasAndroidHome && !androidCmdPath) {
|
||||
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools');
|
||||
}
|
||||
if (hasAndroidHome && !adbInPath) {
|
||||
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'platform-tools');
|
||||
}
|
||||
if (hasAndroidHome && !avdmanagerInPath) {
|
||||
process.env.PATH += path.delimiter + path.join(process.env.ANDROID_SDK_ROOT, 'tools', 'bin');
|
||||
}
|
||||
return hasAndroidHome;
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: is this actually needed?
|
||||
module.exports.getAbsoluteAndroidCmd = function () {
|
||||
var cmd = forgivingWhichSync('android');
|
||||
if (cmd.length === 0) {
|
||||
cmd = forgivingWhichSync('sdkmanager');
|
||||
}
|
||||
if (module.exports.isWindows()) {
|
||||
return '"' + cmd + '"';
|
||||
}
|
||||
return cmd.replace(/(\s)/g, '\\$1');
|
||||
};
|
||||
|
||||
module.exports.check_android_target = function (originalError) {
|
||||
// valid_target can look like:
|
||||
// android-19
|
||||
// android-L
|
||||
// Google Inc.:Google APIs:20
|
||||
// Google Inc.:Glass Development Kit Preview:20
|
||||
var desired_api_level = module.exports.get_target();
|
||||
return android_sdk.list_targets().then(function (targets) {
|
||||
if (targets.indexOf(desired_api_level) >= 0) {
|
||||
return targets;
|
||||
}
|
||||
var androidCmd = module.exports.getAbsoluteAndroidCmd();
|
||||
var msg = 'Please install Android target / API level: "' + desired_api_level + '".\n\n' +
|
||||
'Hint: Open the SDK manager by running: ' + androidCmd + '\n' +
|
||||
'You will require:\n' +
|
||||
'1. "SDK Platform" for API level ' + desired_api_level + '\n' +
|
||||
'2. "Android SDK Platform-tools (latest)\n' +
|
||||
'3. "Android SDK Build-tools" (latest)';
|
||||
if (originalError) {
|
||||
msg = originalError + '\n' + msg;
|
||||
}
|
||||
throw new CordovaError(msg);
|
||||
});
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.run = function () {
|
||||
console.log('Checking Java JDK and Android SDK versions');
|
||||
console.log('ANDROID_SDK_ROOT=' + process.env.ANDROID_SDK_ROOT + ' (recommended setting)');
|
||||
console.log('ANDROID_HOME=' + process.env.ANDROID_HOME + ' (DEPRECATED)');
|
||||
|
||||
return Promise.all([this.check_java(), this.check_android()]).then(function (values) {
|
||||
console.log('Using Android SDK: ' + process.env.ANDROID_SDK_ROOT);
|
||||
|
||||
if (!String(values[0]).startsWith('1.8.')) {
|
||||
throw new CordovaError(
|
||||
'Requirements check failed for JDK 8 (\'1.8.*\')! Detected version: ' + values[0] + '\n' +
|
||||
'Check your ANDROID_SDK_ROOT / JAVA_HOME / PATH environment variables.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!values[1]) {
|
||||
throw new CordovaError('Requirements check failed for Android SDK! Android SDK was not detected.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Object thar represents one of requirements for current platform.
|
||||
* @param {String} id The unique identifier for this requirements.
|
||||
* @param {String} name The name of requirements. Human-readable field.
|
||||
* @param {String} version The version of requirement installed. In some cases could be an array of strings
|
||||
* (for example, check_android_target returns an array of android targets installed)
|
||||
* @param {Boolean} installed Indicates whether the requirement is installed or not
|
||||
*/
|
||||
var Requirement = function (id, name, version, installed) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.installed = installed || false;
|
||||
this.metadata = {
|
||||
version: version
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Methods that runs all checks one by one and returns a result of checks
|
||||
* as an array of Requirement objects. This method intended to be used by cordova-lib check_reqs method
|
||||
*
|
||||
* @return Promise<Requirement[]> Array of requirements. Due to implementation, promise is always fulfilled.
|
||||
*/
|
||||
module.exports.check_all = function () {
|
||||
var requirements = [
|
||||
new Requirement('java', 'Java JDK'),
|
||||
new Requirement('androidSdk', 'Android SDK'),
|
||||
new Requirement('androidTarget', 'Android target'),
|
||||
new Requirement('gradle', 'Gradle')
|
||||
];
|
||||
|
||||
var checkFns = [
|
||||
this.check_java,
|
||||
this.check_android,
|
||||
this.check_android_target,
|
||||
this.check_gradle
|
||||
];
|
||||
|
||||
// Then execute requirement checks one-by-one
|
||||
return checkFns.reduce(function (promise, checkFn, idx) {
|
||||
// Update each requirement with results
|
||||
var requirement = requirements[idx];
|
||||
return promise.then(checkFn).then(function (version) {
|
||||
requirement.installed = true;
|
||||
requirement.metadata.version = version;
|
||||
}, function (err) {
|
||||
requirement.metadata.reason = err instanceof Error ? err.message : err;
|
||||
});
|
||||
}, Promise.resolve()).then(function () {
|
||||
// When chain is completed, return requirements array to upstream API
|
||||
return requirements;
|
||||
});
|
||||
};
|
||||
150
bin/templates/cordova/lib/config/GradlePropertiesParser.js
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const propertiesParser = require('properties-parser');
|
||||
const events = require('cordova-common').events;
|
||||
|
||||
class GradlePropertiesParser {
|
||||
/**
|
||||
* Loads and Edits Gradle Properties File.
|
||||
*
|
||||
* @param {String} platformDir is the path of the Android platform directory
|
||||
*/
|
||||
constructor (platformDir) {
|
||||
this._defaults = {
|
||||
// 10 seconds -> 6 seconds
|
||||
'org.gradle.daemon': 'true',
|
||||
|
||||
// to allow dex in process
|
||||
'org.gradle.jvmargs': '-Xmx2048m',
|
||||
|
||||
// Android X
|
||||
'android.useAndroidX': 'false',
|
||||
'android.enableJetifier': 'false'
|
||||
|
||||
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
||||
// 'org.gradle.parallel': 'true'
|
||||
};
|
||||
|
||||
this.gradleFilePath = path.join(platformDir, 'gradle.properties');
|
||||
}
|
||||
|
||||
configure (userConfigs) {
|
||||
events.emit('verbose', '[Gradle Properties] Preparing Configuration');
|
||||
|
||||
this._initializeEditor();
|
||||
|
||||
events.emit('verbose', '[Gradle Properties] Appending default configuration properties');
|
||||
this._configureProperties(this._defaults);
|
||||
|
||||
events.emit('verbose', '[Gradle Properties] Appending custom configuration properties');
|
||||
this._configureProperties(userConfigs);
|
||||
|
||||
this._save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the properties editor for parsing, setting, etc.
|
||||
*/
|
||||
_initializeEditor () {
|
||||
// Touch empty gradle.properties file if missing.
|
||||
if (!fs.existsSync(this.gradleFilePath)) {
|
||||
events.emit('verbose', '[Gradle Properties] File missing, creating file with Cordova defaults.');
|
||||
fs.writeFileSync(this.gradleFilePath, '', 'utf-8');
|
||||
}
|
||||
|
||||
// Create an editor for parsing, getting, and setting configurations.
|
||||
this.gradleFile = propertiesParser.createEditor(this.gradleFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that defaults or user configuration properties are set and
|
||||
* set the missing items.
|
||||
*/
|
||||
_configureProperties (properties) {
|
||||
// Iterate though the properties and set only if missing.
|
||||
Object.keys(properties).forEach(key => {
|
||||
const value = this.gradleFile.get(key);
|
||||
|
||||
if (!value) {
|
||||
// Handles the case of adding missing defaults or new properties that are missing.
|
||||
events.emit('verbose', `[Gradle Properties] Appending configuration item: ${key}=${properties[key]}`);
|
||||
this.gradleFile.set(key, properties[key]);
|
||||
} else if (value !== properties[key]) {
|
||||
if (this._defaults[key] && this._defaults[key] !== properties[key]) {
|
||||
let shouldEmit = true;
|
||||
if (key === 'org.gradle.jvmargs') {
|
||||
shouldEmit = this._isJVMMemoryLessThanRecommended(properties[key], this._defaults[key]);
|
||||
}
|
||||
|
||||
if (shouldEmit) {
|
||||
// Since the value does not match default, we will notify the discrepancy with Cordova's recommended value.
|
||||
events.emit('info', `[Gradle Properties] Detected Gradle property "${key}" with the value of "${properties[key]}", Cordova's recommended value is "${this._defaults[key]}"`);
|
||||
}
|
||||
} else {
|
||||
// When the current value exists but does not match the new value or does matches the default key value, the new value it set.
|
||||
events.emit('verbose', `[Gradle Properties] Updating Gradle property "${key}" with the value of "${properties[key]}"`);
|
||||
}
|
||||
|
||||
// We will set the new value in either case.
|
||||
this.gradleFile.set(key, properties[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_isJVMMemoryLessThanRecommended (memoryValue, recommendedMemoryValue) {
|
||||
const UNIT = 2;
|
||||
const SIZE = 1;
|
||||
const regex = /-Xmx+([0-9]+)+([mMgGkK])/;
|
||||
|
||||
const recommendedCapture = regex.exec(recommendedMemoryValue);
|
||||
const recommendedBase = this._getBaseJVMSize(recommendedCapture[SIZE], recommendedCapture[UNIT]);
|
||||
const memoryCapture = regex.exec(memoryValue);
|
||||
const memoryBase = this._getBaseJVMSize(memoryCapture[SIZE], memoryCapture[UNIT]);
|
||||
|
||||
return memoryBase < recommendedBase;
|
||||
}
|
||||
|
||||
_getBaseJVMSize (size, unit) {
|
||||
const KILOBYTE = 1024;
|
||||
const MEGABYTE = 1048576;
|
||||
const GIGABYTE = 1073741824;
|
||||
|
||||
switch (unit.toLowerCase()) {
|
||||
case 'k': return size * KILOBYTE;
|
||||
case 'm': return size * MEGABYTE;
|
||||
case 'g': return size * GIGABYTE;
|
||||
}
|
||||
|
||||
events.emit('warn', `[Gradle Properties] Unknown memory size unit (${unit})`);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any changes that has been made to the properties file.
|
||||
*/
|
||||
_save () {
|
||||
events.emit('verbose', '[Gradle Properties] Updating and Saving File');
|
||||
this.gradleFile.save();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GradlePropertiesParser;
|
||||
59
bin/templates/cordova/lib/device.js
vendored
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,12 +17,11 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q'),
|
||||
build = require('./build');
|
||||
const execa = require('execa');
|
||||
var build = require('./build');
|
||||
var path = require('path');
|
||||
var Adb = require('./Adb');
|
||||
var AndroidManifest = require('./AndroidManifest');
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var events = require('cordova-common').events;
|
||||
|
||||
@@ -32,18 +29,16 @@ var events = require('cordova-common').events;
|
||||
* Returns a promise for the list of the device ID's found
|
||||
* @param lookHarder When true, try restarting adb if no devices are found.
|
||||
*/
|
||||
module.exports.list = function(lookHarder) {
|
||||
return Adb.devices()
|
||||
.then(function(list) {
|
||||
module.exports.list = function (lookHarder) {
|
||||
return Adb.devices().then(function (list) {
|
||||
if (list.length === 0 && lookHarder) {
|
||||
// adb kill-server doesn't seem to do the trick.
|
||||
// Could probably find a x-platform version of killall, but I'm not actually
|
||||
// sure that this scenario even happens on non-OSX machines.
|
||||
return spawn('killall', ['adb'])
|
||||
.then(function() {
|
||||
return execa('killall', ['adb']).then(function () {
|
||||
events.emit('verbose', 'Restarting adb to see if more devices are detected.');
|
||||
return Adb.devices();
|
||||
}, function() {
|
||||
}, function () {
|
||||
// For non-killall OS's.
|
||||
return list;
|
||||
});
|
||||
@@ -52,21 +47,19 @@ module.exports.list = function(lookHarder) {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.resolveTarget = function(target) {
|
||||
return this.list(true)
|
||||
.then(function(device_list) {
|
||||
module.exports.resolveTarget = function (target) {
|
||||
return this.list(true).then(function (device_list) {
|
||||
if (!device_list || !device_list.length) {
|
||||
return Q.reject(new CordovaError('Failed to deploy to device, no devices found.'));
|
||||
return Promise.reject(new CordovaError('Failed to deploy to device, no devices found.'));
|
||||
}
|
||||
// default device
|
||||
target = target || device_list[0];
|
||||
|
||||
if (device_list.indexOf(target) < 0) {
|
||||
return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
|
||||
return Promise.reject(new CordovaError('ERROR: Unable to find target \'' + target + '\'.'));
|
||||
}
|
||||
|
||||
return build.detectArchitecture(target)
|
||||
.then(function(arch) {
|
||||
return build.detectArchitecture(target).then(function (arch) {
|
||||
return { target: target, arch: arch, isEmulator: false };
|
||||
});
|
||||
});
|
||||
@@ -77,43 +70,39 @@ module.exports.resolveTarget = function(target) {
|
||||
* and launches it.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.install = function(target, buildResults) {
|
||||
return Q().then(function() {
|
||||
if (target && typeof target == 'object') {
|
||||
module.exports.install = function (target, buildResults) {
|
||||
return Promise.resolve().then(function () {
|
||||
if (target && typeof target === 'object') {
|
||||
return target;
|
||||
}
|
||||
return module.exports.resolveTarget(target);
|
||||
}).then(function(resolvedTarget) {
|
||||
}).then(function (resolvedTarget) {
|
||||
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
|
||||
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
|
||||
var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
|
||||
var pkgName = manifest.getPackageId();
|
||||
var launchName = pkgName + '/.' + manifest.getActivity().getName();
|
||||
events.emit('log', 'Using apk: ' + apk_path);
|
||||
events.emit('log', 'Package name: ' + pkgName);
|
||||
|
||||
return Adb.install(resolvedTarget.target, apk_path, {replace: true})
|
||||
.catch(function (error) {
|
||||
return Adb.install(resolvedTarget.target, apk_path, { replace: true }).catch(function (error) {
|
||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
||||
// is already installed on device was signed w/different certificate
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
|
||||
throw error;
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
||||
|
||||
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' +
|
||||
'installed app already signed with different key');
|
||||
|
||||
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
|
||||
// or the app doesn't installed at all, so no error catching needed.
|
||||
return Adb.uninstall(resolvedTarget.target, pkgName)
|
||||
.then(function() {
|
||||
return Adb.install(resolvedTarget.target, apk_path, {replace: true});
|
||||
return Adb.uninstall(resolvedTarget.target, pkgName).then(function () {
|
||||
return Adb.install(resolvedTarget.target, apk_path, { replace: true });
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
//unlock screen
|
||||
}).then(function () {
|
||||
// unlock screen
|
||||
return Adb.shell(resolvedTarget.target, 'input keyevent 82');
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
return Adb.start(resolvedTarget.target, launchName);
|
||||
}).then(function() {
|
||||
}).then(function () {
|
||||
events.emit('log', 'LAUNCH SUCCESS');
|
||||
});
|
||||
});
|
||||
|
||||
436
bin/templates/cordova/lib/emulator.js
vendored
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,69 +17,83 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint sub:true */
|
||||
|
||||
var retry = require('./retry');
|
||||
var build = require('./build');
|
||||
const execa = require('execa');
|
||||
const fs = require('fs-extra');
|
||||
var android_versions = require('android-versions');
|
||||
var retry = require('./retry');
|
||||
var build = require('./build');
|
||||
var path = require('path');
|
||||
var Adb = require('./Adb');
|
||||
var AndroidManifest = require('./AndroidManifest');
|
||||
var events = require('cordova-common').events;
|
||||
var spawn = require('cordova-common').superspawn.spawn;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var Q = require('q');
|
||||
var os = require('os');
|
||||
var child_process = require('child_process');
|
||||
var android_sdk = require('./android_sdk');
|
||||
var check_reqs = require('./check_reqs');
|
||||
var which = require('which');
|
||||
var os = require('os');
|
||||
|
||||
// constants
|
||||
var ONE_SECOND = 1000; // in milliseconds
|
||||
var ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
|
||||
var INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
|
||||
var NUM_INSTALL_RETRIES = 3;
|
||||
var CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
|
||||
var EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||
const ONE_SECOND = 1000; // in milliseconds
|
||||
const ONE_MINUTE = 60 * ONE_SECOND; // in milliseconds
|
||||
const INSTALL_COMMAND_TIMEOUT = 5 * ONE_MINUTE; // in milliseconds
|
||||
const NUM_INSTALL_RETRIES = 3;
|
||||
const CHECK_BOOTED_INTERVAL = 3 * ONE_SECOND; // in milliseconds
|
||||
const EXEC_KILL_SIGNAL = 'SIGKILL';
|
||||
|
||||
/**
|
||||
* Returns a Promise for a list of emulator images in the form of objects
|
||||
* {
|
||||
name : <emulator_name>,
|
||||
path : <path_to_emulator_image>,
|
||||
target : <api_target>,
|
||||
abi : <cpu>,
|
||||
skin : <skin>
|
||||
}
|
||||
*/
|
||||
module.exports.list_images = function() {
|
||||
return spawn('android', ['list', 'avds'])
|
||||
.then(function(output) {
|
||||
function forgivingWhichSync (cmd) {
|
||||
const whichResult = which.sync(cmd, { nothrow: true });
|
||||
|
||||
// On null, returns empty string to maintain backwards compatibility
|
||||
// realpathSync follows symlinks
|
||||
return whichResult === null ? '' : fs.realpathSync(whichResult);
|
||||
}
|
||||
|
||||
module.exports.list_images_using_avdmanager = function () {
|
||||
return execa('avdmanager', ['list', 'avd']).then(({ stdout: output }) => {
|
||||
var response = output.split('\n');
|
||||
var emulator_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
// To return more detailed information use img_obj
|
||||
var img_obj = {};
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
|
||||
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
|
||||
if (response[i + 1].match(/Device:\s/)) {
|
||||
i++;
|
||||
img_obj['device'] = response[i].split('Device: ')[1].replace('\r', '');
|
||||
img_obj.device = response[i].split('Device: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/Path:\s/)) {
|
||||
i++;
|
||||
img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
|
||||
img_obj.path = response[i].split('Path: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) {
|
||||
if (response[i + 1].match(/Target:\s/)) {
|
||||
i++;
|
||||
var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : '';
|
||||
img_obj['target'] = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/ABI:\s/)) {
|
||||
i++;
|
||||
img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
|
||||
if (response[i + 1].match(/ABI:\s/)) {
|
||||
img_obj.abi = response[i + 1].split('ABI: ')[1].replace('\r', '');
|
||||
}
|
||||
// This next conditional just aims to match the old output of `android list avd`
|
||||
// We do so so that we don't have to change the logic when parsing for the
|
||||
// best emulator target to spawn (see below in `best_image`)
|
||||
// This allows us to transitionally support both `android` and `avdmanager` binaries,
|
||||
// depending on what SDK version the user has
|
||||
if (response[i + 1].match(/Based\son:\s/)) {
|
||||
img_obj.target = response[i + 1].split('Based on:')[1];
|
||||
if (img_obj.target.match(/Tag\/ABI:\s/)) {
|
||||
img_obj.target = img_obj.target.split('Tag/ABI:')[0].replace('\r', '').trim();
|
||||
if (img_obj.target.indexOf('(') > -1) {
|
||||
img_obj.target = img_obj.target.substr(0, img_obj.target.indexOf('(') - 1).trim();
|
||||
}
|
||||
}
|
||||
var version_string = img_obj.target.replace(/Android\s+/, '');
|
||||
|
||||
var api_level = android_sdk.version_string_to_api_level[version_string];
|
||||
if (api_level) {
|
||||
img_obj.target += ' (API level ' + api_level + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (response[i + 1].match(/Skin:\s/)) {
|
||||
i++;
|
||||
img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
|
||||
img_obj.skin = response[i].split('Skin: ')[1].replace('\r', '');
|
||||
}
|
||||
|
||||
emulator_list.push(img_obj);
|
||||
@@ -89,33 +101,109 @@ module.exports.list_images = function() {
|
||||
/* To just return a list of names use this
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
|
||||
}*/
|
||||
|
||||
} */
|
||||
}
|
||||
return emulator_list;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.list_images_using_android = function () {
|
||||
return execa('android', ['list', 'avd']).then(({ stdout: output }) => {
|
||||
var response = output.split('\n');
|
||||
var emulator_list = [];
|
||||
for (var i = 1; i < response.length; i++) {
|
||||
// To return more detailed information use img_obj
|
||||
var img_obj = {};
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
img_obj.name = response[i].split('Name: ')[1].replace('\r', '');
|
||||
if (response[i + 1].match(/Device:\s/)) {
|
||||
i++;
|
||||
img_obj.device = response[i].split('Device: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/Path:\s/)) {
|
||||
i++;
|
||||
img_obj.path = response[i].split('Path: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/\(API\slevel\s/) || (response[i + 2] && response[i + 2].match(/\(API\slevel\s/))) {
|
||||
i++;
|
||||
var secondLine = response[i + 1].match(/\(API\slevel\s/) ? response[i + 1] : '';
|
||||
img_obj.target = (response[i] + secondLine).split('Target: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/ABI:\s/)) {
|
||||
i++;
|
||||
img_obj.abi = response[i].split('ABI: ')[1].replace('\r', '');
|
||||
}
|
||||
if (response[i + 1].match(/Skin:\s/)) {
|
||||
i++;
|
||||
img_obj.skin = response[i].split('Skin: ')[1].replace('\r', '');
|
||||
}
|
||||
|
||||
emulator_list.push(img_obj);
|
||||
}
|
||||
/* To just return a list of names use this
|
||||
if (response[i].match(/Name:\s/)) {
|
||||
emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
|
||||
} */
|
||||
}
|
||||
return emulator_list;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a Promise for a list of emulator images in the form of objects
|
||||
* {
|
||||
name : <emulator_name>,
|
||||
device : <device>,
|
||||
path : <path_to_emulator_image>,
|
||||
target : <api_target>,
|
||||
abi : <cpu>,
|
||||
skin : <skin>
|
||||
}
|
||||
*/
|
||||
module.exports.list_images = function () {
|
||||
return Promise.resolve().then(function () {
|
||||
if (forgivingWhichSync('avdmanager')) {
|
||||
return module.exports.list_images_using_avdmanager();
|
||||
} else if (forgivingWhichSync('android')) {
|
||||
return module.exports.list_images_using_android();
|
||||
} else {
|
||||
return Promise.reject(new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'));
|
||||
}
|
||||
}).then(function (avds) {
|
||||
// In case we're missing the Android OS version string from the target description, add it.
|
||||
return avds.map(function (avd) {
|
||||
if (avd.target && avd.target.indexOf('Android API') > -1 && avd.target.indexOf('API level') < 0) {
|
||||
var api_level = avd.target.match(/\d+/);
|
||||
if (api_level) {
|
||||
var level = android_versions.get(api_level);
|
||||
if (level) {
|
||||
avd.target = 'Android ' + level.semver + ' (API level ' + api_level + ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
return avd;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Will return the closest avd to the projects target
|
||||
* or undefined if no avds exist.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.best_image = function() {
|
||||
return this.list_images()
|
||||
.then(function(images) {
|
||||
module.exports.best_image = function () {
|
||||
return this.list_images().then(function (images) {
|
||||
// Just return undefined if there is no images
|
||||
if (images.length === 0) return;
|
||||
|
||||
var closest = 9999;
|
||||
var best = images[0];
|
||||
// Loading check_reqs at run-time to avoid test-time vs run-time directory structure difference issue
|
||||
var project_target = require('./check_reqs').get_target().replace('android-', '');
|
||||
var project_target = parseInt(check_reqs.get_target().replace('android-', ''));
|
||||
for (var i in images) {
|
||||
var target = images[i].target;
|
||||
if(target) {
|
||||
var num = target.split('(API level ')[1].replace(')', '');
|
||||
if (num == project_target) {
|
||||
if (target && target.indexOf('API level') > -1) {
|
||||
var num = parseInt(target.split('(API level ')[1].replace(')', ''));
|
||||
if (num === project_target) {
|
||||
return images[i];
|
||||
} else if (project_target - num < closest && project_target > num) {
|
||||
closest = project_target - num;
|
||||
@@ -128,18 +216,18 @@ module.exports.best_image = function() {
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.list_started = function() {
|
||||
return Adb.devices({emulators: true});
|
||||
module.exports.list_started = function () {
|
||||
return Adb.devices({ emulators: true });
|
||||
};
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.list_targets = function() {
|
||||
return spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
|
||||
.then(function(output) {
|
||||
// TODO: we should remove this, there's a more robust method under android_sdk.js
|
||||
module.exports.list_targets = function () {
|
||||
return execa('android', ['list', 'targets'], { cwd: os.tmpdir() }).then(({ stdout: output }) => {
|
||||
var target_out = output.split('\n');
|
||||
var targets = [];
|
||||
for (var i = target_out.length; i >= 0; i--) {
|
||||
if(target_out[i].match(/id:/)) {
|
||||
if (target_out[i].match(/id:/)) {
|
||||
targets.push(targets[i].split(' ')[1]);
|
||||
}
|
||||
}
|
||||
@@ -147,6 +235,24 @@ module.exports.list_targets = function() {
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets unused port for android emulator, between 5554 and 5584
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.get_available_port = function () {
|
||||
var self = this;
|
||||
|
||||
return self.list_started().then(function (emulators) {
|
||||
for (var p = 5584; p >= 5554; p -= 2) {
|
||||
if (emulators.indexOf('emulator-' + p) === -1) {
|
||||
events.emit('verbose', 'Found available port: ' + p);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
throw new CordovaError('Could not find an available avd port');
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Starts an emulator with the given ID,
|
||||
* and returns the started ID of that emulator.
|
||||
@@ -157,52 +263,49 @@ module.exports.list_targets = function() {
|
||||
*
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.start = function(emulator_ID, boot_timeout) {
|
||||
module.exports.start = function (emulator_ID, boot_timeout) {
|
||||
var self = this;
|
||||
|
||||
return Q().then(function() {
|
||||
if (emulator_ID) return Q(emulator_ID);
|
||||
return Promise.resolve().then(function () {
|
||||
if (emulator_ID) return Promise.resolve(emulator_ID);
|
||||
|
||||
return self.best_image()
|
||||
.then(function(best) {
|
||||
return self.best_image().then(function (best) {
|
||||
if (best && best.name) {
|
||||
events.emit('warn', 'No emulator specified, defaulting to ' + best.name);
|
||||
return best.name;
|
||||
}
|
||||
|
||||
// Loading check_reqs at run-time to avoid test-time vs run-time directory structure difference issue
|
||||
var androidCmd = require('./check_reqs').getAbsoluteAndroidCmd();
|
||||
return Q.reject(new CordovaError('No emulator images (avds) found.\n' +
|
||||
var androidCmd = check_reqs.getAbsoluteAndroidCmd();
|
||||
return Promise.reject(new CordovaError('No emulator images (avds) found.\n' +
|
||||
'1. Download desired System Image by running: ' + androidCmd + ' sdk\n' +
|
||||
'2. Create an AVD by running: ' + androidCmd + ' avd\n' +
|
||||
'HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver\n'));
|
||||
});
|
||||
}).then(function(emulatorId) {
|
||||
var uuid = 'cordova_emulator_' + new Date().getTime();
|
||||
var uuidProp = 'emu.uuid=' + uuid;
|
||||
var args = ['-avd', emulatorId, '-prop', uuidProp];
|
||||
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
|
||||
child_process
|
||||
.spawn('emulator', args, { stdio: 'inherit', detached: true })
|
||||
.unref();
|
||||
}).then(function (emulatorId) {
|
||||
return self.get_available_port().then(function (port) {
|
||||
// Figure out the directory the emulator binary runs in, and set the cwd to that directory.
|
||||
// Workaround for https://code.google.com/p/android/issues/detail?id=235461
|
||||
var emulator_dir = path.dirname(which.sync('emulator'));
|
||||
var args = ['-avd', emulatorId, '-port', port];
|
||||
// Don't wait for it to finish, since the emulator will probably keep running for a long time.
|
||||
execa('emulator', args, { stdio: 'inherit', detached: true, cwd: emulator_dir })
|
||||
.unref();
|
||||
|
||||
// wait for emulator to start
|
||||
events.emit('log', 'Waiting for emulator to start...');
|
||||
return self.wait_for_emulator(uuid);
|
||||
}).then(function(emulatorId) {
|
||||
if (!emulatorId)
|
||||
return Q.reject(new CordovaError('Failed to start emulator'));
|
||||
// wait for emulator to start
|
||||
events.emit('log', 'Waiting for emulator to start...');
|
||||
return self.wait_for_emulator(port);
|
||||
});
|
||||
}).then(function (emulatorId) {
|
||||
if (!emulatorId) { return Promise.reject(new CordovaError('Failed to start emulator')); }
|
||||
|
||||
//wait for emulator to boot up
|
||||
// wait for emulator to boot up
|
||||
process.stdout.write('Waiting for emulator to boot (this may take a while)...');
|
||||
return self.wait_for_boot(emulatorId, boot_timeout)
|
||||
.then(function(success) {
|
||||
return self.wait_for_boot(emulatorId, boot_timeout).then(function (success) {
|
||||
if (success) {
|
||||
events.emit('log','BOOT COMPLETE');
|
||||
//unlock screen
|
||||
return Adb.shell(emulatorId, 'input keyevent 82')
|
||||
.then(function() {
|
||||
//return the new emulator id for the started emulators
|
||||
events.emit('log', 'BOOT COMPLETE');
|
||||
// unlock screen
|
||||
return Adb.shell(emulatorId, 'input keyevent 82').then(function () {
|
||||
// return the new emulator id for the started emulators
|
||||
return emulatorId;
|
||||
});
|
||||
} else {
|
||||
@@ -214,31 +317,32 @@ module.exports.start = function(emulator_ID, boot_timeout) {
|
||||
};
|
||||
|
||||
/*
|
||||
* Waits for an emulator with given uuid to apear on the started-emulator list.
|
||||
* Returns a promise with this emulator's ID.
|
||||
* Waits for an emulator to boot on a given port.
|
||||
* Returns this emulator's ID in a promise.
|
||||
*/
|
||||
module.exports.wait_for_emulator = function(uuid) {
|
||||
module.exports.wait_for_emulator = function (port) {
|
||||
var self = this;
|
||||
return self.list_started()
|
||||
.then(function(new_started) {
|
||||
var emulator_id = null;
|
||||
var promises = [];
|
||||
|
||||
new_started.forEach(function (emulator) {
|
||||
promises.push(
|
||||
Adb.shell(emulator, 'getprop emu.uuid')
|
||||
.then(function (output) {
|
||||
if (output.indexOf(uuid) >= 0) {
|
||||
emulator_id = emulator;
|
||||
}
|
||||
})
|
||||
);
|
||||
return Promise.resolve().then(function () {
|
||||
var emulator_id = 'emulator-' + port;
|
||||
return Adb.shell(emulator_id, 'getprop dev.bootcomplete').then(function (output) {
|
||||
if (output.indexOf('1') >= 0) {
|
||||
return emulator_id;
|
||||
}
|
||||
return self.wait_for_emulator(port);
|
||||
}, function (error) {
|
||||
if ((error && error.message &&
|
||||
(error.message.indexOf('not found') > -1)) ||
|
||||
(error.message.indexOf('device offline') > -1) ||
|
||||
(error.message.indexOf('device still connecting') > -1) ||
|
||||
(error.message.indexOf('device still authorizing') > -1)) {
|
||||
// emulator not yet started, continue waiting
|
||||
return self.wait_for_emulator(port);
|
||||
} else {
|
||||
// something unexpected has happened
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return Q.all(promises).then(function () {
|
||||
return emulator_id || self.wait_for_emulator(uuid);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -246,21 +350,23 @@ module.exports.wait_for_emulator = function(uuid) {
|
||||
* promise that resolves to a boolean indicating success. Not specifying a
|
||||
* time_remaining or passing a negative value will cause it to wait forever
|
||||
*/
|
||||
module.exports.wait_for_boot = function(emulator_id, time_remaining) {
|
||||
module.exports.wait_for_boot = function (emulator_id, time_remaining) {
|
||||
var self = this;
|
||||
return Adb.shell(emulator_id, 'ps')
|
||||
.then(function(output) {
|
||||
if (output.match(/android\.process\.acore/)) {
|
||||
return Adb.shell(emulator_id, 'getprop sys.boot_completed').then(function (output) {
|
||||
if (output.match(/1/)) {
|
||||
return true;
|
||||
} else if (time_remaining === 0) {
|
||||
return false;
|
||||
} else {
|
||||
process.stdout.write('.');
|
||||
|
||||
// Check at regular intervals
|
||||
return Q.delay(time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL).then(function() {
|
||||
var updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
|
||||
return self.wait_for_boot(emulator_id, updated_time);
|
||||
return new Promise(resolve => {
|
||||
const delay = time_remaining < CHECK_BOOTED_INTERVAL ? time_remaining : CHECK_BOOTED_INTERVAL;
|
||||
|
||||
setTimeout(() => {
|
||||
const updated_time = time_remaining >= 0 ? Math.max(time_remaining - CHECK_BOOTED_INTERVAL, 0) : time_remaining;
|
||||
resolve(self.wait_for_boot(emulator_id, updated_time));
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -271,46 +377,43 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
|
||||
* TODO : Enter the stdin input required to complete the creation of an avd.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.create_image = function(name, target) {
|
||||
module.exports.create_image = function (name, target) {
|
||||
console.log('Creating new avd named ' + name);
|
||||
if (target) {
|
||||
return spawn('android', ['create', 'avd', '--name', name, '--target', target])
|
||||
.then(null, function(error) {
|
||||
return execa('android', ['create', 'avd', '--name', name, '--target', target]).then(null, function (error) {
|
||||
console.error('ERROR : Failed to create emulator image : ');
|
||||
console.error(' Do you have the latest android targets including ' + target + '?');
|
||||
console.error(error);
|
||||
console.error(error.message);
|
||||
});
|
||||
} else {
|
||||
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
|
||||
return spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
|
||||
.then(function() {
|
||||
// TODO: there's a more robust method for finding targets in android_sdk.js
|
||||
return execa('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]).then(function () {
|
||||
// TODO: This seems like another error case, even though it always happens.
|
||||
console.error('ERROR : Unable to create an avd emulator, no targets found.');
|
||||
console.error('Ensure you have targets available by running the "android" command');
|
||||
return Q.reject();
|
||||
}, function(error) {
|
||||
return Promise.reject(new CordovaError());
|
||||
}, function (error) {
|
||||
console.error('ERROR : Failed to create emulator image : ');
|
||||
console.error(error);
|
||||
console.error(error.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.resolveTarget = function(target) {
|
||||
return this.list_started()
|
||||
.then(function(emulator_list) {
|
||||
module.exports.resolveTarget = function (target) {
|
||||
return this.list_started().then(function (emulator_list) {
|
||||
if (emulator_list.length < 1) {
|
||||
return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
|
||||
return Promise.reject(new CordovaError('No running Android emulators found, please start an emulator before deploying your project.'));
|
||||
}
|
||||
|
||||
// default emulator
|
||||
target = target || emulator_list[0];
|
||||
if (emulator_list.indexOf(target) < 0) {
|
||||
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
|
||||
return Promise.reject(new CordovaError('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'));
|
||||
}
|
||||
|
||||
return build.detectArchitecture(target)
|
||||
.then(function(arch) {
|
||||
return {target:target, arch:arch, isEmulator:true};
|
||||
return build.detectArchitecture(target).then(function (arch) {
|
||||
return { target: target, arch: arch, isEmulator: true };
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -321,15 +424,16 @@ module.exports.resolveTarget = function(target) {
|
||||
* If no started emulators are found, error out.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.install = function(givenTarget, buildResults) {
|
||||
|
||||
module.exports.install = function (givenTarget, buildResults) {
|
||||
var target;
|
||||
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml'));
|
||||
var pkgName = manifest.getPackageId();
|
||||
// We need to find the proper path to the Android Manifest
|
||||
const manifestPath = path.join(__dirname, '..', '..', 'app', 'src', 'main', 'AndroidManifest.xml');
|
||||
const manifest = new AndroidManifest(manifestPath);
|
||||
const pkgName = manifest.getPackageId();
|
||||
|
||||
// resolve the target emulator
|
||||
return Q().then(function () {
|
||||
if (givenTarget && typeof givenTarget == 'object') {
|
||||
return Promise.resolve().then(function () {
|
||||
if (givenTarget && typeof givenTarget === 'object') {
|
||||
return givenTarget;
|
||||
} else {
|
||||
return module.exports.resolveTarget(givenTarget);
|
||||
@@ -343,13 +447,11 @@ module.exports.install = function(givenTarget, buildResults) {
|
||||
}).then(function () {
|
||||
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
|
||||
// or the app doesn't installed at all, so no error catching needed.
|
||||
return Q.when()
|
||||
.then(function() {
|
||||
|
||||
return Promise.resolve().then(function () {
|
||||
var apk_path = build.findBestApkForArchitecture(buildResults, target.arch);
|
||||
var execOptions = {
|
||||
cwd: os.tmpdir(),
|
||||
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
||||
timeout: INSTALL_COMMAND_TIMEOUT, // in milliseconds
|
||||
killSignal: EXEC_KILL_SIGNAL
|
||||
};
|
||||
|
||||
@@ -360,58 +462,50 @@ module.exports.install = function(givenTarget, buildResults) {
|
||||
// A special function to call adb install in specific environment w/ specific options.
|
||||
// Introduced as a part of fix for http://issues.apache.org/jira/browse/CB-9119
|
||||
// to workaround sporadic emulator hangs
|
||||
function adbInstallWithOptions(target, apk, opts) {
|
||||
function adbInstallWithOptions (target, apk, opts) {
|
||||
events.emit('verbose', 'Installing apk ' + apk + ' on ' + target + '...');
|
||||
|
||||
var command = 'adb -s ' + target + ' install -r "' + apk + '"';
|
||||
return Q.promise(function (resolve, reject) {
|
||||
child_process.exec(command, opts, function(err, stdout, stderr) {
|
||||
if (err) reject(new CordovaError('Error executing "' + command + '": ' + stderr));
|
||||
// adb does not return an error code even if installation fails. Instead it puts a specific
|
||||
// message to stdout, so we have to use RegExp matching to detect installation failure.
|
||||
else if (/Failure/.test(stdout)) {
|
||||
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
|
||||
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
|
||||
' or sign and deploy the unsigned apk manually using Android tools.';
|
||||
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
|
||||
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
|
||||
'\nEither uninstall an app or increment the versionCode.';
|
||||
}
|
||||
const args = ['-s', target, 'install', '-r', apk];
|
||||
return execa('adb', args, opts).then(({ stdout }) => {
|
||||
// adb does not return an error code even if installation fails. Instead it puts a specific
|
||||
// message to stdout, so we have to use RegExp matching to detect installation failure.
|
||||
if (/Failure/.test(stdout)) {
|
||||
if (stdout.match(/INSTALL_PARSE_FAILED_NO_CERTIFICATES/)) {
|
||||
stdout += 'Sign the build using \'-- --keystore\' or \'--buildConfig\'' +
|
||||
' or sign and deploy the unsigned apk manually using Android tools.';
|
||||
} else if (stdout.match(/INSTALL_FAILED_VERSION_DOWNGRADE/)) {
|
||||
stdout += 'You\'re trying to install apk with a lower versionCode that is already installed.' +
|
||||
'\nEither uninstall an app or increment the versionCode.';
|
||||
}
|
||||
|
||||
reject(new CordovaError('Failed to install apk to emulator: ' + stdout));
|
||||
} else resolve(stdout);
|
||||
});
|
||||
throw new CordovaError('Failed to install apk to emulator: ' + stdout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function installPromise () {
|
||||
return adbInstallWithOptions(target.target, apk_path, execOptions)
|
||||
.catch(function (error) {
|
||||
return adbInstallWithOptions(target.target, apk_path, execOptions).catch(function (error) {
|
||||
// CB-9557 CB-10157 only uninstall and reinstall app if the one that
|
||||
// is already installed on device was signed w/different certificate
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
|
||||
throw error;
|
||||
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) { throw error; }
|
||||
|
||||
events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
|
||||
'currently installed app was signed with different key');
|
||||
|
||||
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app
|
||||
// or the app doesn't installed at all, so no error catching needed.
|
||||
return Adb.uninstall(target.target, pkgName)
|
||||
.then(function() {
|
||||
return Adb.uninstall(target.target, pkgName).then(function () {
|
||||
return adbInstallWithOptions(target.target, apk_path, execOptions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise)
|
||||
.then(function (output) {
|
||||
return retry.retryPromise(NUM_INSTALL_RETRIES, installPromise).then(function (output) {
|
||||
events.emit('log', 'INSTALL SUCCESS');
|
||||
});
|
||||
});
|
||||
// unlock screen
|
||||
}).then(function () {
|
||||
|
||||
events.emit('verbose', 'Unlocking screen...');
|
||||
return Adb.shell(target.target, 'input keyevent 82');
|
||||
}).then(function () {
|
||||
|
||||
3
bin/templates/cordova/lib/getASPath.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
@ECHO OFF
|
||||
for /f "tokens=2*" %%a in ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Android Studio" /v Path') do set "ASPath=%%~b"
|
||||
ECHO %ASPath%
|
||||
@@ -19,23 +19,23 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var device = require('./device'),
|
||||
args = process.argv;
|
||||
var device = require('./device');
|
||||
var args = process.argv;
|
||||
|
||||
if(args.length > 2) {
|
||||
if (args.length > 2) {
|
||||
var install_target;
|
||||
if (args[2].substring(0, 9) == '--target=') {
|
||||
if (args[2].substring(0, 9) === '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
device.install(install_target).done(null, function(err) {
|
||||
device.install(install_target).catch(function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
} else {
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
device.install().done(null, function(err) {
|
||||
device.install().catch(function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0install-device"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'install-device' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
||||
@@ -19,20 +19,20 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var emulator = require('./emulator'),
|
||||
args = process.argv;
|
||||
var emulator = require('./emulator');
|
||||
var args = process.argv;
|
||||
|
||||
var install_target;
|
||||
if(args.length > 2) {
|
||||
if (args[2].substring(0, 9) == '--target=') {
|
||||
if (args.length > 2) {
|
||||
if (args[2].substring(0, 9) === '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
} else {
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emulator.install(install_target).done(null, function(err) {
|
||||
emulator.install(install_target).catch(function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0install-emulator"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'install-emulator' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
var devices = require('./device');
|
||||
|
||||
// Usage support for when args are given
|
||||
require('../lib/check_reqs').check_android().then(function() {
|
||||
devices.list().done(function(device_list) {
|
||||
device_list && device_list.forEach(function(dev) {
|
||||
require('./check_reqs').check_android().then(function () {
|
||||
devices.list().then(function (device_list) {
|
||||
device_list && device_list.forEach(function (dev) {
|
||||
console.log(dev);
|
||||
});
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0list-devices"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-devices' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
var emulators = require('./emulator');
|
||||
|
||||
// Usage support for when args are given
|
||||
require('../lib/check_reqs').check_android().then(function() {
|
||||
emulators.list_images().done(function(emulator_list) {
|
||||
emulator_list && emulator_list.forEach(function(emu) {
|
||||
require('./check_reqs').check_android().then(function () {
|
||||
emulators.list_images().then(function (emulator_list) {
|
||||
emulator_list && emulator_list.forEach(function (emu) {
|
||||
console.log(emu.name);
|
||||
});
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,9 +18,9 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0list-emulator-images"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-emulator-images' script in 'cordova\lib' folder, aborting...>&2
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
var emulators = require('./emulator');
|
||||
|
||||
// Usage support for when args are given
|
||||
require('../lib/check_reqs').check_android().then(function() {
|
||||
emulators.list_started().done(function(emulator_list) {
|
||||
emulator_list && emulator_list.forEach(function(emu) {
|
||||
require('./check_reqs').check_android().then(function () {
|
||||
emulators.list_started().then(function (emulator_list) {
|
||||
emulator_list && emulator_list.forEach(function (emu) {
|
||||
console.log(emu);
|
||||
});
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0list-started-emulators"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'list-started-emulators' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
||||
31
bin/templates/cordova/lib/log.js
vendored
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,37 +17,28 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var path = require('path'),
|
||||
os = require('os'),
|
||||
Q = require('q'),
|
||||
child_process = require('child_process'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
var execa = require('execa');
|
||||
var ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
/*
|
||||
* Starts running logcat in the shell.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.run = function() {
|
||||
var d = Q.defer();
|
||||
var adb = child_process.spawn('adb', ['logcat'], {cwd: os.tmpdir()});
|
||||
module.exports.run = function () {
|
||||
var adb = execa('adb', ['logcat'], { cwd: os.tmpdir(), stderr: 'inherit' });
|
||||
|
||||
adb.stdout.on('data', function(data) {
|
||||
adb.stdout.on('data', function (data) {
|
||||
var lines = data ? data.toString().split('\n') : [];
|
||||
var out = lines.filter(function(x) { return x.indexOf('nativeGetEnabledTags') < 0; });
|
||||
var out = lines.filter(function (x) { return x.indexOf('nativeGetEnabledTags') < 0; });
|
||||
console.log(out.join('\n'));
|
||||
});
|
||||
|
||||
adb.stderr.on('data', console.error);
|
||||
adb.on('close', function(code) {
|
||||
if (code > 0) {
|
||||
d.reject('Failed to run logcat command.');
|
||||
} else d.resolve();
|
||||
});
|
||||
|
||||
return d.promise;
|
||||
return adb;
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
module.exports.help = function () {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
|
||||
console.log('Gives the logcat output on the command line.');
|
||||
process.exit(0);
|
||||
|
||||
@@ -20,32 +20,23 @@
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
// Switch the Android Gradle plugin version requirement depending on the
|
||||
// installed version of Gradle. This dependency is documented at
|
||||
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
|
||||
// and https://issues.apache.org/jira/browse/CB-8143
|
||||
if (gradle.gradleVersion >= "2.2") {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0+'
|
||||
}
|
||||
} else if (gradle.gradleVersion >= "2.1") {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.14.0+'
|
||||
}
|
||||
} else {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.12.0+'
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0+'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'android-library'
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||
debugCompile project(path: ":CordovaLib", configuration: "debug")
|
||||
releaseCompile project(path: ":CordovaLib", configuration: "release")
|
||||
}
|
||||
@@ -53,7 +44,6 @@ dependencies {
|
||||
android {
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
publishNonDefault true
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
|
||||
181
bin/templates/cordova/lib/pluginHandlers.js
vendored
@@ -1,7 +1,4 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2013 Anis Kadri
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
@@ -17,51 +14,59 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* jshint unused: vars */
|
||||
|
||||
var fs = require('fs');
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var events = require('cordova-common').events;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
|
||||
var handlers = {
|
||||
'source-file':{
|
||||
install:function(obj, plugin, project, options) {
|
||||
'source-file': {
|
||||
install: function (obj, plugin, project, options) {
|
||||
if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
|
||||
if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
|
||||
var dest = path.join(obj.targetDir, path.basename(obj.src));
|
||||
|
||||
var dest = getInstallDestination(obj);
|
||||
|
||||
if (options && options.force) {
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
} else {
|
||||
copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
}
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
var dest = path.join(obj.targetDir, path.basename(obj.src));
|
||||
deleteJava(project.projectDir, dest);
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var dest = getInstallDestination(obj);
|
||||
|
||||
// TODO: Add Koltin extension to uninstall, since they are handled like Java files
|
||||
if (obj.src.endsWith('java')) {
|
||||
deleteJava(project.projectDir, dest);
|
||||
} else {
|
||||
// Just remove the file, not the whole parent directory
|
||||
removeFile(path.resolve(project.projectDir, dest));
|
||||
}
|
||||
}
|
||||
},
|
||||
'lib-file':{
|
||||
install:function(obj, plugin, project, options) {
|
||||
var dest = path.join('libs', path.basename(obj.src));
|
||||
'lib-file': {
|
||||
install: function (obj, plugin, project, options) {
|
||||
var dest = path.join('app/libs', path.basename(obj.src));
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
var dest = path.join('libs', path.basename(obj.src));
|
||||
removeFile(project.projectDir, dest);
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var dest = path.join('app/libs', path.basename(obj.src));
|
||||
removeFile(path.resolve(project.projectDir, dest));
|
||||
}
|
||||
},
|
||||
'resource-file':{
|
||||
install:function(obj, plugin, project, options) {
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
|
||||
'resource-file': {
|
||||
install: function (obj, plugin, project, options) {
|
||||
var dest = path.join('app', 'src', 'main', obj.target);
|
||||
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
removeFile(project.projectDir, path.normalize(obj.target));
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var dest = path.join('app', 'src', 'main', obj.target);
|
||||
removeFile(path.resolve(project.projectDir, dest));
|
||||
}
|
||||
},
|
||||
'framework': {
|
||||
install:function(obj, plugin, project, options) {
|
||||
framework: {
|
||||
install: function (obj, plugin, project, options) {
|
||||
var src = obj.src;
|
||||
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
|
||||
|
||||
@@ -78,15 +83,15 @@ var handlers = {
|
||||
subDir = src;
|
||||
}
|
||||
|
||||
if (obj.type == 'gradleReference') {
|
||||
if (obj.type === 'gradleReference') {
|
||||
project.addGradleReference(parentDir, subDir);
|
||||
} else if (obj.type == 'sys') {
|
||||
} else if (obj.type === 'sys') {
|
||||
project.addSystemLibrary(parentDir, subDir);
|
||||
} else {
|
||||
project.addSubProject(parentDir, subDir);
|
||||
}
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var src = obj.src;
|
||||
if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
|
||||
|
||||
@@ -96,7 +101,7 @@ var handlers = {
|
||||
|
||||
if (obj.custom) {
|
||||
var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
|
||||
removeFile(project.projectDir, subRelativeDir);
|
||||
removeFile(path.resolve(project.projectDir, subRelativeDir));
|
||||
subDir = path.resolve(project.projectDir, subRelativeDir);
|
||||
// If it's the last framework in the plugin, remove the parent directory.
|
||||
var parDir = path.dirname(subDir);
|
||||
@@ -108,17 +113,17 @@ var handlers = {
|
||||
subDir = src;
|
||||
}
|
||||
|
||||
if (obj.type == 'gradleReference') {
|
||||
if (obj.type === 'gradleReference') {
|
||||
project.removeGradleReference(parentDir, subDir);
|
||||
} else if (obj.type == 'sys') {
|
||||
} else if (obj.type === 'sys') {
|
||||
project.removeSystemLibrary(parentDir, subDir);
|
||||
} else {
|
||||
project.removeSubProject(parentDir, subDir);
|
||||
}
|
||||
}
|
||||
},
|
||||
asset:{
|
||||
install:function(obj, plugin, project, options) {
|
||||
asset: {
|
||||
install: function (obj, plugin, project, options) {
|
||||
if (!obj.src) {
|
||||
throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
|
||||
}
|
||||
@@ -132,17 +137,17 @@ var handlers = {
|
||||
copyFile(plugin.dir, obj.src, project.platformWww, obj.target);
|
||||
}
|
||||
},
|
||||
uninstall:function(obj, plugin, project, options) {
|
||||
uninstall: function (obj, plugin, project, options) {
|
||||
var target = obj.target || obj.src;
|
||||
|
||||
if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
|
||||
|
||||
removeFileF(path.resolve(project.www, target));
|
||||
removeFileF(path.resolve(project.www, 'plugins', plugin.id));
|
||||
removeFile(path.resolve(project.www, target));
|
||||
removeFile(path.resolve(project.www, 'plugins', plugin.id));
|
||||
if (options && options.usePlatformWww) {
|
||||
// CB-11022 remove file from both directories if usePlatformWww is specified
|
||||
removeFileF(path.resolve(project.platformWww, target));
|
||||
removeFileF(path.resolve(project.platformWww, 'plugins', plugin.id));
|
||||
removeFile(path.resolve(project.platformWww, target));
|
||||
removeFile(path.resolve(project.platformWww, 'plugins', plugin.id));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -150,7 +155,7 @@ var handlers = {
|
||||
install: function (obj, plugin, project, options) {
|
||||
// Copy the plugin's files into the www directory.
|
||||
var moduleSource = path.resolve(plugin.dir, obj.src);
|
||||
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname (obj.src)));
|
||||
var moduleName = plugin.id + '.' + (obj.name || path.basename(obj.src, path.extname(obj.src)));
|
||||
|
||||
// Read in the file, prepend the cordova.define, and write it back out.
|
||||
var scriptContent = fs.readFileSync(moduleSource, 'utf-8').replace(/^\ufeff/, ''); // Window BOM
|
||||
@@ -160,13 +165,13 @@ var handlers = {
|
||||
scriptContent = 'cordova.define("' + moduleName + '", function(require, exports, module) {\n' + scriptContent + '\n});\n';
|
||||
|
||||
var wwwDest = path.resolve(project.www, 'plugins', plugin.id, obj.src);
|
||||
shell.mkdir('-p', path.dirname(wwwDest));
|
||||
fs.ensureDirSync(path.dirname(wwwDest));
|
||||
fs.writeFileSync(wwwDest, scriptContent, 'utf-8');
|
||||
|
||||
if (options && options.usePlatformWww) {
|
||||
// CB-11022 copy file to both directories if usePlatformWww is specified
|
||||
var platformWwwDest = path.resolve(project.platformWww, 'plugins', plugin.id, obj.src);
|
||||
shell.mkdir('-p', path.dirname(platformWwwDest));
|
||||
fs.ensureDirSync(path.dirname(platformWwwDest));
|
||||
fs.writeFileSync(platformWwwDest, scriptContent, 'utf-8');
|
||||
}
|
||||
},
|
||||
@@ -189,7 +194,7 @@ module.exports.getInstaller = function (type) {
|
||||
events.emit('verbose', '<' + type + '> is not supported for android plugins');
|
||||
};
|
||||
|
||||
module.exports.getUninstaller = function(type) {
|
||||
module.exports.getUninstaller = function (type) {
|
||||
if (handlers[type] && handlers[type].uninstall) {
|
||||
return handlers[type].uninstall;
|
||||
}
|
||||
@@ -204,45 +209,46 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
|
||||
// check that src path is inside plugin directory
|
||||
var real_path = fs.realpathSync(src);
|
||||
var real_plugin_path = fs.realpathSync(plugin_dir);
|
||||
if (real_path.indexOf(real_plugin_path) !== 0)
|
||||
throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');
|
||||
if (real_path.indexOf(real_plugin_path) !== 0) { throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"'); }
|
||||
|
||||
dest = path.resolve(project_dir, dest);
|
||||
|
||||
// check that dest path is located in project directory
|
||||
if (dest.indexOf(project_dir) !== 0)
|
||||
throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');
|
||||
|
||||
shell.mkdir('-p', path.dirname(dest));
|
||||
if (dest.indexOf(project_dir) !== 0) { throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project'); }
|
||||
|
||||
fs.ensureDirSync(path.dirname(dest));
|
||||
if (link) {
|
||||
fs.symlinkSync(path.relative(path.dirname(dest), src), dest);
|
||||
} else if (fs.statSync(src).isDirectory()) {
|
||||
// XXX shelljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
|
||||
shell.cp('-Rf', src+'/*', dest);
|
||||
symlinkFileOrDirTree(src, dest);
|
||||
} else {
|
||||
shell.cp('-f', src, dest);
|
||||
fs.copySync(src, dest);
|
||||
}
|
||||
}
|
||||
|
||||
// Same as copy file but throws error if target exists
|
||||
function copyNewFile (plugin_dir, src, project_dir, dest, link) {
|
||||
var target_path = path.resolve(project_dir, dest);
|
||||
if (fs.existsSync(target_path))
|
||||
throw new CordovaError('"' + target_path + '" already exists!');
|
||||
if (fs.existsSync(target_path)) { throw new CordovaError('"' + target_path + '" already exists!'); }
|
||||
|
||||
copyFile(plugin_dir, src, project_dir, dest, !!link);
|
||||
}
|
||||
|
||||
// checks if file exists and then deletes. Error if doesn't exist
|
||||
function removeFile (project_dir, src) {
|
||||
var file = path.resolve(project_dir, src);
|
||||
shell.rm('-Rf', file);
|
||||
function symlinkFileOrDirTree (src, dest) {
|
||||
if (fs.existsSync(dest)) {
|
||||
fs.removeSync(dest);
|
||||
}
|
||||
|
||||
if (fs.statSync(src).isDirectory()) {
|
||||
fs.ensureDirSync(path.dirname(dest));
|
||||
fs.readdirSync(src).forEach(function (entry) {
|
||||
symlinkFileOrDirTree(path.join(src, entry), path.join(dest, entry));
|
||||
});
|
||||
} else {
|
||||
fs.symlinkSync(path.relative(fs.realpathSync(path.dirname(dest)), src), dest);
|
||||
}
|
||||
}
|
||||
|
||||
// deletes file/directory without checking
|
||||
function removeFileF (file) {
|
||||
shell.rm('-Rf', file);
|
||||
function removeFile (file) {
|
||||
fs.removeSync(file);
|
||||
}
|
||||
|
||||
// Sometimes we want to remove some java, and prune any unnecessary empty directories
|
||||
@@ -255,13 +261,13 @@ function removeFileAndParents (baseDir, destFile, stopper) {
|
||||
var file = path.resolve(baseDir, destFile);
|
||||
if (!fs.existsSync(file)) return;
|
||||
|
||||
removeFileF(file);
|
||||
removeFile(file);
|
||||
|
||||
// check if directory is empty
|
||||
var curDir = path.dirname(file);
|
||||
|
||||
while(curDir !== path.resolve(baseDir, stopper)) {
|
||||
if(fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
|
||||
while (curDir !== path.resolve(baseDir, stopper)) {
|
||||
if (fs.existsSync(curDir) && fs.readdirSync(curDir).length === 0) {
|
||||
fs.rmdirSync(curDir);
|
||||
curDir = path.resolve(curDir, '..');
|
||||
} else {
|
||||
@@ -271,6 +277,47 @@ function removeFileAndParents (baseDir, destFile, stopper) {
|
||||
}
|
||||
}
|
||||
|
||||
function generateAttributeError(attribute, element, id) {
|
||||
function generateAttributeError (attribute, element, id) {
|
||||
return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
|
||||
}
|
||||
|
||||
function getInstallDestination (obj) {
|
||||
var APP_MAIN_PREFIX = 'app/src/main';
|
||||
var PATH_SEPARATOR = '/';
|
||||
|
||||
var PATH_SEP_MATCH = '\\' + PATH_SEPARATOR;
|
||||
var PATH_SEP_OR_EOL_MATCH = '(\\' + PATH_SEPARATOR + '|$)';
|
||||
|
||||
var appReg = new RegExp('^app' + PATH_SEP_OR_EOL_MATCH);
|
||||
var libsReg = new RegExp('^libs' + PATH_SEP_OR_EOL_MATCH);
|
||||
var srcReg = new RegExp('^src' + PATH_SEP_OR_EOL_MATCH);
|
||||
var srcMainReg = new RegExp('^src' + PATH_SEP_MATCH + 'main' + PATH_SEP_OR_EOL_MATCH);
|
||||
|
||||
if (appReg.test(obj.targetDir)) {
|
||||
// If any source file is using the new app directory structure,
|
||||
// don't penalize it
|
||||
return path.join(obj.targetDir, path.basename(obj.src));
|
||||
} else {
|
||||
// Plugin using deprecated target directory structure (GH-580)
|
||||
if (obj.src.endsWith('.java')) {
|
||||
return path.join(APP_MAIN_PREFIX, 'java', obj.targetDir.replace(srcReg, ''),
|
||||
path.basename(obj.src));
|
||||
} else if (obj.src.endsWith('.aidl')) {
|
||||
return path.join(APP_MAIN_PREFIX, 'aidl', obj.targetDir.replace(srcReg, ''),
|
||||
path.basename(obj.src));
|
||||
} else if (libsReg.test(obj.targetDir)) {
|
||||
if (obj.src.endsWith('.so')) {
|
||||
return path.join(APP_MAIN_PREFIX, 'jniLibs', obj.targetDir.replace(libsReg, ''),
|
||||
path.basename(obj.src));
|
||||
} else {
|
||||
return path.join('app', obj.targetDir, path.basename(obj.src));
|
||||
}
|
||||
} else if (srcMainReg.test(obj.targetDir)) {
|
||||
return path.join('app', obj.targetDir, path.basename(obj.src));
|
||||
}
|
||||
|
||||
// For all other source files not using the new app directory structure,
|
||||
// add 'app/src/main' to the targetDir
|
||||
return path.join(APP_MAIN_PREFIX, obj.targetDir, path.basename(obj.src));
|
||||
}
|
||||
}
|
||||
|
||||
502
bin/templates/cordova/lib/prepare.js
vendored
@@ -17,12 +17,12 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var Q = require('q');
|
||||
var fs = require('fs');
|
||||
var fs = require('fs-extra');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
const nopt = require('nopt');
|
||||
var events = require('cordova-common').events;
|
||||
var AndroidManifest = require('./AndroidManifest');
|
||||
var checkReqs = require('./check_reqs');
|
||||
var xmlHelpers = require('cordova-common').xmlHelpers;
|
||||
var CordovaError = require('cordova-common').CordovaError;
|
||||
var ConfigParser = require('cordova-common').ConfigParser;
|
||||
@@ -30,27 +30,65 @@ var FileUpdater = require('cordova-common').FileUpdater;
|
||||
var PlatformJson = require('cordova-common').PlatformJson;
|
||||
var PlatformMunger = require('cordova-common').ConfigChanges.PlatformMunger;
|
||||
var PluginInfoProvider = require('cordova-common').PluginInfoProvider;
|
||||
const utils = require('./utils');
|
||||
|
||||
const GradlePropertiesParser = require('./config/GradlePropertiesParser');
|
||||
|
||||
function parseArguments (argv) {
|
||||
return nopt({
|
||||
// `jvmargs` is a valid option however, we don't actually want to parse it because we want the entire string as is.
|
||||
// jvmargs: String
|
||||
}, {}, argv || [], 0);
|
||||
}
|
||||
|
||||
module.exports.prepare = function (cordovaProject, options) {
|
||||
var self = this;
|
||||
var platformResourcesDir = path.relative(cordovaProject.root, path.join(this.locations.root, 'res'));
|
||||
|
||||
let args = {};
|
||||
if (options && options.options) {
|
||||
args = parseArguments(options.options.argv);
|
||||
}
|
||||
|
||||
var platformJson = PlatformJson.load(this.locations.root, this.platform);
|
||||
var munger = new PlatformMunger(this.platform, this.locations.root, platformJson, new PluginInfoProvider());
|
||||
|
||||
this._config = updateConfigFilesFrom(cordovaProject.projectConfig, munger, this.locations);
|
||||
|
||||
// Get the min SDK version from config.xml
|
||||
const minSdkVersion = this._config.getPreference('android-minSdkVersion', 'android');
|
||||
const maxSdkVersion = this._config.getPreference('android-maxSdkVersion', 'android');
|
||||
const targetSdkVersion = this._config.getPreference('android-targetSdkVersion', 'android');
|
||||
const androidXEnabled = this._config.getPreference('AndroidXEnabled', 'android');
|
||||
const isGradlePluginKotlinEnabled = this._config.getPreference('GradlePluginKotlinEnabled', 'android');
|
||||
const gradlePluginKotlinCodeStyle = this._config.getPreference('GradlePluginKotlinCodeStyle', 'android');
|
||||
|
||||
const gradlePropertiesUserConfig = {};
|
||||
if (minSdkVersion) gradlePropertiesUserConfig.cdvMinSdkVersion = minSdkVersion;
|
||||
if (maxSdkVersion) gradlePropertiesUserConfig.cdvMaxSdkVersion = maxSdkVersion;
|
||||
if (targetSdkVersion) gradlePropertiesUserConfig.cdvTargetSdkVersion = targetSdkVersion;
|
||||
if (args.jvmargs) gradlePropertiesUserConfig['org.gradle.jvmargs'] = args.jvmargs;
|
||||
if (isGradlePluginKotlinEnabled) {
|
||||
gradlePropertiesUserConfig['kotlin.code.style'] = gradlePluginKotlinCodeStyle || 'official';
|
||||
}
|
||||
|
||||
// Both 'useAndroidX' and 'enableJetifier' are linked together.
|
||||
if (androidXEnabled) {
|
||||
gradlePropertiesUserConfig['android.useAndroidX'] = androidXEnabled;
|
||||
gradlePropertiesUserConfig['android.enableJetifier'] = androidXEnabled;
|
||||
}
|
||||
|
||||
const gradlePropertiesParser = new GradlePropertiesParser(this.locations.root);
|
||||
gradlePropertiesParser.configure(gradlePropertiesUserConfig);
|
||||
|
||||
// Update own www dir with project's www assets and plugins' assets and js-files
|
||||
return Q.when(updateWww(cordovaProject, this.locations))
|
||||
.then(function () {
|
||||
return Promise.resolve(updateWww(cordovaProject, this.locations)).then(function () {
|
||||
// update project according to config.xml changes.
|
||||
return updateProjectAccordingTo(self._config, self.locations);
|
||||
})
|
||||
.then(function () {
|
||||
updateIcons(cordovaProject, platformResourcesDir);
|
||||
updateSplashes(cordovaProject, platformResourcesDir);
|
||||
})
|
||||
.then(function () {
|
||||
}).then(function () {
|
||||
updateIcons(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateSplashes(cordovaProject, path.relative(cordovaProject.root, self.locations.res));
|
||||
updateFileResources(cordovaProject, path.relative(cordovaProject.root, self.locations.root));
|
||||
}).then(function () {
|
||||
events.emit('verbose', 'Prepared android project successfully');
|
||||
});
|
||||
};
|
||||
@@ -61,20 +99,19 @@ module.exports.clean = function (options) {
|
||||
// noPrepare option passed in by the non-CLI clean script. If that's present, or if
|
||||
// there's no config.xml found at the project root, then don't clean prepared files.
|
||||
var projectRoot = path.resolve(this.root, '../..');
|
||||
var projectConfigFile = path.join(projectRoot, 'config.xml');
|
||||
if ((options && options.noPrepare) || !fs.existsSync(projectConfigFile) ||
|
||||
if ((options && options.noPrepare) || !fs.existsSync(this.locations.configXml) ||
|
||||
!fs.existsSync(this.locations.configXml)) {
|
||||
return Q();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var projectConfig = new ConfigParser(this.locations.configXml);
|
||||
var platformResourcesDir = path.relative(projectRoot, path.join(this.locations.root, 'res'));
|
||||
|
||||
var self = this;
|
||||
return Q().then(function () {
|
||||
return Promise.resolve().then(function () {
|
||||
cleanWww(projectRoot, self.locations);
|
||||
cleanIcons(projectRoot, projectConfig, platformResourcesDir);
|
||||
cleanSplashes(projectRoot, projectConfig, platformResourcesDir);
|
||||
cleanIcons(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
|
||||
cleanSplashes(projectRoot, projectConfig, path.relative(projectRoot, self.locations.res));
|
||||
cleanFileResources(projectRoot, projectConfig, path.relative(projectRoot, self.locations.root));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -92,12 +129,12 @@ module.exports.clean = function (options) {
|
||||
* represents current project's configuration. When returned, the
|
||||
* configuration is already dumped to appropriate config.xml file.
|
||||
*/
|
||||
function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
|
||||
function updateConfigFilesFrom (sourceConfig, configMunger, locations) {
|
||||
events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml);
|
||||
|
||||
// First cleanup current config and merge project's one into own
|
||||
// Overwrite platform config.xml with defaults.xml.
|
||||
shell.cp('-f', locations.defaultConfigXml, locations.configXml);
|
||||
fs.copySync(locations.defaultConfigXml, locations.configXml);
|
||||
|
||||
// Then apply config changes from global munge to all config files
|
||||
// in project (including project's config)
|
||||
@@ -107,7 +144,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
|
||||
// Merge changes from app's config.xml into platform's one
|
||||
var config = new ConfigParser(locations.configXml);
|
||||
xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
|
||||
config.doc.getroot(), 'android', /*clobber=*/true);
|
||||
config.doc.getroot(), 'android', /* clobber= */true);
|
||||
|
||||
config.write();
|
||||
return config;
|
||||
@@ -116,7 +153,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
|
||||
/**
|
||||
* Logs all file operations via the verbose event stream, indented.
|
||||
*/
|
||||
function logFileOp(message) {
|
||||
function logFileOp (message) {
|
||||
events.emit('verbose', ' ' + message);
|
||||
}
|
||||
|
||||
@@ -129,7 +166,7 @@ function logFileOp(message) {
|
||||
* @param {Object} destinations An object that contains destination
|
||||
* paths for www files.
|
||||
*/
|
||||
function updateWww(cordovaProject, destinations) {
|
||||
function updateWww (cordovaProject, destinations) {
|
||||
var sourceDirs = [
|
||||
path.relative(cordovaProject.root, cordovaProject.locations.www),
|
||||
path.relative(cordovaProject.root, destinations.platformWww)
|
||||
@@ -152,7 +189,7 @@ function updateWww(cordovaProject, destinations) {
|
||||
/**
|
||||
* Cleans all files from the platform 'www' directory.
|
||||
*/
|
||||
function cleanWww(projectRoot, locations) {
|
||||
function cleanWww (projectRoot, locations) {
|
||||
var targetDir = path.relative(projectRoot, locations.www);
|
||||
events.emit('verbose', 'Cleaning ' + targetDir);
|
||||
|
||||
@@ -168,19 +205,26 @@ function cleanWww(projectRoot, locations) {
|
||||
* be used to update project
|
||||
* @param {Object} locations A map of locations for this platform
|
||||
*/
|
||||
function updateProjectAccordingTo(platformConfig, locations) {
|
||||
function updateProjectAccordingTo (platformConfig, locations) {
|
||||
// Update app name by editing res/values/strings.xml
|
||||
var name = platformConfig.name();
|
||||
var strings = xmlHelpers.parseElementtreeSync(locations.strings);
|
||||
strings.find('string[@name="app_name"]').text = name;
|
||||
fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
|
||||
|
||||
var name = platformConfig.name();
|
||||
strings.find('string[@name="app_name"]').text = name.replace(/'/g, '\\\'');
|
||||
|
||||
var shortName = platformConfig.shortName && platformConfig.shortName();
|
||||
if (shortName && shortName !== name) {
|
||||
strings.find('string[@name="launcher_name"]').text = shortName.replace(/'/g, '\\\'');
|
||||
}
|
||||
|
||||
fs.writeFileSync(locations.strings, strings.write({ indent: 4 }), 'utf-8');
|
||||
events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
|
||||
|
||||
// Java packages cannot support dashes
|
||||
var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
|
||||
var androidPkgName = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
|
||||
|
||||
var manifest = new AndroidManifest(locations.manifest);
|
||||
var orig_pkg = manifest.getPackageId();
|
||||
var manifestId = manifest.getPackageId();
|
||||
|
||||
manifest.getActivity()
|
||||
.setOrientation(platformConfig.getPreference('orientation'))
|
||||
@@ -188,36 +232,45 @@ function updateProjectAccordingTo(platformConfig, locations) {
|
||||
|
||||
manifest.setVersionName(platformConfig.version())
|
||||
.setVersionCode(platformConfig.android_versionCode() || default_versionCode(platformConfig.version()))
|
||||
.setPackageId(pkg)
|
||||
.setMinSdkVersion(platformConfig.getPreference('android-minSdkVersion', 'android'))
|
||||
.setMaxSdkVersion(platformConfig.getPreference('android-maxSdkVersion', 'android'))
|
||||
.setTargetSdkVersion(platformConfig.getPreference('android-targetSdkVersion', 'android'))
|
||||
.setPackageId(androidPkgName)
|
||||
.write();
|
||||
|
||||
var javaPattern = path.join(locations.root, 'src', orig_pkg.replace(/\./g, '/'), '*.java');
|
||||
var java_files = shell.ls(javaPattern).filter(function(f) {
|
||||
return shell.grep(/extends\s+CordovaActivity/g, f);
|
||||
// Java file paths shouldn't be hard coded
|
||||
const javaDirectory = path.join(locations.javaSrc, manifestId.replace(/\./g, '/'));
|
||||
const javaPattern = /\.java$/;
|
||||
const java_files = utils.scanDirectory(javaDirectory, javaPattern, true).filter(function (f) {
|
||||
return utils.grep(f, /extends\s+CordovaActivity/g) !== null;
|
||||
});
|
||||
|
||||
if (java_files.length === 0) {
|
||||
throw new CordovaError('No Java files found that extend CordovaActivity.');
|
||||
} else if(java_files.length > 1) {
|
||||
} else if (java_files.length > 1) {
|
||||
events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
|
||||
}
|
||||
|
||||
var destFile = path.join(locations.root, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0]));
|
||||
shell.mkdir('-p', path.dirname(destFile));
|
||||
shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
|
||||
events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);
|
||||
const destFile = java_files[0];
|
||||
|
||||
if (orig_pkg !== pkg) {
|
||||
// var destFile = path.join(locations.root, 'app', 'src', 'main', 'java', androidPkgName.replace(/\./g, '/'), path.basename(java_files[0]));
|
||||
// fs.ensureDirSync(path.dirname(destFile));
|
||||
// events.emit('verbose', java_files[0]);
|
||||
// events.emit('verbose', destFile);
|
||||
// console.log(locations);
|
||||
// fs.copySync(java_files[0], destFile);
|
||||
utils.replaceFileContents(destFile, /package [\w.]*;/, 'package ' + androidPkgName + ';');
|
||||
events.emit('verbose', 'Wrote out Android package name "' + androidPkgName + '" to ' + destFile);
|
||||
|
||||
var removeOrigPkg = checkReqs.isWindows() || checkReqs.isDarwin()
|
||||
? manifestId.toUpperCase() !== androidPkgName.toUpperCase()
|
||||
: manifestId !== androidPkgName;
|
||||
|
||||
if (removeOrigPkg) {
|
||||
// If package was name changed we need to remove old java with main activity
|
||||
shell.rm('-Rf',java_files[0]);
|
||||
fs.removeSync(java_files[0]);
|
||||
// remove any empty directories
|
||||
var currentDir = path.dirname(java_files[0]);
|
||||
var sourcesRoot = path.resolve(locations.root, 'src');
|
||||
while(currentDir !== sourcesRoot) {
|
||||
if(fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
|
||||
while (currentDir !== sourcesRoot) {
|
||||
if (fs.existsSync(currentDir) && fs.readdirSync(currentDir).length === 0) {
|
||||
fs.rmdirSync(currentDir);
|
||||
currentDir = path.resolve(currentDir, '..');
|
||||
} else {
|
||||
@@ -230,7 +283,7 @@ function updateProjectAccordingTo(platformConfig, locations) {
|
||||
// Consturct the default value for versionCode as
|
||||
// PATCH + MINOR * 100 + MAJOR * 10000
|
||||
// see http://developer.android.com/tools/publishing/versioning.html
|
||||
function default_versionCode(version) {
|
||||
function default_versionCode (version) {
|
||||
var nums = version.split('-')[0].split('.');
|
||||
var versionCode = 0;
|
||||
if (+nums[0]) {
|
||||
@@ -247,15 +300,23 @@ function default_versionCode(version) {
|
||||
return versionCode;
|
||||
}
|
||||
|
||||
function getImageResourcePath(resourcesDir, density, name, sourceName) {
|
||||
function getImageResourcePath (resourcesDir, type, density, name, sourceName) {
|
||||
if (/\.9\.png$/.test(sourceName)) {
|
||||
name = name.replace(/\.png$/, '.9.png');
|
||||
}
|
||||
var resourcePath = path.join(resourcesDir, (density ? 'drawable-' + density : 'drawable'), name);
|
||||
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density : type), name);
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
function updateSplashes(cordovaProject, platformResourcesDir) {
|
||||
function getAdaptiveImageResourcePath (resourcesDir, type, density, name, sourceName) {
|
||||
if (/\.9\.png$/.test(sourceName)) {
|
||||
name = name.replace(/\.png$/, '.9.png');
|
||||
}
|
||||
var resourcePath = path.join(resourcesDir, (density ? type + '-' + density + '-v26' : type), name);
|
||||
return resourcePath;
|
||||
}
|
||||
|
||||
function updateSplashes (cordovaProject, platformResourcesDir) {
|
||||
var resources = cordovaProject.projectConfig.getSplashScreens('android');
|
||||
|
||||
// if there are "splash" elements in config.xml
|
||||
@@ -264,25 +325,25 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'screen.png');
|
||||
var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'drawable', 'screen.png');
|
||||
|
||||
var hadMdpi = false;
|
||||
resources.forEach(function (resource) {
|
||||
if (!resource.density) {
|
||||
return;
|
||||
}
|
||||
if (resource.density == 'mdpi') {
|
||||
if (resource.density === 'mdpi') {
|
||||
hadMdpi = true;
|
||||
}
|
||||
var targetPath = getImageResourcePath(
|
||||
platformResourcesDir, resource.density, 'screen.png', path.basename(resource.src));
|
||||
platformResourcesDir, 'drawable', resource.density, 'screen.png', path.basename(resource.src));
|
||||
resourceMap[targetPath] = resource.src;
|
||||
});
|
||||
|
||||
// There's no "default" drawable, so assume default == mdpi.
|
||||
if (!hadMdpi && resources.defaultResource) {
|
||||
var targetPath = getImageResourcePath(
|
||||
platformResourcesDir, 'mdpi', 'screen.png', path.basename(resources.defaultResource.src));
|
||||
platformResourcesDir, 'drawable', 'mdpi', 'screen.png', path.basename(resources.defaultResource.src));
|
||||
resourceMap[targetPath] = resources.defaultResource.src;
|
||||
}
|
||||
|
||||
@@ -291,10 +352,10 @@ function updateSplashes(cordovaProject, platformResourcesDir) {
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
|
||||
function cleanSplashes (projectRoot, projectConfig, platformResourcesDir) {
|
||||
var resources = projectConfig.getSplashScreens('android');
|
||||
if (resources.length > 0) {
|
||||
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'screen.png');
|
||||
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'drawable', 'screen.png');
|
||||
events.emit('verbose', 'Cleaning splash screens at ' + platformResourcesDir);
|
||||
|
||||
// No source paths are specified in the map, so updatePaths() will delete the target files.
|
||||
@@ -303,21 +364,203 @@ function cleanSplashes(projectRoot, projectConfig, platformResourcesDir) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
var icons = cordovaProject.projectConfig.getIcons('android');
|
||||
function updateIcons (cordovaProject, platformResourcesDir) {
|
||||
const icons = cordovaProject.projectConfig.getIcons('android');
|
||||
|
||||
// if there are icon elements in config.xml
|
||||
// Skip if there are no app defined icons in config.xml
|
||||
if (icons.length === 0) {
|
||||
events.emit('verbose', 'This app does not have launcher icons defined');
|
||||
return;
|
||||
}
|
||||
|
||||
var resourceMap = mapImageResources(cordovaProject.root, platformResourcesDir, 'icon.png');
|
||||
// 1. loop icons determin if there is an error in the setup.
|
||||
// 2. during initial loop, also setup for legacy support.
|
||||
const errorMissingAttributes = [];
|
||||
const errorLegacyIconNeeded = [];
|
||||
let hasAdaptive = false;
|
||||
icons.forEach((icon, key) => {
|
||||
if (
|
||||
(icon.background && !icon.foreground) ||
|
||||
(!icon.background && icon.foreground) ||
|
||||
(!icon.background && !icon.foreground && !icon.src)
|
||||
) {
|
||||
errorMissingAttributes.push(icon.density ? icon.density : 'size=' + (icon.height || icon.width));
|
||||
}
|
||||
|
||||
var android_icons = {};
|
||||
var default_icon;
|
||||
if (icon.foreground) {
|
||||
hasAdaptive = true;
|
||||
|
||||
if (
|
||||
!icon.src &&
|
||||
(
|
||||
icon.foreground.startsWith('@color') ||
|
||||
path.extname(path.basename(icon.foreground)) === '.xml'
|
||||
)
|
||||
) {
|
||||
errorLegacyIconNeeded.push(icon.density ? icon.density : 'size=' + (icon.height || icon.width));
|
||||
} else if (!icon.src) {
|
||||
icons[key].src = icon.foreground;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const errorMessage = [];
|
||||
if (errorMissingAttributes.length > 0) {
|
||||
errorMessage.push('One of the following attributes are set but missing the other for the density type: ' + errorMissingAttributes.join(', ') + '. Please ensure that all require attributes are defined.');
|
||||
}
|
||||
|
||||
if (errorLegacyIconNeeded.length > 0) {
|
||||
errorMessage.push('For the following icons with the density of: ' + errorLegacyIconNeeded.join(', ') + ', adaptive foreground with a defined color or vector can not be used as a standard fallback icon for older Android devices. To support older Android environments, please provide a value for the src attribute.');
|
||||
}
|
||||
|
||||
if (errorMessage.length > 0) {
|
||||
throw new CordovaError(errorMessage.join(' '));
|
||||
}
|
||||
|
||||
let resourceMap = Object.assign(
|
||||
{},
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
|
||||
mapImageResources(cordovaProject.root, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
|
||||
);
|
||||
|
||||
const preparedIcons = prepareIcons(icons);
|
||||
|
||||
if (hasAdaptive) {
|
||||
resourceMap = updateIconResourceForAdaptive(preparedIcons, resourceMap, platformResourcesDir);
|
||||
}
|
||||
|
||||
resourceMap = updateIconResourceForLegacy(preparedIcons, resourceMap, platformResourcesDir);
|
||||
|
||||
events.emit('verbose', 'Updating icons at ' + platformResourcesDir);
|
||||
FileUpdater.updatePaths(resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
function updateIconResourceForAdaptive (preparedIcons, resourceMap, platformResourcesDir) {
|
||||
const android_icons = preparedIcons.android_icons;
|
||||
const default_icon = preparedIcons.default_icon;
|
||||
|
||||
// The source paths for icons and splashes are relative to
|
||||
// project's config.xml location, so we use it as base path.
|
||||
let background;
|
||||
let foreground;
|
||||
let targetPathBackground;
|
||||
let targetPathForeground;
|
||||
|
||||
for (const density in android_icons) {
|
||||
let backgroundVal = '@mipmap/ic_launcher_background';
|
||||
let foregroundVal = '@mipmap/ic_launcher_foreground';
|
||||
|
||||
background = android_icons[density].background;
|
||||
foreground = android_icons[density].foreground;
|
||||
|
||||
if (!background || !foreground) {
|
||||
// This icon isn't an adaptive icon, so skip it
|
||||
continue;
|
||||
}
|
||||
|
||||
if (background.startsWith('@color')) {
|
||||
// Colors Use Case
|
||||
backgroundVal = background; // Example: @color/background_foobar_1
|
||||
} else if (path.extname(path.basename(background)) === '.xml') {
|
||||
// Vector Use Case
|
||||
targetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_background.xml', path.basename(android_icons[density].background));
|
||||
resourceMap[targetPathBackground] = android_icons[density].background;
|
||||
} else if (path.extname(path.basename(background)) === '.png') {
|
||||
// Images Use Case
|
||||
targetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_background.png', path.basename(android_icons[density].background));
|
||||
resourceMap[targetPathBackground] = android_icons[density].background;
|
||||
}
|
||||
|
||||
if (foreground.startsWith('@color')) {
|
||||
// Colors Use Case
|
||||
foregroundVal = foreground;
|
||||
} else if (path.extname(path.basename(foreground)) === '.xml') {
|
||||
// Vector Use Case
|
||||
targetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_foreground.xml', path.basename(android_icons[density].foreground));
|
||||
resourceMap[targetPathForeground] = android_icons[density].foreground;
|
||||
} else if (path.extname(path.basename(foreground)) === '.png') {
|
||||
// Images Use Case
|
||||
targetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher_foreground.png', path.basename(android_icons[density].foreground));
|
||||
resourceMap[targetPathForeground] = android_icons[density].foreground;
|
||||
}
|
||||
|
||||
// create an XML for DPI and set color
|
||||
const icLauncherTemplate = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="` + backgroundVal + `" />
|
||||
<foreground android:drawable="` + foregroundVal + `" />
|
||||
</adaptive-icon>`;
|
||||
|
||||
const launcherXmlPath = path.join(platformResourcesDir, 'mipmap-' + density + '-v26', 'ic_launcher.xml');
|
||||
|
||||
// Remove the XML from the resourceMap so the file does not get removed.
|
||||
delete resourceMap[launcherXmlPath];
|
||||
|
||||
fs.writeFileSync(path.resolve(launcherXmlPath), icLauncherTemplate);
|
||||
}
|
||||
|
||||
// There's no "default" drawable, so assume default == mdpi.
|
||||
if (default_icon && !android_icons.mdpi) {
|
||||
let defaultTargetPathBackground;
|
||||
let defaultTargetPathForeground;
|
||||
|
||||
if (background.startsWith('@color')) {
|
||||
// Colors Use Case
|
||||
targetPathBackground = default_icon.background;
|
||||
} else if (path.extname(path.basename(background)) === '.xml') {
|
||||
// Vector Use Case
|
||||
defaultTargetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_background.xml', path.basename(default_icon.background));
|
||||
resourceMap[defaultTargetPathBackground] = default_icon.background;
|
||||
} else if (path.extname(path.basename(background)) === '.png') {
|
||||
// Images Use Case
|
||||
defaultTargetPathBackground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_background.png', path.basename(default_icon.background));
|
||||
resourceMap[defaultTargetPathBackground] = default_icon.background;
|
||||
}
|
||||
|
||||
if (foreground.startsWith('@color')) {
|
||||
// Colors Use Case
|
||||
targetPathForeground = default_icon.foreground;
|
||||
} else if (path.extname(path.basename(foreground)) === '.xml') {
|
||||
// Vector Use Case
|
||||
defaultTargetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_foreground.xml', path.basename(default_icon.foreground));
|
||||
resourceMap[defaultTargetPathForeground] = default_icon.foreground;
|
||||
} else if (path.extname(path.basename(foreground)) === '.png') {
|
||||
// Images Use Case
|
||||
defaultTargetPathForeground = getAdaptiveImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher_foreground.png', path.basename(default_icon.foreground));
|
||||
resourceMap[defaultTargetPathForeground] = default_icon.foreground;
|
||||
}
|
||||
}
|
||||
|
||||
return resourceMap;
|
||||
}
|
||||
|
||||
function updateIconResourceForLegacy (preparedIcons, resourceMap, platformResourcesDir) {
|
||||
const android_icons = preparedIcons.android_icons;
|
||||
const default_icon = preparedIcons.default_icon;
|
||||
|
||||
// The source paths for icons and splashes are relative to
|
||||
// project's config.xml location, so we use it as base path.
|
||||
for (var density in android_icons) {
|
||||
var targetPath = getImageResourcePath(platformResourcesDir, 'mipmap', density, 'ic_launcher.png', path.basename(android_icons[density].src));
|
||||
resourceMap[targetPath] = android_icons[density].src;
|
||||
}
|
||||
|
||||
// There's no "default" drawable, so assume default == mdpi.
|
||||
if (default_icon && !android_icons.mdpi) {
|
||||
var defaultTargetPath = getImageResourcePath(platformResourcesDir, 'mipmap', 'mdpi', 'ic_launcher.png', path.basename(default_icon.src));
|
||||
resourceMap[defaultTargetPath] = default_icon.src;
|
||||
}
|
||||
|
||||
return resourceMap;
|
||||
}
|
||||
|
||||
function prepareIcons (icons) {
|
||||
// http://developer.android.com/design/style/iconography.html
|
||||
var sizeToDensityMap = {
|
||||
const SIZE_TO_DENSITY_MAP = {
|
||||
36: 'ldpi',
|
||||
48: 'mdpi',
|
||||
72: 'hdpi',
|
||||
@@ -325,11 +568,15 @@ function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
144: 'xxhdpi',
|
||||
192: 'xxxhdpi'
|
||||
};
|
||||
|
||||
const android_icons = {};
|
||||
let default_icon;
|
||||
|
||||
// find the best matching icon for a given density or size
|
||||
// @output android_icons
|
||||
var parseIcon = function(icon, icon_size) {
|
||||
var parseIcon = function (icon, icon_size) {
|
||||
// do I have a platform icon for that density already
|
||||
var density = icon.density || sizeToDensityMap[icon_size];
|
||||
var density = icon.density || SIZE_TO_DENSITY_MAP[icon_size];
|
||||
if (!density) {
|
||||
// invalid icon defition ( or unsupported size)
|
||||
return;
|
||||
@@ -342,15 +589,37 @@ function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
};
|
||||
|
||||
// iterate over all icon elements to find the default icon and call parseIcon
|
||||
for (var i=0; i<icons.length; i++) {
|
||||
for (var i = 0; i < icons.length; i++) {
|
||||
var icon = icons[i];
|
||||
var size = icon.width;
|
||||
|
||||
if (!size) {
|
||||
size = icon.height;
|
||||
}
|
||||
|
||||
if (!size && !icon.density) {
|
||||
if (default_icon) {
|
||||
events.emit('verbose', 'Found extra default icon: ' + icon.src + ' (ignoring in favor of ' + default_icon.src + ')');
|
||||
const found = {};
|
||||
const favor = {};
|
||||
|
||||
// populating found icon.
|
||||
if (icon.background && icon.foreground) {
|
||||
found.background = icon.background;
|
||||
found.foreground = icon.foreground;
|
||||
}
|
||||
if (icon.src) {
|
||||
found.src = icon.src;
|
||||
}
|
||||
|
||||
if (default_icon.background && default_icon.foreground) {
|
||||
favor.background = default_icon.background;
|
||||
favor.foreground = default_icon.foreground;
|
||||
}
|
||||
if (default_icon.src) {
|
||||
favor.src = default_icon.src;
|
||||
}
|
||||
|
||||
events.emit('verbose', 'Found extra default icon: ' + JSON.stringify(found) + ' and ignoring in favor of ' + JSON.stringify(favor) + '.');
|
||||
} else {
|
||||
default_icon = icon;
|
||||
}
|
||||
@@ -359,51 +628,86 @@ function updateIcons(cordovaProject, platformResourcesDir) {
|
||||
}
|
||||
}
|
||||
|
||||
// The source paths for icons and splashes are relative to
|
||||
// project's config.xml location, so we use it as base path.
|
||||
for (var density in android_icons) {
|
||||
var targetPath = getImageResourcePath(
|
||||
platformResourcesDir, density, 'icon.png', path.basename(android_icons[density].src));
|
||||
resourceMap[targetPath] = android_icons[density].src;
|
||||
}
|
||||
|
||||
// There's no "default" drawable, so assume default == mdpi.
|
||||
if (default_icon && !android_icons.mdpi) {
|
||||
var defaultTargetPath = getImageResourcePath(
|
||||
platformResourcesDir, 'mdpi', 'icon.png', path.basename(default_icon.src));
|
||||
resourceMap[defaultTargetPath] = default_icon.src;
|
||||
}
|
||||
|
||||
events.emit('verbose', 'Updating icons at ' + platformResourcesDir);
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
return {
|
||||
android_icons: android_icons,
|
||||
default_icon: default_icon
|
||||
};
|
||||
}
|
||||
|
||||
function cleanIcons(projectRoot, projectConfig, platformResourcesDir) {
|
||||
function cleanIcons (projectRoot, projectConfig, platformResourcesDir) {
|
||||
var icons = projectConfig.getIcons('android');
|
||||
if (icons.length > 0) {
|
||||
var resourceMap = mapImageResources(projectRoot, platformResourcesDir, 'icon.png');
|
||||
events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir);
|
||||
|
||||
// No source paths are specified in the map, so updatePaths() will delete the target files.
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
|
||||
// Skip if there are no app defined icons in config.xml
|
||||
if (icons.length === 0) {
|
||||
events.emit('verbose', 'This app does not have launcher icons defined');
|
||||
return;
|
||||
}
|
||||
|
||||
const resourceMap = Object.assign(
|
||||
{},
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.png'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_foreground.xml'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher_background.xml'),
|
||||
mapImageResources(projectRoot, platformResourcesDir, 'mipmap', 'ic_launcher.xml')
|
||||
);
|
||||
|
||||
events.emit('verbose', 'Cleaning icons at ' + platformResourcesDir);
|
||||
|
||||
// No source paths are specified in the map, so updatePaths() will delete the target files.
|
||||
FileUpdater.updatePaths(resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a map containing resources of a specified name from all drawable folders in a directory.
|
||||
*/
|
||||
function mapImageResources(rootDir, subDir, resourceName) {
|
||||
var pathMap = {};
|
||||
shell.ls(path.join(rootDir, subDir, 'drawable-*'))
|
||||
.forEach(function (drawableFolder) {
|
||||
var imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
|
||||
function mapImageResources (rootDir, subDir, type, resourceName) {
|
||||
const pathMap = {};
|
||||
const pattern = new RegExp(type + '+-.+');
|
||||
utils.scanDirectory(path.join(rootDir, subDir), pattern).forEach(function (drawableFolder) {
|
||||
const imagePath = path.join(subDir, path.basename(drawableFolder), resourceName);
|
||||
pathMap[imagePath] = null;
|
||||
});
|
||||
return pathMap;
|
||||
}
|
||||
|
||||
function updateFileResources (cordovaProject, platformDir) {
|
||||
var files = cordovaProject.projectConfig.getFileResources('android');
|
||||
|
||||
// if there are resource-file elements in config.xml
|
||||
if (files.length === 0) {
|
||||
events.emit('verbose', 'This app does not have additional resource files defined');
|
||||
return;
|
||||
}
|
||||
|
||||
var resourceMap = {};
|
||||
files.forEach(function (res) {
|
||||
var targetPath = path.join(platformDir, res.target);
|
||||
resourceMap[targetPath] = res.src;
|
||||
});
|
||||
|
||||
events.emit('verbose', 'Updating resource files at ' + platformDir);
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: cordovaProject.root }, logFileOp);
|
||||
}
|
||||
|
||||
function cleanFileResources (projectRoot, projectConfig, platformDir) {
|
||||
var files = projectConfig.getFileResources('android', true);
|
||||
if (files.length > 0) {
|
||||
events.emit('verbose', 'Cleaning resource files at ' + platformDir);
|
||||
|
||||
var resourceMap = {};
|
||||
files.forEach(function (res) {
|
||||
var filePath = path.join(platformDir, res.target);
|
||||
resourceMap[filePath] = null;
|
||||
});
|
||||
|
||||
FileUpdater.updatePaths(
|
||||
resourceMap, { rootDir: projectRoot, all: true }, logFileOp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and validates 'AndroidLaunchMode' prepference from config.xml. Returns
|
||||
* preference value and warns if it doesn't seems to be valid
|
||||
@@ -415,7 +719,7 @@ function mapImageResources(rootDir, subDir, resourceName) {
|
||||
* default value, if there is no such preference. The default value is
|
||||
* 'singleTop'
|
||||
*/
|
||||
function findAndroidLaunchModePreference(platformConfig) {
|
||||
function findAndroidLaunchModePreference (platformConfig) {
|
||||
var launchMode = platformConfig.getPreference('AndroidLaunchMode');
|
||||
if (!launchMode) {
|
||||
// Return a default value
|
||||
|
||||
27
bin/templates/cordova/lib/retry.js
vendored
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,8 +17,6 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint node: true */
|
||||
|
||||
'use strict';
|
||||
|
||||
var events = require('cordova-common').events;
|
||||
@@ -29,39 +25,36 @@ var events = require('cordova-common').events;
|
||||
* Retry a promise-returning function a number of times, propagating its
|
||||
* results on success or throwing its error on a failed final attempt.
|
||||
*
|
||||
* @arg {Number} attemts_left - The number of times to retry the passed call.
|
||||
* @arg {Number} attemptsLeft - The number of times to retry the passed call.
|
||||
* @arg {Function} promiseFunction - A function that returns a promise.
|
||||
* @arg {...} - Arguments to pass to promiseFunction.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
module.exports.retryPromise = function (attemts_left, promiseFunction) {
|
||||
|
||||
module.exports.retryPromise = function (attemptsLeft, promiseFunction) {
|
||||
// NOTE:
|
||||
// get all trailing arguments, by skipping the first two (attemts_left and
|
||||
// get all trailing arguments, by skipping the first two (attemptsLeft and
|
||||
// promiseFunction) because they shouldn't get passed to promiseFunction
|
||||
var promiseFunctionArguments = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
return promiseFunction.apply(undefined, promiseFunctionArguments).then(
|
||||
|
||||
// on success pass results through
|
||||
function onFulfilled(value) {
|
||||
function onFulfilled (value) {
|
||||
return value;
|
||||
},
|
||||
|
||||
// on rejection either retry, or throw the error
|
||||
function onRejected(error) {
|
||||
function onRejected (error) {
|
||||
attemptsLeft -= 1;
|
||||
|
||||
attemts_left -= 1;
|
||||
|
||||
if (attemts_left < 1) {
|
||||
if (attemptsLeft < 1) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
events.emit('verbose', 'A retried call failed. Retrying ' + attemts_left + ' more time(s).');
|
||||
events.emit('verbose', 'A retried call failed. Retrying ' + attemptsLeft + ' more time(s).');
|
||||
|
||||
// retry call self again with the same arguments, except attemts_left is now lower
|
||||
var fullArguments = [attemts_left, promiseFunction].concat(promiseFunctionArguments);
|
||||
// retry call self again with the same arguments, except attemptsLeft is now lower
|
||||
var fullArguments = [attemptsLeft, promiseFunction].concat(promiseFunctionArguments);
|
||||
return module.exports.retryPromise.apply(undefined, fullArguments);
|
||||
}
|
||||
);
|
||||
|
||||
82
bin/templates/cordova/lib/run.js
vendored
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
@@ -19,16 +17,13 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/* jshint loopfunc:true */
|
||||
var path = require('path');
|
||||
var emulator = require('./emulator');
|
||||
var device = require('./device');
|
||||
var PackageType = require('./PackageType');
|
||||
const { CordovaError, events } = require('cordova-common');
|
||||
|
||||
var path = require('path'),
|
||||
build = require('./build'),
|
||||
emulator = require('./emulator'),
|
||||
device = require('./device'),
|
||||
Q = require('q'),
|
||||
events = require('cordova-common').events;
|
||||
|
||||
function getInstallTarget(runOptions) {
|
||||
function getInstallTarget (runOptions) {
|
||||
var install_target;
|
||||
if (runOptions.target) {
|
||||
install_target = runOptions.target;
|
||||
@@ -51,17 +46,16 @@ function getInstallTarget(runOptions) {
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
module.exports.run = function(runOptions) {
|
||||
module.exports.run = function (runOptions) {
|
||||
runOptions = runOptions || {};
|
||||
|
||||
var self = this;
|
||||
var install_target = getInstallTarget(runOptions);
|
||||
|
||||
return Q()
|
||||
.then(function() {
|
||||
return Promise.resolve().then(function () {
|
||||
if (!install_target) {
|
||||
// no target given, deploy to device if available, otherwise use the emulator.
|
||||
return device.list()
|
||||
.then(function(device_list) {
|
||||
return device.list().then(function (device_list) {
|
||||
if (device_list.length > 0) {
|
||||
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
|
||||
install_target = device_list[0];
|
||||
@@ -71,63 +65,65 @@ function getInstallTarget(runOptions) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}).then(function() {
|
||||
if (install_target == '--device') {
|
||||
}).then(function () {
|
||||
if (install_target === '--device') {
|
||||
return device.resolveTarget(null);
|
||||
} else if (install_target == '--emulator') {
|
||||
} else if (install_target === '--emulator') {
|
||||
// Give preference to any already started emulators. Else, start one.
|
||||
return emulator.list_started()
|
||||
.then(function(started) {
|
||||
return emulator.list_started().then(function (started) {
|
||||
return started && started.length > 0 ? started[0] : emulator.start();
|
||||
}).then(function(emulatorId) {
|
||||
}).then(function (emulatorId) {
|
||||
return emulator.resolveTarget(emulatorId);
|
||||
});
|
||||
}
|
||||
// They specified a specific device/emulator ID.
|
||||
return device.list()
|
||||
.then(function(devices) {
|
||||
return device.list().then(function (devices) {
|
||||
if (devices.indexOf(install_target) > -1) {
|
||||
return device.resolveTarget(install_target);
|
||||
}
|
||||
return emulator.list_started()
|
||||
.then(function(started_emulators) {
|
||||
return emulator.list_started().then(function (started_emulators) {
|
||||
if (started_emulators.indexOf(install_target) > -1) {
|
||||
return emulator.resolveTarget(install_target);
|
||||
}
|
||||
return emulator.list_images()
|
||||
.then(function(avds) {
|
||||
return emulator.list_images().then(function (avds) {
|
||||
// if target emulator isn't started, then start it.
|
||||
for (var avd in avds) {
|
||||
if (avds[avd].name == install_target) {
|
||||
return emulator.start(install_target)
|
||||
.then(function(emulatorId) {
|
||||
if (avds[avd].name === install_target) {
|
||||
return emulator.start(install_target).then(function (emulatorId) {
|
||||
return emulator.resolveTarget(emulatorId);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
|
||||
return Promise.reject(new CordovaError(`Target '${install_target}' not found, unable to run project`));
|
||||
});
|
||||
});
|
||||
});
|
||||
}).then(function(resolvedTarget) {
|
||||
// Better just call self.build, but we're doing some processing of
|
||||
// build results (according to platformApi spec) so they are in different
|
||||
// format than emulator.install expects.
|
||||
// TODO: Update emulator/device.install to handle this change
|
||||
return build.run.call(self, runOptions, resolvedTarget)
|
||||
.then(function(buildResults) {
|
||||
if (resolvedTarget.isEmulator) {
|
||||
return emulator.wait_for_boot(resolvedTarget.target)
|
||||
.then(function () {
|
||||
}).then(function (resolvedTarget) {
|
||||
return new Promise((resolve) => {
|
||||
const builder = require('./builders/builders').getBuilder();
|
||||
const buildOptions = require('./build').parseBuildOptions(runOptions, null, self.root);
|
||||
|
||||
// Android app bundles cannot be deployed directly to the device
|
||||
if (buildOptions.packageType === PackageType.BUNDLE) {
|
||||
const packageTypeErrorMessage = 'Package type "bundle" is not supported during cordova run.';
|
||||
events.emit('error', packageTypeErrorMessage);
|
||||
throw packageTypeErrorMessage;
|
||||
}
|
||||
|
||||
resolve(builder.fetchBuildResults(buildOptions.buildType, buildOptions.arch));
|
||||
}).then(function (buildResults) {
|
||||
if (resolvedTarget && resolvedTarget.isEmulator) {
|
||||
return emulator.wait_for_boot(resolvedTarget.target).then(function () {
|
||||
return emulator.install(resolvedTarget, buildResults);
|
||||
});
|
||||
}
|
||||
|
||||
return device.install(resolvedTarget, buildResults);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
module.exports.help = function () {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]) + ' [options]');
|
||||
console.log('Build options :');
|
||||
console.log(' --debug : Builds project in debug mode');
|
||||
|
||||
@@ -19,21 +19,20 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var emulator = require('./emulator'),
|
||||
args = process.argv;
|
||||
var emulator = require('./emulator');
|
||||
var args = process.argv;
|
||||
|
||||
var install_target;
|
||||
if(args.length > 2) {
|
||||
if (args[2].substring(0, 9) == '--target=') {
|
||||
if (args.length > 2) {
|
||||
if (args[2].substring(0, 9) === '--target=') {
|
||||
install_target = args[2].substring(9, args[2].length);
|
||||
} else {
|
||||
} else {
|
||||
console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emulator.start(install_target).done(null, function(err) {
|
||||
emulator.start(install_target).catch(function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
:: to you under the Apache License, Version 2.0 (the
|
||||
:: "License"); you may not use this file except in compliance
|
||||
:: with the License. You may obtain a copy of the License at
|
||||
::
|
||||
::
|
||||
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||
::
|
||||
::
|
||||
:: Unless required by applicable law or agreed to in writing,
|
||||
:: software distributed under the License is distributed on an
|
||||
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@@ -18,7 +18,7 @@
|
||||
@ECHO OFF
|
||||
SET script_path="%~dp0start-emulator"
|
||||
IF EXIST %script_path% (
|
||||
node "%script_path%" %*
|
||||
node %script_path% %*
|
||||
) ELSE (
|
||||
ECHO.
|
||||
ECHO ERROR: Could not find 'start-emulator' script in 'cordova\lib' folder, aborting...>&2
|
||||
|
||||
97
bin/templates/cordova/lib/utils.js
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Provides a set of utility methods, which can also be spied on during unit tests.
|
||||
*/
|
||||
|
||||
// TODO: Perhaps this should live in cordova-common?
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Reads, searches, and replaces the found occurences with replacementString and then writes the file back out.
|
||||
* A backup is not made.
|
||||
*
|
||||
* @param {string} file A file path to a readable & writable file
|
||||
* @param {RegExp} searchRegex The search regex
|
||||
* @param {string} replacementString The string to replace the found occurences
|
||||
* @returns void
|
||||
*/
|
||||
exports.replaceFileContents = function (file, searchRegex, replacementString) {
|
||||
let contents = fs.readFileSync(file).toString();
|
||||
contents = contents.replace(searchRegex, replacementString);
|
||||
fs.writeFileSync(file, contents);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reads a file and scans for regex. Returns the line of the first occurence or null if no occurences are found.
|
||||
*
|
||||
* @param {string} file A file path
|
||||
* @param {RegExp} regex A search regex
|
||||
* @returns string|null
|
||||
*/
|
||||
exports.grep = function (file, regex) {
|
||||
const contents = fs.readFileSync(file).toString().replace(/\\r/g, '').split('\n');
|
||||
for (let i = 0; i < contents.length; i++) {
|
||||
const line = contents[i];
|
||||
if (regex.test(line)) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Scans directories and outputs a list of found paths that matches the regex
|
||||
*
|
||||
* @param {string} directory The starting directory
|
||||
* @param {RegExp} regex The search regex
|
||||
* @param {boolean} recursive Enables recursion
|
||||
* @returns Array<string>
|
||||
*/
|
||||
exports.scanDirectory = function (directory, regex, recursive) {
|
||||
let output = [];
|
||||
|
||||
if (fs.existsSync(directory)) {
|
||||
const items = fs.readdirSync(directory);
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
const itemPath = path.join(directory, item);
|
||||
const stats = fs.statSync(itemPath);
|
||||
|
||||
if (regex.test(itemPath)) {
|
||||
output.push(itemPath);
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
if (recursive) {
|
||||
output = output.concat(exports.scanDirectory(itemPath, regex, recursive));
|
||||
} else {
|
||||
// Move onto the next item
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
||||
@@ -19,17 +19,17 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var log = require('./lib/log'),
|
||||
reqs = require('./lib/check_reqs'),
|
||||
args = process.argv;
|
||||
var log = require('./lib/log');
|
||||
var reqs = require('./lib/check_reqs');
|
||||
var args = process.argv;
|
||||
|
||||
// Usage support for when args are given
|
||||
if(args.length > 2) {
|
||||
if (args.length > 2) {
|
||||
log.help();
|
||||
} else {
|
||||
reqs.run().done(function() {
|
||||
reqs.run().then(function () {
|
||||
return log.run();
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
console.error('ERROR: ' + err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -24,22 +24,23 @@ var nopt = require('nopt');
|
||||
var path = require('path');
|
||||
|
||||
// Support basic help commands
|
||||
if(['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0)
|
||||
if (['--help', '/?', '-h', 'help', '-help', '/help'].indexOf(process.argv[2]) >= 0) {
|
||||
require('./lib/run').help();
|
||||
}
|
||||
|
||||
// Do some basic argument parsing
|
||||
var runOpts = nopt({
|
||||
'verbose' : Boolean,
|
||||
'silent' : Boolean,
|
||||
'debug' : Boolean,
|
||||
'release' : Boolean,
|
||||
'nobuild': Boolean,
|
||||
'buildConfig' : path,
|
||||
'archs' : String,
|
||||
'device' : Boolean,
|
||||
'emulator': Boolean,
|
||||
'target' : String
|
||||
}, { 'd' : '--verbose' });
|
||||
verbose: Boolean,
|
||||
silent: Boolean,
|
||||
debug: Boolean,
|
||||
release: Boolean,
|
||||
nobuild: Boolean,
|
||||
buildConfig: path,
|
||||
archs: String,
|
||||
device: Boolean,
|
||||
emulator: Boolean,
|
||||
target: String
|
||||
}, { d: '--verbose' });
|
||||
|
||||
// Make runOptions compatible with PlatformApi run method spec
|
||||
runOpts.argv = runOpts.argv.remain;
|
||||
@@ -47,7 +48,7 @@ runOpts.argv = runOpts.argv.remain;
|
||||
require('./loggingHelper').adjustLoggerLevel(runOpts);
|
||||
|
||||
new Api().run(runOpts)
|
||||
.catch(function(err) {
|
||||
console.error(err, err.stack);
|
||||
process.exit(2);
|
||||
});
|
||||
.catch(function (err) {
|
||||
console.error(err, err.stack);
|
||||
process.exit(2);
|
||||
});
|
||||
|
||||
@@ -19,11 +19,6 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
// Coho updates this line:
|
||||
var VERSION = "5.2.0-dev";
|
||||
const Api = require('./Api');
|
||||
|
||||
module.exports.version = VERSION;
|
||||
|
||||
if (!module.parent) {
|
||||
console.log(VERSION);
|
||||
}
|
||||
console.log(Api.version());
|
||||
|
||||
@@ -28,6 +28,13 @@ public class __ACTIVITY__ extends CordovaActivity
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// enable Cordova apps to be started in the background
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
// Set by <content src="index.html" /> in config.xml
|
||||
loadUrl(launchUrl);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="__PACKAGE__" android:versionName="1.0" android:versionCode="1" android:hardwareAccelerated="true">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
@@ -30,20 +30,18 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
<application android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true" android:supportsRtl="true">
|
||||
<activity android:name="__ACTIVITY__"
|
||||
android:label="@string/activity_name"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode">
|
||||
<intent-filter android:label="@string/launcher_name">
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="__APILEVEL__"/>
|
||||
</manifest>
|
||||
|
||||
420
bin/templates/project/app/build.gradle
Normal file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
}
|
||||
|
||||
buildscript {
|
||||
apply from: '../CordovaLib/cordova.gradle'
|
||||
|
||||
if(cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
|
||||
String defaultGradlePluginKotlinVersion = kotlin_version
|
||||
|
||||
/**
|
||||
* Fetches the user's defined Kotlin Version from config.xml.
|
||||
* If the version is not set or invalid, it will default to the ${defaultGradlePluginKotlinVersion}
|
||||
*/
|
||||
String gradlePluginKotlinVersion = cdvHelpers.getConfigPreference('GradlePluginKotlinVersion', defaultGradlePluginKotlinVersion)
|
||||
if(!cdvHelpers.isVersionValid(gradlePluginKotlinVersion)) {
|
||||
println("The defined Kotlin version (${gradlePluginKotlinVersion}) does not appear to be a valid version. Falling back to version: ${defaultGradlePluginKotlinVersion}.")
|
||||
gradlePluginKotlinVersion = defaultGradlePluginKotlinVersion
|
||||
}
|
||||
|
||||
// Change the version to be used.
|
||||
ext.kotlin_version = gradlePluginKotlinVersion
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
apply from: '../CordovaLib/cordova.gradle'
|
||||
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
|
||||
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
|
||||
if(cdvHelpers.getConfigPreference('GradlePluginGoogleServicesEnabled', 'false').toBoolean()) {
|
||||
String defaultGradlePluginGoogleServicesVersion = '4.2.0'
|
||||
|
||||
/**
|
||||
* Fetches the user's defined Google Services Plugin Version from config.xml.
|
||||
* If the version is not set or invalid, it will default to the ${defaultGradlePluginGoogleServicesVersion}
|
||||
*/
|
||||
String gradlePluginGoogleServicesVersion = cdvHelpers.getConfigPreference('GradlePluginGoogleServicesVersion', defaultGradlePluginGoogleServicesVersion)
|
||||
if(!cdvHelpers.isVersionValid(gradlePluginGoogleServicesVersion)) {
|
||||
println("The defined Google Services plugin version (${gradlePluginGoogleServicesVersion}) does not appear to be a valid version. Falling back to version: ${defaultGradlePluginGoogleServicesVersion}.")
|
||||
gradlePluginGoogleServicesVersion = defaultGradlePluginGoogleServicesVersion
|
||||
}
|
||||
|
||||
// Create the Google Services classpath and set it.
|
||||
String gradlePluginGoogleServicesClassPath = "com.google.gms:google-services:${gradlePluginGoogleServicesVersion}"
|
||||
println "Adding classpath: ${gradlePluginGoogleServicesClassPath}"
|
||||
classpath gradlePluginGoogleServicesClassPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow plugins to declare Maven dependencies via build-extras.gradle.
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '6.5'
|
||||
}
|
||||
|
||||
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
|
||||
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
|
||||
ext {
|
||||
apply from: '../CordovaLib/cordova.gradle'
|
||||
|
||||
// The value for android.compileSdkVersion.
|
||||
if (!project.hasProperty('cdvCompileSdkVersion')) {
|
||||
cdvCompileSdkVersion = null;
|
||||
}
|
||||
// The value for android.buildToolsVersion.
|
||||
if (!project.hasProperty('cdvBuildToolsVersion')) {
|
||||
cdvBuildToolsVersion = null;
|
||||
}
|
||||
// Sets the versionCode to the given value.
|
||||
if (!project.hasProperty('cdvVersionCode')) {
|
||||
cdvVersionCode = null
|
||||
}
|
||||
// Sets the minSdkVersion to the given value.
|
||||
if (!project.hasProperty('cdvMinSdkVersion')) {
|
||||
cdvMinSdkVersion = null
|
||||
}
|
||||
// Sets the maxSdkVersion to the given value.
|
||||
if (!project.hasProperty('cdvMaxSdkVersion')) {
|
||||
cdvMaxSdkVersion = null
|
||||
}
|
||||
// The value for android.targetSdkVersion.
|
||||
if (!project.hasProperty('cdvTargetSdkVersion')) {
|
||||
cdvTargetSdkVersion = null;
|
||||
}
|
||||
// Whether to build architecture-specific APKs.
|
||||
if (!project.hasProperty('cdvBuildMultipleApks')) {
|
||||
cdvBuildMultipleApks = null
|
||||
}
|
||||
// Whether to append a 0 "abi digit" to versionCode when only a single APK is build
|
||||
if (!project.hasProperty('cdvVersionCodeForceAbiDigit')) {
|
||||
cdvVersionCodeForceAbiDigit = null
|
||||
}
|
||||
// .properties files to use for release signing.
|
||||
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
|
||||
cdvReleaseSigningPropertiesFile = null
|
||||
}
|
||||
// .properties files to use for debug signing.
|
||||
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
|
||||
cdvDebugSigningPropertiesFile = null
|
||||
}
|
||||
// Set by build.js script.
|
||||
if (!project.hasProperty('cdvBuildArch')) {
|
||||
cdvBuildArch = null
|
||||
}
|
||||
|
||||
// Plugin gradle extensions can append to this to have code run at the end.
|
||||
cdvPluginPostBuildExtras = []
|
||||
}
|
||||
|
||||
// PLUGIN GRADLE EXTENSIONS START
|
||||
// PLUGIN GRADLE EXTENSIONS END
|
||||
|
||||
def hasBuildExtras1 = file('build-extras.gradle').exists()
|
||||
if (hasBuildExtras1) {
|
||||
apply from: 'build-extras.gradle'
|
||||
}
|
||||
|
||||
def hasBuildExtras2 = file('../build-extras.gradle').exists()
|
||||
if (hasBuildExtras2) {
|
||||
apply from: '../build-extras.gradle'
|
||||
}
|
||||
|
||||
// Set property defaults after extension .gradle files.
|
||||
ext.cdvCompileSdkVersion = cdvCompileSdkVersion == null ? (
|
||||
defaultCompileSdkVersion == null
|
||||
? privateHelpers.getProjectTarget()
|
||||
: defaultCompileSdkVersion
|
||||
) : Integer.parseInt('' + cdvCompileSdkVersion);
|
||||
|
||||
if (ext.cdvBuildToolsVersion == null) {
|
||||
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
|
||||
//ext.cdvBuildToolsVersion = project.ext.defaultBuildToolsVersion
|
||||
}
|
||||
if (ext.cdvDebugSigningPropertiesFile == null && file('../debug-signing.properties').exists()) {
|
||||
ext.cdvDebugSigningPropertiesFile = '../debug-signing.properties'
|
||||
}
|
||||
if (ext.cdvReleaseSigningPropertiesFile == null && file('../release-signing.properties').exists()) {
|
||||
ext.cdvReleaseSigningPropertiesFile = '../release-signing.properties'
|
||||
}
|
||||
|
||||
// Cast to appropriate types.
|
||||
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
|
||||
ext.cdvVersionCodeForceAbiDigit = cdvVersionCodeForceAbiDigit == null ? false : cdvVersionCodeForceAbiDigit.toBoolean();
|
||||
|
||||
// minSdkVersion, maxSdkVersion and targetSdkVersion
|
||||
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? defaultMinSdkVersion : Integer.parseInt('' + cdvMinSdkVersion)
|
||||
if (cdvMaxSdkVersion != null) {
|
||||
ext.cdvMaxSdkVersion = Integer.parseInt('' + cdvMaxSdkVersion)
|
||||
}
|
||||
ext.cdvTargetSdkVersion = cdvTargetSdkVersion == null ? defaultTargetSdkVersion : Integer.parseInt('' + cdvTargetSdkVersion)
|
||||
|
||||
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
|
||||
|
||||
def computeBuildTargetName(debugBuild) {
|
||||
def ret = 'assemble'
|
||||
if (cdvBuildMultipleApks && cdvBuildArch) {
|
||||
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
|
||||
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
|
||||
}
|
||||
return ret + (debugBuild ? 'Debug' : 'Release')
|
||||
}
|
||||
|
||||
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
|
||||
task cdvBuildDebug
|
||||
cdvBuildDebug.dependsOn {
|
||||
return computeBuildTargetName(true)
|
||||
}
|
||||
|
||||
task cdvBuildRelease
|
||||
cdvBuildRelease.dependsOn {
|
||||
return computeBuildTargetName(false)
|
||||
}
|
||||
|
||||
task cdvPrintProps {
|
||||
doLast {
|
||||
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
|
||||
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
|
||||
println('cdvVersionCode=' + cdvVersionCode)
|
||||
println('cdvVersionCodeForceAbiDigit=' + cdvVersionCodeForceAbiDigit)
|
||||
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
|
||||
println('cdvMaxSdkVersion=' + cdvMaxSdkVersion)
|
||||
println('cdvTargetSdkVersion=' + cdvTargetSdkVersion)
|
||||
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
|
||||
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
|
||||
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
|
||||
println('cdvBuildArch=' + cdvBuildArch)
|
||||
println('computedVersionCode=' + android.defaultConfig.versionCode)
|
||||
android.productFlavors.each { flavor ->
|
||||
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
versionCode cdvVersionCode ?: new BigInteger("" + privateHelpers.extractIntFromManifest("versionCode"))
|
||||
applicationId privateHelpers.extractStringFromManifest("package")
|
||||
|
||||
if (cdvMinSdkVersion != null) {
|
||||
minSdkVersion cdvMinSdkVersion
|
||||
}
|
||||
|
||||
if (cdvMaxSdkVersion != null) {
|
||||
maxSdkVersion cdvMaxSdkVersion
|
||||
}
|
||||
|
||||
if(cdvTargetSdkVersion != null) {
|
||||
targetSdkVersion cdvTargetSdkVersion
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false;
|
||||
}
|
||||
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
|
||||
// This code exists for Crosswalk and other Native APIs.
|
||||
// By default, we multiply the existing version code in the
|
||||
// Android Manifest by 10 and add a number for each architecture.
|
||||
// If you are not using Crosswalk or SQLite, you can
|
||||
// ignore this chunk of code, and your version codes will be respected.
|
||||
|
||||
if (Boolean.valueOf(cdvBuildMultipleApks)) {
|
||||
flavorDimensions "default"
|
||||
|
||||
productFlavors {
|
||||
armeabi {
|
||||
versionCode defaultConfig.versionCode*10 + 1
|
||||
ndk {
|
||||
abiFilters = ["armeabi"]
|
||||
}
|
||||
}
|
||||
armv7 {
|
||||
versionCode defaultConfig.versionCode*10 + 2
|
||||
ndk {
|
||||
abiFilters = ["armeabi-v7a"]
|
||||
}
|
||||
}
|
||||
arm64 {
|
||||
versionCode defaultConfig.versionCode*10 + 3
|
||||
ndk {
|
||||
abiFilters = ["arm64-v8a"]
|
||||
}
|
||||
}
|
||||
x86 {
|
||||
versionCode defaultConfig.versionCode*10 + 4
|
||||
ndk {
|
||||
abiFilters = ["x86"]
|
||||
}
|
||||
}
|
||||
x86_64 {
|
||||
versionCode defaultConfig.versionCode*10 + 5
|
||||
ndk {
|
||||
abiFilters = ["x86_64"]
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Boolean.valueOf(cdvVersionCodeForceAbiDigit)) {
|
||||
// This provides compatibility to the default logic for versionCode before cordova-android 5.2.0
|
||||
defaultConfig {
|
||||
versionCode defaultConfig.versionCode*10
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
if (cdvReleaseSigningPropertiesFile) {
|
||||
signingConfigs {
|
||||
release {
|
||||
// These must be set or Gradle will complain (even if they are overridden).
|
||||
keyAlias = ""
|
||||
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
|
||||
storeFile = null
|
||||
storePassword = "__unset"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
|
||||
}
|
||||
|
||||
if (cdvDebugSigningPropertiesFile) {
|
||||
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* WARNING: Cordova Lib and platform scripts do management inside of this code here,
|
||||
* if you are adding the dependencies manually, do so outside the comments, otherwise
|
||||
* the Cordova tools will overwrite them
|
||||
*/
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||
|
||||
if (cdvHelpers.getConfigPreference('GradlePluginKotlinEnabled', 'false').toBoolean()) {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
|
||||
// SUB-PROJECT DEPENDENCIES START
|
||||
debugCompile(project(path: ":CordovaLib", configuration: "debug"))
|
||||
releaseCompile(project(path: ":CordovaLib", configuration: "release"))
|
||||
// SUB-PROJECT DEPENDENCIES END
|
||||
}
|
||||
|
||||
def promptForReleaseKeyPassword() {
|
||||
if (!cdvReleaseSigningPropertiesFile) {
|
||||
return;
|
||||
}
|
||||
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
|
||||
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
|
||||
}
|
||||
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
|
||||
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
|
||||
}
|
||||
}
|
||||
|
||||
gradle.taskGraph.whenReady { taskGraph ->
|
||||
taskGraph.getAllTasks().each() { task ->
|
||||
if(['validateReleaseSigning', 'validateSigningRelease', 'validateSigningArmv7Release', 'validateSigningX76Release'].contains(task.name)) {
|
||||
promptForReleaseKeyPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def addSigningProps(propsFilePath, signingConfig) {
|
||||
def propsFile = file(propsFilePath)
|
||||
def props = new Properties()
|
||||
propsFile.withReader { reader ->
|
||||
props.load(reader)
|
||||
}
|
||||
|
||||
def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
|
||||
if (!storeFile.isAbsolute()) {
|
||||
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
|
||||
}
|
||||
if (!storeFile.exists()) {
|
||||
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
|
||||
}
|
||||
signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
|
||||
signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
|
||||
signingConfig.storeFile = storeFile
|
||||
signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
|
||||
def storeType = props.get('storeType', props.get('key.store.type', ''))
|
||||
if (!storeType) {
|
||||
def filename = storeFile.getName().toLowerCase();
|
||||
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
|
||||
storeType = 'pkcs12'
|
||||
} else {
|
||||
storeType = signingConfig.storeType // "jks"
|
||||
}
|
||||
}
|
||||
signingConfig.storeType = storeType
|
||||
}
|
||||
|
||||
for (def func : cdvPluginPostBuildExtras) {
|
||||
func()
|
||||
}
|
||||
|
||||
// This can be defined within build-extras.gradle as:
|
||||
// ext.postBuildExtras = { ... code here ... }
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
}
|
||||
|
||||
if (cdvHelpers.getConfigPreference('GradlePluginGoogleServicesEnabled', 'false').toBoolean()) {
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
}
|
||||
1267
bin/templates/project/assets/www/cordova.js
vendored
@@ -102,13 +102,13 @@ h1 {
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
|
||||
.blink {
|
||||
animation:fade 3000ms infinite;
|
||||
-webkit-animation:fade 3000ms infinite;
|
||||
|
||||
@@ -18,25 +18,25 @@
|
||||
*/
|
||||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function() {
|
||||
initialize: function () {
|
||||
this.bindEvents();
|
||||
},
|
||||
// Bind Event Listeners
|
||||
//
|
||||
// Bind any events that are required on startup. Common events are:
|
||||
// 'load', 'deviceready', 'offline', and 'online'.
|
||||
bindEvents: function() {
|
||||
bindEvents: function () {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
// deviceready Event Handler
|
||||
//
|
||||
// The scope of 'this' is the event. In order to call the 'receivedEvent'
|
||||
// function, we must explicitly call 'app.receivedEvent(...);'
|
||||
onDeviceReady: function() {
|
||||
onDeviceReady: function () {
|
||||
app.receivedEvent('deviceready');
|
||||
},
|
||||
// Update DOM on a Received Event
|
||||
receivedEvent: function(id) {
|
||||
receivedEvent: function (id) {
|
||||
var parentElement = document.getElementById(id);
|
||||
var listeningElement = parentElement.querySelector('.listening');
|
||||
var receivedElement = parentElement.querySelector('.received');
|
||||
@@ -48,4 +48,4 @@ var app = {
|
||||
}
|
||||
};
|
||||
|
||||
app.initialize();
|
||||
app.initialize();
|
||||
|
||||
@@ -1,311 +1,52 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
/* Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
// Switch the Android Gradle plugin version requirement depending on the
|
||||
// installed version of Gradle. This dependency is documented at
|
||||
// http://tools.android.com/tech-docs/new-build-system/version-compatibility
|
||||
// and https://issues.apache.org/jira/browse/CB-8143
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.1.0'
|
||||
classpath 'com.android.tools.build:gradle:4.0.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
// Allow plugins to declare Maven dependencies via build-extras.gradle.
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenCentral();
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '2.13'
|
||||
}
|
||||
|
||||
// Configuration properties. Set these via environment variables, build-extras.gradle, or gradle.properties.
|
||||
// Refer to: http://www.gradle.org/docs/current/userguide/tutorial_this_and_that.html
|
||||
ext {
|
||||
apply from: 'CordovaLib/cordova.gradle'
|
||||
// The value for android.compileSdkVersion.
|
||||
if (!project.hasProperty('cdvCompileSdkVersion')) {
|
||||
cdvCompileSdkVersion = null;
|
||||
}
|
||||
// The value for android.buildToolsVersion.
|
||||
if (!project.hasProperty('cdvBuildToolsVersion')) {
|
||||
cdvBuildToolsVersion = null;
|
||||
}
|
||||
// Sets the versionCode to the given value.
|
||||
if (!project.hasProperty('cdvVersionCode')) {
|
||||
cdvVersionCode = null
|
||||
}
|
||||
// Sets the minSdkVersion to the given value.
|
||||
if (!project.hasProperty('cdvMinSdkVersion')) {
|
||||
cdvMinSdkVersion = null
|
||||
}
|
||||
// Whether to build architecture-specific APKs.
|
||||
if (!project.hasProperty('cdvBuildMultipleApks')) {
|
||||
cdvBuildMultipleApks = null
|
||||
}
|
||||
// .properties files to use for release signing.
|
||||
if (!project.hasProperty('cdvReleaseSigningPropertiesFile')) {
|
||||
cdvReleaseSigningPropertiesFile = null
|
||||
}
|
||||
// .properties files to use for debug signing.
|
||||
if (!project.hasProperty('cdvDebugSigningPropertiesFile')) {
|
||||
cdvDebugSigningPropertiesFile = null
|
||||
}
|
||||
// Set by build.js script.
|
||||
if (!project.hasProperty('cdvBuildArch')) {
|
||||
cdvBuildArch = null
|
||||
}
|
||||
|
||||
// Plugin gradle extensions can append to this to have code run at the end.
|
||||
cdvPluginPostBuildExtras = []
|
||||
}
|
||||
|
||||
// PLUGIN GRADLE EXTENSIONS START
|
||||
// PLUGIN GRADLE EXTENSIONS END
|
||||
|
||||
def hasBuildExtras = file('build-extras.gradle').exists()
|
||||
if (hasBuildExtras) {
|
||||
apply from: 'build-extras.gradle'
|
||||
}
|
||||
|
||||
// Set property defaults after extension .gradle files.
|
||||
if (ext.cdvCompileSdkVersion == null) {
|
||||
ext.cdvCompileSdkVersion = privateHelpers.getProjectTarget()
|
||||
}
|
||||
if (ext.cdvBuildToolsVersion == null) {
|
||||
ext.cdvBuildToolsVersion = privateHelpers.findLatestInstalledBuildTools()
|
||||
}
|
||||
if (ext.cdvDebugSigningPropertiesFile == null && file('debug-signing.properties').exists()) {
|
||||
ext.cdvDebugSigningPropertiesFile = 'debug-signing.properties'
|
||||
}
|
||||
if (ext.cdvReleaseSigningPropertiesFile == null && file('release-signing.properties').exists()) {
|
||||
ext.cdvReleaseSigningPropertiesFile = 'release-signing.properties'
|
||||
}
|
||||
|
||||
// Cast to appropriate types.
|
||||
ext.cdvBuildMultipleApks = cdvBuildMultipleApks == null ? false : cdvBuildMultipleApks.toBoolean();
|
||||
ext.cdvMinSdkVersion = cdvMinSdkVersion == null ? null : Integer.parseInt('' + cdvMinSdkVersion)
|
||||
ext.cdvVersionCode = cdvVersionCode == null ? null : Integer.parseInt('' + cdvVersionCode)
|
||||
|
||||
def computeBuildTargetName(debugBuild) {
|
||||
def ret = 'assemble'
|
||||
if (cdvBuildMultipleApks && cdvBuildArch) {
|
||||
def arch = cdvBuildArch == 'arm' ? 'armv7' : cdvBuildArch
|
||||
ret += '' + arch.toUpperCase().charAt(0) + arch.substring(1);
|
||||
}
|
||||
return ret + (debugBuild ? 'Debug' : 'Release')
|
||||
}
|
||||
|
||||
// Make cdvBuild a task that depends on the debug/arch-sepecific task.
|
||||
task cdvBuildDebug
|
||||
cdvBuildDebug.dependsOn {
|
||||
return computeBuildTargetName(true)
|
||||
}
|
||||
|
||||
task cdvBuildRelease
|
||||
cdvBuildRelease.dependsOn {
|
||||
return computeBuildTargetName(false)
|
||||
}
|
||||
|
||||
task cdvPrintProps << {
|
||||
println('cdvCompileSdkVersion=' + cdvCompileSdkVersion)
|
||||
println('cdvBuildToolsVersion=' + cdvBuildToolsVersion)
|
||||
println('cdvVersionCode=' + cdvVersionCode)
|
||||
println('cdvMinSdkVersion=' + cdvMinSdkVersion)
|
||||
println('cdvBuildMultipleApks=' + cdvBuildMultipleApks)
|
||||
println('cdvReleaseSigningPropertiesFile=' + cdvReleaseSigningPropertiesFile)
|
||||
println('cdvDebugSigningPropertiesFile=' + cdvDebugSigningPropertiesFile)
|
||||
println('cdvBuildArch=' + cdvBuildArch)
|
||||
println('computedVersionCode=' + android.defaultConfig.versionCode)
|
||||
android.productFlavors.each { flavor ->
|
||||
println('computed' + flavor.name.capitalize() + 'VersionCode=' + flavor.versionCode)
|
||||
//This replaces project.properties w.r.t. build settings
|
||||
project.ext {
|
||||
defaultBuildToolsVersion="29.0.2" //String
|
||||
defaultMinSdkVersion=22 //Integer - Minimum requirement is Android 5.1
|
||||
defaultTargetSdkVersion=29 //Integer - We ALWAYS target the latest by default
|
||||
defaultCompileSdkVersion=29 //Integer - We ALWAYS compile with the latest by default
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
resources.srcDirs = ['src']
|
||||
aidl.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
res.srcDirs = ['res']
|
||||
assets.srcDirs = ['assets']
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode cdvVersionCode ?: Integer.parseInt("" + privateHelpers.extractIntFromManifest("versionCode"))
|
||||
applicationId privateHelpers.extractStringFromManifest("package")
|
||||
|
||||
if (cdvMinSdkVersion != null) {
|
||||
minSdkVersion cdvMinSdkVersion
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false;
|
||||
}
|
||||
|
||||
compileSdkVersion cdvCompileSdkVersion
|
||||
buildToolsVersion cdvBuildToolsVersion
|
||||
|
||||
if (Boolean.valueOf(cdvBuildMultipleApks)) {
|
||||
productFlavors {
|
||||
armv7 {
|
||||
versionCode defaultConfig.versionCode*10 + 2
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", ""
|
||||
}
|
||||
}
|
||||
x86 {
|
||||
versionCode defaultConfig.versionCode*10 + 4
|
||||
ndk {
|
||||
abiFilters "x86", ""
|
||||
}
|
||||
}
|
||||
all {
|
||||
ndk {
|
||||
abiFilters "all", ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
ELSE NOTHING! DON'T MESS WITH THE VERSION CODE IF YOU DON'T HAVE TO!
|
||||
|
||||
else if (!cdvVersionCode) {
|
||||
def minSdkVersion = cdvMinSdkVersion ?: privateHelpers.extractIntFromManifest("minSdkVersion")
|
||||
// Vary versionCode by the two most common API levels:
|
||||
// 14 is ICS, which is the lowest API level for many apps.
|
||||
// 20 is Lollipop, which is the lowest API level for the updatable system webview.
|
||||
if (minSdkVersion >= 20) {
|
||||
defaultConfig.versionCode += 9
|
||||
} else if (minSdkVersion >= 14) {
|
||||
defaultConfig.versionCode += 8
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_6
|
||||
targetCompatibility JavaVersion.VERSION_1_6
|
||||
}
|
||||
|
||||
if (cdvReleaseSigningPropertiesFile) {
|
||||
signingConfigs {
|
||||
release {
|
||||
// These must be set or Gradle will complain (even if they are overridden).
|
||||
keyAlias = ""
|
||||
keyPassword = "__unset" // And these must be set to non-empty in order to have the signing step added to the task graph.
|
||||
storeFile = null
|
||||
storePassword = "__unset"
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
addSigningProps(cdvReleaseSigningPropertiesFile, signingConfigs.release)
|
||||
}
|
||||
if (cdvDebugSigningPropertiesFile) {
|
||||
addSigningProps(cdvDebugSigningPropertiesFile, signingConfigs.debug)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
// SUB-PROJECT DEPENDENCIES START
|
||||
// SUB-PROJECT DEPENDENCIES END
|
||||
}
|
||||
|
||||
def promptForReleaseKeyPassword() {
|
||||
if (!cdvReleaseSigningPropertiesFile) {
|
||||
return;
|
||||
}
|
||||
if ('__unset'.equals(android.signingConfigs.release.storePassword)) {
|
||||
android.signingConfigs.release.storePassword = privateHelpers.promptForPassword('Enter key store password: ')
|
||||
}
|
||||
if ('__unset'.equals(android.signingConfigs.release.keyPassword)) {
|
||||
android.signingConfigs.release.keyPassword = privateHelpers.promptForPassword('Enter key password: ');
|
||||
}
|
||||
}
|
||||
|
||||
gradle.taskGraph.whenReady { taskGraph ->
|
||||
taskGraph.getAllTasks().each() { task ->
|
||||
if (task.name == 'validateReleaseSigning') {
|
||||
promptForReleaseKeyPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def addSigningProps(propsFilePath, signingConfig) {
|
||||
def propsFile = file(propsFilePath)
|
||||
def props = new Properties()
|
||||
propsFile.withReader { reader ->
|
||||
props.load(reader)
|
||||
}
|
||||
|
||||
def storeFile = new File(props.get('key.store') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'storeFile'))
|
||||
if (!storeFile.isAbsolute()) {
|
||||
storeFile = RelativePath.parse(true, storeFile.toString()).getFile(propsFile.getParentFile())
|
||||
}
|
||||
if (!storeFile.exists()) {
|
||||
throw new FileNotFoundException('Keystore file does not exist: ' + storeFile.getAbsolutePath())
|
||||
}
|
||||
signingConfig.keyAlias = props.get('key.alias') ?: privateHelpers.ensureValueExists(propsFilePath, props, 'keyAlias')
|
||||
signingConfig.keyPassword = props.get('keyPassword', props.get('key.alias.password', signingConfig.keyPassword))
|
||||
signingConfig.storeFile = storeFile
|
||||
signingConfig.storePassword = props.get('storePassword', props.get('key.store.password', signingConfig.storePassword))
|
||||
def storeType = props.get('storeType', props.get('key.store.type', ''))
|
||||
if (!storeType) {
|
||||
def filename = storeFile.getName().toLowerCase();
|
||||
if (filename.endsWith('.p12') || filename.endsWith('.pfx')) {
|
||||
storeType = 'pkcs12'
|
||||
} else {
|
||||
storeType = signingConfig.storeType // "jks"
|
||||
}
|
||||
}
|
||||
signingConfig.storeType = storeType
|
||||
}
|
||||
|
||||
for (def func : cdvPluginPostBuildExtras) {
|
||||
func()
|
||||
}
|
||||
|
||||
// This can be defined within build-extras.gradle as:
|
||||
// ext.postBuildExtras = { ... code here ... }
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
@@ -10,5 +10,12 @@ ant-gen
|
||||
# Eclipse builds
|
||||
gen
|
||||
out
|
||||
# Gradle builds
|
||||
# Gradle build artifacts
|
||||
.gradle
|
||||
.gradletasknamecache
|
||||
/build
|
||||
/CordovaLib/build
|
||||
/app/build
|
||||
gradle-app.setting
|
||||
# Android Studio
|
||||
.idea
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
# This file was originally created by the Android Tools, but is now
|
||||
# used by cordova-android to manage the state of the various third party
|
||||
# libraries used in your application
|
||||
|
||||
# This is the Library Module that contains the Cordova Library, this is not
|
||||
# required when using an AAR
|
||||
android.library.reference.1=CordovaLib
|
||||
|
||||
# This is the application project. This is only required for Android Studio Gradle projects
|
||||
android.library.reference.2=app
|
||||
|
||||
# Project target.
|
||||
target=This_gets_replaced
|
||||
|
||||
|
Before Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 478 KiB After Width: | Height: | Size: 78 KiB |
BIN
bin/templates/project/res/drawable-land-xxhdpi/screen.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
bin/templates/project/res/drawable-land-xxxhdpi/screen.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 493 KiB After Width: | Height: | Size: 75 KiB |
BIN
bin/templates/project/res/drawable-port-xxhdpi/screen.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
bin/templates/project/res/drawable-port-xxxhdpi/screen.png
Normal file
|
After Width: | Height: | Size: 192 KiB |