pwa-builder / CloudAPK

Build Android APK packages on the cloud
Other
147 stars 44 forks source link

Any examples for how to use CloudAPK? #13

Closed davidmaxwaterman closed 4 years ago

davidmaxwaterman commented 4 years ago

I think I've managed to get this installed/etc and it seems to be running, but I'm not clear on how to actually use it. I see that I should make a POST with json like in the readme, but I wonder if you could give an actual working example - like a curl command that produces a basic/useless apk. I think that'd help me make the final leap to see how it works end-to-end. I tried my own curl command using the object given in the readme, but I get some error in html, so I suspect I'm missing something basic:

$ curl 'localhost:3000/generateSignedApk'   -X POST -H 'content-type: application/json'   --data '{"packageId":"com.mycompany.myapp","host":"https://contoso.com","name":"My App","themeColor":"#2f3d58","navigationColor":"#2f3d58","backgroundColor":"#2f3d58","startUrl":"/","iconUrl":"https://contoso.com/images/512x512.png","maskableIconUrl":"https://contoso.com/images/maskable512x512.png","appVersion":"1.0.0","useBrowserOnChromeOS":true,"splashScreenFadeOutDuration":300,"enableNotifications":false,"shortcuts":"[]","signingInfo":{"fullName":"John Doe","organization":"Contoso","organizationalUnit":"Engineering Department","countryCode":"US"}'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>SyntaxError: Unexpected end of JSON input<br> &nbsp; &nbsp;at JSON.parse (&lt;anonymous&gt;)<br> &nbsp; &nbsp;at parse (/app/node_modules/body-parser/lib/types/json.js:89:19)<br> &nbsp; &nbsp;at /app/node_modules/body-parser/lib/read.js:121:18<br> &nbsp; &nbsp;at invokeCallback (/app/node_modules/raw-body/index.js:224:16)<br> &nbsp; &nbsp;at done (/app/node_modules/raw-body/index.js:213:7)<br> &nbsp; &nbsp;at IncomingMessage.onEnd (/app/node_modules/raw-body/index.js:273:7)<br> &nbsp; &nbsp;at IncomingMessage.emit (events.js:203:15)<br> &nbsp; &nbsp;at endReadableNT (_stream_readable.js:1145:12)<br> &nbsp; &nbsp;at process._tickCallback (internal/process/next_tick.js:63:19)</pre>
</body>
</html>
ghost commented 4 years ago

Hello davidmaxwaterman, thank you for opening an issue with us!

I have automatically added a "needs triage" label to help get things started. Our team will investigate the issue and help solve it ASAP. Other community members may also look into the issue and provide feedback 🙌

andreban commented 4 years ago

This is the curl command that the browser sends to server when requesting to generate an APK

curl 'https://pwabuilder-cloudapk.azurewebsites.net/generateSignedApkZip' -H 'Connection: keep-alive' -H 'Sec-Fetch-Dest: empty' -H 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Mobile Safari/537.36' -H 'Content-Type: application/json' -H 'Accept: */*' -H 'Origin: https://www.pwabuilder.com' -H 'Sec-Fetch-Site: cross-site' -H 'Sec-Fetch-Mode: cors' -H 'Referer: https://www.pwabuilder.com/publish' -H 'Accept-Language: en-GB,en;q=0.9,pt-BR;q=0.8,pt;q=0.7,en-US;q=0.6' --data-binary '{"packageId":"com.pwadirectory.twa","host":"pwa-directory.appspot.com","name":"PwaDirectory","themeColor":"#7cc0ff","navigationColor":"#7cc0ff","backgroundColor":"#7cc0ff","startUrl":"/","iconUrl":"https://pwa-directory.appspot.com/favicons/android-chrome-192x192.png","maskableIconUrl":"https://pwa-directory.appspot.com/favicons/android-chrome-192x192.png","appVersion":"1.0.0","useBrowserOnChromeOS":true,"splashScreenFadeOutDuration":300,"enableNotifications":false,"shortcuts":"[]","signingInfo":{"fullName":"John Doe","organization":"Contoso","organizationalUnit":"Engineering Department","countryCode":"US"}}' --compressed
davidmaxwaterman commented 4 years ago

OK, thanks. So, to try that, I run the docker thingy (not sure of the terms yet), and then, in another terminal, run your curl...though, I presume I replace https://pwabuilder-cloudapk.azurewebsites.net/generateSignedApkZip with http://localhost:3000, right?

It seems to get further, but no joy.

Am I missing something basic here? Perhaps I should try some simple PWA, like https://airhorner.com/ ?

This is what I get:

09:41:47 ~$ curl 'http://localhost:3000/generateSignedApkZip' -H 'Connection: keep-alive' -H 'Sec-Fetch-Dest: empty' -H 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Mobile Safari/537.36' -H 'Content-Type: application/json' -H 'Accept: */*' -H 'Origin: https://www.pwabuilder.com' -H 'Sec-Fetch-Site: cross-site' -H 'Sec-Fetch-Mode: cors' -H 'Referer: https://www.pwabuilder.com/publish' -H 'Accept-Language: en-GB,en;q=0.9,pt-BR;q=0.8,pt;q=0.7,en-US;q=0.6' --data-binary '{"packageId":"com.pwadirectory.twa","host":"pwa-directory.appspot.com","name":"PwaDirectory","themeColor":"#7cc0ff","navigationColor":"#7cc0ff","backgroundColor":"#7cc0ff","startUrl":"/","iconUrl":"https://pwa-directory.appspot.com/favicons/android-chrome-192x192.png","maskableIconUrl":"https://pwa-directory.appspot.com/favicons/android-chrome-192x192.png","appVersion":"1.0.0","useBrowserOnChromeOS":true,"splashScreenFadeOutDuration":300,"enableNotifications":false,"shortcuts":"[]","signingInfo":{"fullName":"John Doe","organization":"Contoso","organizationalUnit":"Engineering Department","countryCode":"US"}}' --compressed
curl: (52) Empty reply from server
09:40:35 (master) ~/z/antenna/CloudAPK$ docker run -p 3000:80 --name cloudapk cloudapk-image

> cloudapk@1.0.0 start /app
> node server.js

App listening on port 80
Saving Config to: /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/twa-manifest.json
(node:25) ExperimentalWarning: The fs.promises API is experimental
twa-generator Generating Android Project files:
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/settings.gradle
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/gradle.properties
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/build.gradle
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/gradlew
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/gradlew.bat
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/gradle/wrapper/gradle-wrapper.jar
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/gradle/wrapper/gradle-wrapper.properties
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/app/src/main/res/values/styles.xml
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/app/src/main/res/xml/filepaths.xml
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/app/src/main/res/xml/shortcuts.xml
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/app/build.gradle
twa-generator    /tmp/pwabuilder-cloudapk-25Ar582SC3ba57/app/src/main/AndroidManifest.xml
(node:25) UnhandledPromiseRejectionWarning: TypeError: this.shortcuts.map is not a function
    at TwaManifest.generateShortcuts (/app/node_modules/@llama-pack/core/dist/lib/TwaManifest.js:203:37)
    at eval (lodash.templateSources[1]:28:11)
    at TwaGenerator.<anonymous> (/app/node_modules/@llama-pack/core/dist/lib/TwaGenerator.js:177:65)
    at step (/app/node_modules/@llama-pack/core/dist/lib/TwaGenerator.js:59:23)
    at Object.next (/app/node_modules/@llama-pack/core/dist/lib/TwaGenerator.js:40:53)
    at fulfilled (/app/node_modules/@llama-pack/core/dist/lib/TwaGenerator.js:31:58)
(node:25) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
andreban commented 4 years ago

On your input JSON, can you try replacing "shortcuts":"[]" with "shortcuts":[]?

Is your goal to create a REST service to generate packages?

davidmaxwaterman commented 4 years ago

Ah, that seems to spend some time doing something :) Oddly, it seems to 'die' on a fetch error:

curl: (52) Empty reply from server

Docker output:

Error generating signed APK { FetchError: request to https://pwa-directory.appspot.com/favicons/android-chrome-192x192.png failed, reason: read ECONNRESET
    at ClientRequest.<anonymous> (/app/node_modules/node-fetch/lib/index.js:1455:11)
    at ClientRequest.emit (events.js:198:13)
    at TLSSocket.socketErrorListener (_http_client.js:392:9)
    at TLSSocket.emit (events.js:198:13)
    at emitErrorNT (internal/streams/destroy.js:91:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:59:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)
  message:
   'request to https://pwa-directory.appspot.com/favicons/android-chrome-192x192.png failed, reason: read ECONNRESET',
  type: 'system',
  errno: 'ECONNRESET',
  code: 'ECONNRESET' }

I see it is the 'iconUrl'. I can load that URL in my browser just fine.

I wonder if my VPN is effective from within docker?

Aha, if I replace that image URL with one from the airhorner PWA, then I see much more output...and I see in 'glances' that there's some 'java' thing running, and the cloudapk container is consuming cpu....

Hrmph, but the curl command seems to return too early :/

Curl:

curl: (52) Empty reply from server

The docker output seems to be quite positive though:

Saving Config to: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/twa-manifest.json
twa-generator Generating Android Project files:
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/settings.gradle
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/gradle.properties
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/build.gradle
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/gradlew
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/gradlew.bat
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/gradle/wrapper/gradle-wrapper.jar
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/gradle/wrapper/gradle-wrapper.properties
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/values/styles.xml
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/xml/filepaths.xml
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/xml/shortcuts.xml
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/build.gradle
twa-generator    /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/AndroidManifest.xml
twa-generator    72x72 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-hdpi/ic_launcher.png
twa-generator    48x48 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-mdpi/ic_launcher.png
twa-generator    96x96 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-xhdpi/ic_launcher.png
twa-generator    144x144 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
twa-generator    192x192 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
twa-generator    450x450 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/drawable-hdpi/splash.png
twa-generator    300x300 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/drawable-mdpi/splash.png
twa-generator    600x600 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/drawable-xhdpi/splash.png
twa-generator    900x900 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/drawable-xxhdpi/splash.png
twa-generator    1200x1200 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/drawable-xxxhdpi/splash.png
twa-generator    512x512 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/store_icon.png
twa-generator    123x123 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-hdpi/ic_maskable.png
twa-generator    82x82 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-mdpi/ic_maskable.png
twa-generator    164x164 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-xhdpi/ic_maskable.png
twa-generator    246x246 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-xxhdpi/ic_maskable.png
twa-generator    328x328 Icon: /tmp/pwabuilder-cloudapk-25xNQ3Tu2dREfG/app/src/main/res/mipmap-xxxhdpi/ic_maskable.png
keytool Signing Key created successfully
Optimizing the APK...
Signing the APK...
Zipping signed APK and key info...
Process completed successfully.

I wonder how to make curl 'just wait longer'.

Is your goal to create a REST service to generate packages?

That sounds about right, yes. We have an 'admin' web site that generates PWAs based on our database content, and we also have an 'install' service that we use to install un-related (self-contained) apks onto our android devices (hundreds of them). We want to add a button to the admin web site that will generate an apk from the PWA, so we can use the install service to to install those apks, and effectively perform the equivalent of someone visiting the PWA on each device and adding it to the homescreen of the phone. It is impractical to have a person do this on hundreds of phones - out of the question.

davidmaxwaterman commented 4 years ago

OK, I added '-m 900 --output myapp.apk' and that seems to have produced an apk!

Oddly enough it seemed to work much more quickly (43 seconds, according to the curl progress output) than the previous attempts, so I doubt the '-m 900' was needed - perhaps some caching was happening.

Anyway, I think I can now start trying to get it working for one of our PWAs :)

Thanks for the help!

davidmaxwaterman commented 4 years ago

Hrm, working on it again this morning, I notice the same behaviour - ie the first time it doesn't work, but the second time it does - exact same curl command.

Any ideas?

davidmaxwaterman commented 4 years ago

Anyway, I unzipped the file that is downloaded, and installed the apk, and it works. Result.

...and it seems to work for my own PWA too.

However, I notice it isn't quite the same as when installed from the PWA in Chrome...eg it has all the browser 'chrome' and isn't full screen. Is there a way to influence that? My PWAs have "display": "fullscreen", in the manifest, so I tried adding that into the curl's json, but it didn't seem to make any difference.

I'll see if I can glean anything from the source code...

davidmaxwaterman commented 4 years ago

I just noticed the 'Next-steps.md', which seems to be instructions for getting the app onto the Google Play store - this is the first I've heard that this is an objective of this tool...but, in any case, I don't want to do that, since I'll install all my apps using my own tools. However, I wondered if the instructions contained any hints why my app is shown non-fullscreen. I tried generating the assetlinks.json, but the way our PWA is served means I need some changes to the server config to get to the path it needs to be in (ie, as per[1], I cannot access the equivalent of https://example.com/.well-known/assetlinks.json on my site). I wonder if that is needed even if you don't want anything to do with Google Play...it's not exactly clear, I find.

[1] https://developers.google.com/web/updates/2019/08/twas-quickstart#creating-your-asset-link-file

JudahGabriel commented 4 years ago

We updated PWABuilder to utilize the new llama-pack, which requires that shortcuts be an array, not a string.

With regards to showing your app full screen, you need assetlinks.json. That's why we included it in the next-steps.md.

Hope this helps.

andreban commented 4 years ago

@davidmaxwaterman The assetlinks.json file is used, along with the asset_statements section in AndroidManifest.xml to prove that the developer controls both the Android App and the Web App. This is required to show the application in fullscreen. There are more details here: https://developers.google.com/web/updates/2019/02/using-twa#remove-url-bar

davidmaxwaterman commented 4 years ago

OK, thanks guys. That makes my next step clearer - need to get some b/e work done, so that the assetlinks.json file gets served properly. Unfortunately, that's not my domain, so it'll take a lot longer to get sorted out :(