Compare commits

...
Sign in to create a new pull request.

20 commits

Author SHA1 Message Date
4689bb5144 keep track of ideas 2024-12-02 08:42:14 +01:00
c57affd486 make Makefile.apps generation outsourced bash script 2024-12-02 08:41:51 +01:00
d8d3ffa647 fix .gitignore 2024-12-01 08:32:32 +01:00
e3324e374b commit to new branch for android emulator 2024-12-01 08:25:10 +01:00
631056030e remove incorrect empty file 2024-11-01 06:38:39 +01:00
20a426e229 improve Makefile 2024-11-01 06:12:24 +01:00
c08394e44c modify webviews AndroidManifest prevent app restart on rotation 2024-10-31 05:54:01 +01:00
ec9073c681 test to only require permission if needed 2024-10-30 09:30:22 +01:00
f9044c39c2 index.html: add meta-viewport; add input field 2024-10-30 09:30:00 +01:00
ecbbc8dfa2 improve Makefile dependencies; add run 2024-10-30 09:28:36 +01:00
a8b47f8bdf add to .gitignore 2024-10-11 08:43:16 +02:00
0c367132b4 add .gitkeep to webview 2024-10-10 14:11:40 +02:00
df09b2dfcb move apk->apk/webview 2024-10-10 14:02:51 +02:00
6327c380ba prepare Makefie and stuff for apk/{webview,native} 2024-10-10 13:59:02 +02:00
3a9ac9f73f improve Makefiles 2024-10-08 11:38:09 +02:00
94cc0ca1fa fix typo in README.md 2024-10-08 11:38:09 +02:00
fb5409ae83 make example simpler, remove uneeded code 2024-10-08 11:38:09 +02:00
98eef38c7e do not require a res.xml file 2024-10-08 11:38:09 +02:00
17696caf99 make minimal example cleaner 2024-10-08 11:38:09 +02:00
1d7c281a7a update README.md for minimal example app 2024-10-08 09:51:46 +02:00
27 changed files with 558 additions and 214 deletions

View file

@ -1 +0,0 @@

8
.gitignore vendored
View file

@ -1,5 +1,13 @@
*.apk
*.apk.install-log
apk/ToyKey.keystore apk/ToyKey.keystore
apk/obj apk/obj
apk/obj/* apk/obj/*
apk/bin/* apk/bin/*
apk/result
apk/result/
apk/result/*
apk/bin/!.gitkeep apk/bin/!.gitkeep
/example.app.apk
docker-compose-build.log
/Makefile.apps

View file

@ -1,28 +1,42 @@
FROM archlinux:latest FROM archlinux:latest
ARG YESACCEPT=n
RUN echo "this is $YESACCEPT"
RUN pacman -Syu --noconfirm RUN pacman -Syu --noconfirm
RUN pacman -S --noconfirm coreutils bash shadow make unzip jdk11-openjdk wget RUN pacman -S --noconfirm coreutils bash shadow make unzip zip jdk17-openjdk wget
ENV ANDROID_SDK_ROOT="/opt/android" ENV ANDROID_SDK_ROOT="/opt/android"
ENV BUILD_TOOLS_LATEST="$ANDROID_SDK_ROOT/cmdline-tools/latest" ENV BUILD_TOOLS_LATEST="$ANDROID_SDK_ROOT/cmdline-tools/latest"
RUN BUILD_TOOLS="$(realpath -m "$BUILD_TOOLS_LATEST/..")";\ RUN BUILD_TOOLS="$(realpath -m "$BUILD_TOOLS_LATEST/..")";\
mkdir -p "$BUILD_TOOLS";\ mkdir -p "$BUILD_TOOLS";\
cd "$BUILD_TOOLS";\ cd "$BUILD_TOOLS";\
pwd;\ pwd;\
wget -O cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip;\ for LATESTTOOLS in \
unzip cmdline-tools.zip;\ "$(curl https://developer.android.com/studio#command-line-tools-only | grep -e 'https://dl.google.com/android/repository/commandlinetools-linux-.*_latest.zip' | cut -f2 -d'"')" \
'https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip' ;\
do \
echo "testing to get '$LATESTTOOLS'";\
echo test "${LATESTTOOLS:0:22}" = "https://dl.google.com/" -a "${LATESTTOOLS:(-11)}" = "_latest.zip" ;\
test "${LATESTTOOLS:0:22}" = "https://dl.google.com/" -a "${LATESTTOOLS:(-11)}" = "_latest.zip" || { echo "unsure of URL $LATESTTOOLS correct, skipping it..." >&2; continue; }; \
wget -O cmdline-tools.zip "$LATESTTOOLS" ;\
unzip cmdline-tools.zip && break || { echo "error downloading working cmdline-tools.zip"; exit 1; };\
done;\
ls ;\ ls ;\
ls cmdline-tools;\ ls cmdline-tools;\
rm cmdline-tools.zip;\ rm cmdline-tools.zip;\
mv -v cmdline-tools "$BUILD_TOOLS_LATEST" || true mv -v cmdline-tools "$BUILD_TOOLS_LATEST" || true
ENV PATH="$PATH:$BUILD_TOOLS_LATEST/bin" ENV PATH="$PATH:$BUILD_TOOLS_LATEST/bin"
ENV JAVA_HOME="/usr/lib/jvm/java-11-openjdk/" #TODO make this automatic
#ENTRYPOINT bash -c 'sleep 10000' ENV JAVA_HOME="/usr/lib/jvm/java-17-openjdk/"
ENV LIBRARY_PATH="$LIBRARY_PATH:$BUILD_TOOLS_LATEST/lib" ENV LIBRARY_PATH="$LIBRARY_PATH:$BUILD_TOOLS_LATEST/lib"
ARG YESACCEPT=n RUN test "$YESACCEPT" = "y" || { printf "\033[31;1;4m%s\n%s\033[0m " "FAILED TO BUILD CONTAINER: You did not ACCEPT THE UPSTREAM LICENSE" " -> export YESACCEPT=y" >&2; exit 1; }
RUN echo you selected to accept the licenses/TOS RUN echo you selected to accept the licenses/TOS
RUN echo "$YESACCEPT" | sdkmanager --install "build-tools;33.0.2" RUN echo "$YESACCEPT" | sdkmanager --install "build-tools;33.0.2"
RUN echo "$YESACCEPT" | sdkmanager --install "platforms;android-33" RUN echo "$YESACCEPT" | sdkmanager --install "platforms;android-33"
#RUN apk add setpriv RUN echo "$YESACCEPT" | sdkmanager --install "ndk;28.0.12433566"
#RUN echo "$YESACCEPT" | sdkmanager --install "system-images;android-33;aosp_atd;x86_64"
#RUN echo "$YESACCEPT" | sdkmanager --install "emulator"
#RUN echo "no" | avdmanager --verbose create avd --force --name "thedevice" --package 'system-images;android-33;aosp_atd;x86_64' --tag "aosp_atd" --abi "x86_64"
#RUN echo "$YESACCEPT" | sdkmanager --install "platform-tools"
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chown 0:0 /entrypoint.sh RUN chown 0:0 /entrypoint.sh
RUN chmod 0700 /entrypoint.sh RUN chmod 0700 /entrypoint.sh

View file

@ -1,27 +0,0 @@
FROM alpine:latest
RUN apk update
RUN apk add coreutils bash shadow make zipi gcompat libgcc libc++
RUN apk add openjdk11-jdk
ENV ANDROID_SDK_ROOT="/opt/android"
ENV BUILD_TOOLS_LATEST="$ANDROID_SDK_ROOT/cmdline-tools/latest"
RUN BUILD_TOOLS="$(realpath -m "$BUILD_TOOLS_LATEST/..")";\
mkdir -p "$BUILD_TOOLS";\
cd "$BUILD_TOOLS";\
pwd;\
wget -O cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip;\
unzip cmdline-tools.zip;\
ls ;\
ls cmdline-tools;\
rm cmdline-tools.zip;\
mv -v cmdline-tools "$BUILD_TOOLS_LATEST" || true
ENV PATH="$PATH:$BUILD_TOOLS_LATEST/bin"
ENV LIBRARY_PATH="$LIBRARY_PATH:$BUILD_TOOLS_LATEST/lib"
RUN yes | sdkmanager --install "build-tools;33.0.2"
RUN yes | sdkmanager --install "platforms;android-33"
RUN apk add setpriv
COPY entrypoint.sh /entrypoint.sh
RUN chown 0:0 /entrypoint.sh
RUN chmod 0700 /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

43
IDEAS.TODOS.md Normal file
View file

@ -0,0 +1,43 @@
# Ideas and todos
## 1. EMULATOR: enable a way to have the "sdk emulator" running
this is attempted in the `emulator` branch. Challenges comprise:
* Xorg server must be forwarded (and other stuff like sound etc)
* the emulator itself might need to have `docker .... --device` setup for `/dev/kvm` probably (and at that point it might be
better to have it all run natively inside a VM anyways... (alternative would be nested virutalization)
## 2. BUILD and PACKAGENAME improvements
### status quo: 2 Makefiles (gnu make) are involved,
* a) one being the outer one that uses Makefile.apps as an include and then
* b) an inner one located at `./apps/<app-name>/Makefile`
the inner-one is not "parametrized" and at present has explicit nameing of the android app/ package (i.e. `example.app`),
which comes with the additional negative that one has several files that need to be adjusted, this also being true
because of the "fun that JAVA" naming conventions which basically means that the very least those files all have to
be kept in sync
* `./apps/<app-name>/AndroidManifest.xml`
* `./apps/<app-name>/Makefile`
* `./apps/<app-name>/src/<package-name>/<activity-name>.java`
### goal: attempt to have the `<app-name>`, `<package-name>` and `<package-name>` be abstracted
Ideally make the inner Makefile superfluous
## 3. ALLOW KOTLIN
Since Java was apparently not "good/fancy" enough of maybe "bloated" the hippster did do what they like best
re-invent to garnish personal "pride/achievement" and thus make a unhelpful thing (java) into now 2 unhelpful
things (i.e. Kotlin and Java) super!
However oftentimes now the undesired-yet-existing-kotlin is there and a goal could be to enable it being used.
After all it appears to be a tiny bit less of a boilerplate annoyance...
## 4. ALLOW GRADLEBUILDS
GRADLE is often used as a building system for apps, it would be beneficial to have those build run also
in this `android-docker-app` thing

View file

@ -1,24 +1,18 @@
# this includes explicit rules
include Makefile.apps
Makefile.apps: Makefile apps
@./Makefile.make.Makefile.apps.sh
all: build install apps:
mkdir "$@"
build: install: $(shell for APP in apps/*/; do test -d "$$APP" || continue; echo "$$APP""app.apk.install-log"; done)
docker-compose run compile force-install reinstall:
for APP in apps/*/; do rm -f "$$APP""app.apk.install-log" 2>/dev/null; done; $(MAKE) install
run: $(shell cd apps/; for APP in */; do test -d "$$APP" || continue; echo "run-$${APP%/}"; done)
install: docker-compose-build.log: Dockerfile compose.yml
adb install -r ./apk/bin/app1.apk
clean:
rm docker-compose-build.log || true
docker-compose down --remove-orphans --rmi all docker-compose down --remove-orphans --rmi all
BUILDKIT_PROGRESS=plain docker-compose build | tee docker-compose-build.log
apk: apk/app.apk
true
apk/app.apk: docker-compose-build.log
docker-compose run compile
docker-compose-build.log: Dockerfile docker-compose.yml
docker-compose down --remove-orphans --rmi all
BUILDKIT_PROGRESS=plain docker-compose build --no-cache | tee docker-compose-build.log

22
Makefile.make.Makefile.apps.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/bash
(
set -xe
cd apps
for APP in */
do
test -d "$APP" || continue;
APP="${APP%/}";
cat << MAKEFILE
apps/$APP/app.apk: docker-compose-build.log
docker compose run --rm compile $APP
build-$APP: apps/$APP/app.apk
true
apps/$APP/app.apk.install-log: apps/$APP/app.apk
adb install -r $< > \$@ || rm \$@
install-$APP: apps/$APP/app.apk.install-log
true
run-$APP: apps/$APP/app.apk.install-log
adb shell am start -n app.example/.ExampleApp
MAKEFILE
done
) > Makefile.apps

View file

@ -1,8 +1,42 @@
# Repo allowing -in a KISS way- to create a containered way to build an Android App APK # Repo allowing -in a KISS way- to create a containered way to build an Android App APK
## brach `no-res.xml`
this branch features the exmaple.app to not require the compoletely bogus unneeded requirement
to setup a `./apk/res/layouts/res.xml` file to setup the layout to be used.
Instead a layout is created inline
## tl;dr ## tl;dr
to build an application to `make` minimal exmaple.app via the docker-compose:
```
# clone repo
git clone https://git.alexmahr.de/lion/android-app-docker
# enter local copy
cd android-app-docker
# checkout the branch for minmal-example-app
git checkout minimal-example-app
# set env variable
export YESACCEPT=y #to acceccpt the license agreement terms (it is your responsibility to read it)
# build app (this involves first building the docker compose setup and container used to build the apk)
make build
```
This will generate the APK file `./apk/example.app.apk`
this can be installed via `adb`
```
adb install -r ./apk/example.app.apk
# or alternative type
make install
```
which of course would require your mobile device to be connected and setup for USB-debugging.
## a bit longer
build an application
0. build the image _*_ 0. build the image _*_
```sh ```sh

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.alexmahr.app1"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="30"
android:targetSdkVersion="33"/>
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
<!-- android:maxSdkVersion="integer" /> -->
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<activity android:name="de.alexmahr.app1.App1"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -1,58 +0,0 @@
#JAVA_HONE = /usr/lib/jvm/java-8-openjdk
ANDROID_HOME = /opt/android
ANDROID_VERSION = 33.0.2
PLATFORM = android-$(shell echo $(ANDROID_VERSION) | sed 's/\..*//')
TOOLCHAIN = /Users/amon/grive/development/Android/NativeToolchain
GCC = $(TOOLCHAIN)/bin/aarch64-linux-android-gcc
CXX_FLAGS = -march=armv8-a --sysroot=$(TOOLCHAIN)/sysroot
all: build
.PHONY : build
.PHONY : deploy
.PHONY : clean
build : ./bin/app1.apk
./bin/app1.apk : ./bin/signed.apk
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/zipalign -v -f 4 $< $@
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/apksigner sign --ks ToyKey.keystore --key-pass pass:armena --ks-pass pass:armena $@
./bin/signed.apk : ./bin/unsigned.apk ./ToyKey.keystore
#$(JAVA_HOME)/bin/jarsigner -verbose -keystore ./ToyKey.keystore -storepass armena -keypass armena -signedjar $@ $< helljniKey
jarsigner -verbose -keystore ./ToyKey.keystore -storepass armena -keypass armena -signedjar $@ $< helljniKey
./bin/unsigned.apk : ./bin/classes.dex
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/aapt package -v -u -f -M ./AndroidManifest.xml -S ./res \
-I $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar -F $@ ./bin
# $(ANDROID_HOME)/build-tools/23.0.3/aapt add $@ lib/arm64-v8a/libhello.so
./bin/classes.dex : ./obj/de/alexmahr/app1/App1.class
#$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/d8 --dex --verbose --output=$@ ./obj
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/d8 $(shell find obj -name '*.class') --lib $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar --output bin
#./lib/arm64-v8a/libhello.so : ./jni/hello.o
# $(GCC) -shared $< -o $@
#./jni/%.o : ./jni/%.c
# $(GCC) $(CXX_FLAGS) -fPIC -c $< -o $@
./src/de/alexmahr/app1/R.java : $(shell find ./res -type f)
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/aapt package -v -f -m -S ./res -J ./src -M ./AndroidManifest.xml \
-I $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar
./obj/de/alexmahr/app1/App1.class : ./src/de/alexmahr/app1/App1.java ./src/de/alexmahr/app1/R.java
#$(JAVA_HOME)/bin/javac -source 7 -target 7 -d ./obj -classpath $(ANDROID_HOME)/platforms/$(PLATFORMgg)/android.jar -sourcepath ./src $<
#javac -source 7 -target 7 -d ./obj -classpath $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar -sourcepath ./src $<
javac -d ./obj -classpath $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar -sourcepath ./src $<
./ToyKey.keystore :
#$(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=some company,O=Android,C=JPN" -keystore $@
keytool -genkeypair -validity 1000 -dname "CN=alexander,O=Android,C=JPN" -keystore $@ \
-storepass armena -keypass armena -alias helljniKey -keyalg RSA -v
clean:
rm -f ./bin/* ./lib/arm64-v8a/*

View file

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="aa"
/>
</LinearLayout>

View file

@ -1,37 +0,0 @@
package de.alexmahr.app1;
import android.util.Log;
import android.widget.TextView;
import android.app.Activity;
import android.os.Bundle;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;
import android.text.method.ScrollingMovementMethod;
public class App1 extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
try
{
super.onCreate(savedInstanceState);
//volatile String result = "initial";
String result = "initial";
TextView tv = new TextView(this);
tv.setMovementMethod(new ScrollingMovementMethod());
tv.setText( result);
setContentView(tv);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,22 +0,0 @@
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package de.alexmahr.app1;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040000;
}
}

9
apps/webview/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
/ToyKey.keystore
/obj
/obj/*
/bin/*
!/bin/.gitkeep
/result
/result/
/result/*
/example.app.apk

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.example"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="30"
android:targetSdkVersion="33"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
<uses-permission android:name="android.permission.MANAGE_MEDIA"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<activity android:name="app.example.ExampleApp"
android:exported="true"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

53
apps/webview/Makefile Normal file
View file

@ -0,0 +1,53 @@
#JAVA_HONE = /usr/lib/jvm/java-8-openjdk
ANDROID_HOME = /opt/android
ANDROID_VERSION = 33.0.2
PLATFORM = android-$(shell echo $(ANDROID_VERSION) | sed 's/\..*//')
TOOLCHAIN = /Users/amon/grive/development/Android/NativeToolchain
GCC = $(TOOLCHAIN)/bin/aarch64-linux-android-gcc
CXX_FLAGS = -march=armv8-a --sysroot=$(TOOLCHAIN)/sysroot
all: build
.PHONY : build
.PHONY : deploy
.PHONY : clean
build : ./result/example.app.apk
mv ./result/example.app.apk app.apk
true
env:
set > set ; env > env
./result/example.app.apk : ./result/signed.apk
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/zipalign -v -f 4 $< $@
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/apksigner sign --ks ToyKey.keystore --key-pass pass:armena --ks-pass pass:armena $@
./result/signed.apk : ./result/unsigned.apk ./ToyKey.keystore ./result
jarsigner -verbose -keystore ./ToyKey.keystore -storepass armena -keypass armena -signedjar $@ $< helljniKey
./result:
mkdir -p "$@"
./result/unsigned.apk : ./bin/classes.dex ./result
rm -rvf "$@"
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/aapt package -v -u -f -M ./AndroidManifest.xml -S ./res \
-I $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar -A ./assets -F $@ ./bin
./bin/classes.dex : ./obj/app/example/ExampleApp.class ./obj/app/example/ExampleApp$$1.class
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/d8 ./obj/app/example/*.class --lib $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar --output bin
./src/app/example/R.java : $(shell find ./res -type f)
$(ANDROID_HOME)/build-tools/$(ANDROID_VERSION)/aapt package -v -f -m -S ./res -J ./src -M ./AndroidManifest.xml \
-I $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar
./obj/app/example/ExampleApp.class : ./src/app/example/ExampleApp.java ./src/app/example/R.java
javac -d ./obj -classpath $(ANDROID_HOME)/platforms/$(PLATFORM)/android.jar -sourcepath ./src $<
./ToyKey.keystore :
keytool -genkeypair -validity 1000 -dname "CN=alexander,O=Android,C=JPN" -keystore $@ \
-storepass armena -keypass armena -alias helljniKey -keyalg RSA -v
clean:
rm -f ./bin/* ./lib/arm64-v8a/* ./result/*

View file

@ -0,0 +1,111 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="width=device-width,initial-scale=1.0" name="viewport">
</head>
<script>
function sleepit(ms) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, ms);
});
}
function divMessage(html){
var div = document.createElement('div');
div.innerHTML=html;
document.body.appendChild(div);
div.scrollIntoView({ behavior: 'smooth'});
}
(async function webviewprogram(){
var port;
var resolves = [];
function setupMessage(){
return new Promise((resolve) => {
window.addEventListener('message',(e)=>{
if(e.data=="init-from-java" && ! port){
divMessage('INIT');
port = e.ports[0];
port.onmessage = function (ee) {
divMessage('AAport.onmessage='+ee.data);
var response = JSON.parse(ee.data);
divMessage('response.resolveIndex'+response.resolveIndex);//AAport.onmessage='+ee.data);
var localresolve = resolves[response.resolveIndex];
divMessage(typeof localresolve)
localresolve(response);
}
resolve()
}
divMessage('message='+e.data);
},true);
});
}
function doLs(path){
divMessage('doLs');
return new Promise((resolve) => {
divMessage('resolves.length'+resolves.length);
resolves.push(resolve);
divMessage('after push resolves.length'+resolves.length);
port.postMessage('{"function":"ls","path":"'+path+'","resolveIndex":"'+(resolves.length-1)+'"}');
});
}
function doCat(file){
return new Promise((resolve) => {
resolves.push(resolve);
port.postMessage('{"function":"cat","resolveIndex":"'+resolves.length+'"}');
});
}
window.addEventListener('load',()=>{
divMessage('JAVASCRIPT WORKS');
},false);
// await sleepit(2000);
// divMessage('awaited 2000');
// await sleepit(2000);
// divMessage('awaited again 2000');
await setupMessage();
divMessage('setup');
async function mapDoLs(path){
var reply = await doLs(path);
divMessage("reply.result.length"+reply.result.length);
reply.result.forEach((file)=>{
divMessage(file.name);//reply.result.length"+reply.result.length);
//var button
button = document.createElement("button");
button.addEventListener("click",()=>{
if(file.isDirectory)
mapDoLs(path+"/"+file.name);
},false);
//if(file.isDirectory){
//{
// button = document.createElement("button");
// button.addEventListener("click",()=>{
// mapDoLs(path+"/"+file.name);
// },false);
//} else {
// button = document.createElement("div");
//}
////a.href="#";
//////a.textContent="asdasdada"
button.textContent=file.name+ " " + file.size;
document.body.appendChild(button);
});
}
await mapDoLs("/DCIM");
divMessage("done");
// for(let file of DCIM.result){
// divMessage("filename");
//});
})();
</script>
<a href='https://html5test.co/'>https://html5test.co/</a>
<h1> this is html <h1>
<h2> this is a h2</h2>
<input type=text />
<img src='https://wald.alexmahr.de/images/bear.avif'>
<img src='https://wald.alexmahr.de/images/delphin.avif'>
</html>

View file

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">App1</string> <string name="app_name">Example App</string>
</resources> </resources>

View file

@ -0,0 +1 @@
R.java

View file

@ -0,0 +1,179 @@
package app.example;
import android.provider.Settings ;
import android.content.Intent;
import android.util.Log;
import android.util.Base64;
import java.util.Objects;
import android.app.Activity;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.text.method.ScrollingMovementMethod;
import android.view.*;
//import android.view.MenuItem;
import android.view.ViewGroup.*;
import android.widget.*;
//import android.widget.Toast;
//import android.widget.TextView;
import android.webkit.*;
import android.net.Uri;
import org.json.JSONObject;
//import android.webkit.WebView;
//import android.webkit.WebMessage;
//import android.webkit.WebMessagePort;
import java.io.InputStream;
import java.io.File;
public class ExampleApp extends Activity {
public JSONObject doLs(JSONObject message){
try{
JSONObject file;
File directory = new File(Environment.getExternalStorageDirectory().toString() + message.getString("path"));
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++)
{
try{
//Log.d("ALEXINFO","filename"+files[i].getName());
file = new JSONObject();
file.put("name",files[i].getName());
file.put("isFile",files[i].isFile());
file.put("isDirectory",files[i].isDirectory());
file.put("size",files[i].length());
message.accumulate("result",file);
} catch (Exception e) {
Log.d("Exception EX1","ex1");
}
}
} catch (Exception e) {
Log.d("Exception EX2","ex2");
}
return message;
}
public JSONObject doCat(JSONObject message){
try{
File directory = new File(Environment.getExternalStorageDirectory().toString() + message.getString("path"));
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++)
{
try{
message.accumulate("result",files[i].getName());
} catch (Exception e) {}
}
} catch (Exception e) {}
return message;
}
final static int APP_STORAGE_ACCESS_REQUEST_CODE = 501; // Any value
private static final String BASE_URI = "https://alexmahr.de";
private WebMessagePort port;
private void initPort(WebView myWebView) {
final WebMessagePort[] channel=myWebView.createWebMessageChannel();
port=channel[0];
port.setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
@Override
public void onMessage(WebMessagePort porte, WebMessage message) {
try{
JSONObject messageJSON = new JSONObject(message.getData());
JSONObject reply;
if(Objects.equals(messageJSON.getString("function"),"ls"))
{
reply = doLs(messageJSON);
reply.put("super","man");
port.postMessage(new WebMessage(reply.toString()));
return;
}
else if(Objects.equals(messageJSON.getString("function"),"cat"))
{
reply = doCat(messageJSON);
reply.put("super","cat");
port.postMessage(new WebMessage(reply.toString()));
return;
}
else {
reply=messageJSON;
reply.put("super","else");
reply.accumulate("result",1);
reply.accumulate("result","something");
port.postMessage(new WebMessage(reply.toString()));
}
} catch( Exception e) { }
}
});
myWebView.postWebMessage(new WebMessage("init-from-java", new WebMessagePort[]{channel[1]}),Uri.parse(BASE_URI));
}
public String readFileFromAssets(String filename) {
String filecontents = "";
try {
InputStream stream = getAssets().open(filename);
int filesize = stream.available();
byte[] filebuffer = new byte[filesize];
stream.read(filebuffer);
stream.close();
filecontents = new String(filebuffer);
} catch (Exception e) {
// I <3 java exceptions
}
return filecontents;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// this removes the title bar (a ~1cm big strip at the top of the app showing its name
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
// we create the webview (at least on the android 14 that is a webview that features avif + websockets etc....)
WebView myWebView = new WebView(this);//activityContext);
// MyJavascriptInterface myJavaScriptInterface = new MyJavascriptInterface(this,myWebView);
// we create a webview (there is also setwebviewclient-vs-setwebchromeclient
WebViewClient myWebViewClient= new WebViewClient();
myWebView.setWebViewClient(myWebViewClient);
// to setup settings
WebSettings myWebSettings = myWebView.getSettings();
myWebSettings.setBuiltInZoomControls(true);
myWebSettings.setDisplayZoomControls(false);
myWebSettings.setJavaScriptEnabled(true);
myWebView.addJavascriptInterface(this, "myJavaScriptInterface");
// load the html from assets file
String html = readFileFromAssets("index.html");
myWebView.loadDataWithBaseURL(BASE_URI,html, "text/html", "UTF-8",null);
//myWebView.loadData(encodedHtml, "text/html", "base64");
// alternatively this could be to load a website
//myWebView.loadUrl("https://alexmahr.de/ru");
setContentView(myWebView);
new CountDownTimer(500, 100) {
public void onTick(long millisUntilFinished) {
try{
JSONObject MyJSONObject = new JSONObject("{\"json\":[1,2,3],\"something\":\"test\"}");
myWebView.postWebMessage(new WebMessage("this is the message"+millisUntilFinished+ " " + MyJSONObject.get("something")),Uri.parse(BASE_URI));
} catch( Exception e) { }
// mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
// myWebView.evaluateJavascript("document.body.innerHTML='SUP "+millisUntilFinished+" all is lost';",null);
}
public void onFinish() {
initPort(myWebView);//myWebView.evaluateJavascript("document.body.innerHTML='all is lost';",null);
}
}.start();
// Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, Uri.parse("package: app.example"));// + BuildConfig.APPLICATION_ID));
if(!Environment.isExternalStorageManager()){
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", this.getPackageName(), null);
intent.setData(uri);
startActivity(intent);
} else {
Toast.makeText(this, "berechtigt", Toast.LENGTH_SHORT).show();
}
//intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
// startActivityForResult(intent, APP_STORAGE_ACCESS_REQUEST_CODE);
}
@JavascriptInterface
public String toString() {
// this.webview.evaluateJavascript("(setTimeout(()=>{document.body.innerHTML='all gone';},2000)()",null);
return "this is good";
}
}

View file

@ -1,9 +1,16 @@
services: services:
compile: compile:
hostname: thinkbox
build: build:
context: . context: .
args: args:
YESACCEPT: ${YESACCEPT} YESACCEPT: ${YESACCEPT:-}
stop_grace_period: 1s stop_grace_period: 1s
environment:
XAUTHORITY: /root/.Xauthority
DISPLAY: ":0"
volumes: volumes:
- ./apk:/apk - ./apps:/apps
- /home/alex/.Xauthority:/root/.Xauthority:ro

View file

@ -1,8 +1,18 @@
#!/bin/sh #!/bin/sh
cd /apk; set -x
VOLUID="$(stat -c "%u" /apk)";
VOLGID="$(stat -c "%g" /apk)";
CMD="${*:-"make"}" APKDIR="/apps/$1"
setpriv --reuid $VOLUID --regid $VOLGID --clear-groups sh -c "$CMD" shift
#test -d "$1" ==
#APKDIR=
#cd /apk/$1;
#shift
VOLUID="$(stat -c "%u" "$APKDIR")";
VOLGID="$(stat -c "%g" "$APKDIR")";
#CMD="${*:-"make"}"
cd "$APKDIR"
test -z "$*" && set -- make
setpriv --reuid $VOLUID --regid $VOLGID --clear-groups sh -c "$*"