Compare commits
10 commits
03c0ec74c0
...
94b5d2aeca
Author | SHA1 | Date | |
---|---|---|---|
94b5d2aeca | |||
eef403f0cb | |||
cdd0ac3531 | |||
38f4fa9774 | |||
81f421b9c0 | |||
2787aee01a | |||
f83bf386cd | |||
b8dbee59ad | |||
be0786db68 | |||
444b92d1e7 |
9 changed files with 774 additions and 143 deletions
84
README.md
84
README.md
|
@ -1,7 +1,6 @@
|
|||
# Android App (build via docker/podman)
|
||||
# Calender App
|
||||
|
||||
The goal of this repo, is to create a container that can serve to produce an "empty android Application" (i.e `app.apk` file).
|
||||
As such the philosophy is to keep the process "simple" as to now make the understanding too difficult.
|
||||
simple calender app
|
||||
|
||||
## usage
|
||||
|
||||
|
@ -12,82 +11,7 @@ As such the philosophy is to keep the process "simple" as to now make the unders
|
|||
./build-android-app.sh
|
||||
```
|
||||
|
||||
2. follow the configuration
|
||||
3. upon success the apk file created is in app/result/app.apk and can be installed via `adb`
|
||||
```
|
||||
adb install -r app/result/app.apk
|
||||
```
|
||||
|
||||
## basic ideas
|
||||
## Screenshots
|
||||
|
||||
* work within container (debian based image)
|
||||
* use Makefile as a build tool
|
||||
|
||||
## benefits of this (compared to AndroidStudio)
|
||||
|
||||
* no need to install rather bloated hell of software (i.e AndroidStudio) and all
|
||||
* less hidding of internals (i.e Makefile allows to see how app.apk is made)
|
||||
* small app.apk file
|
||||
* oftentimes faster compile time (as compared with AndroidStudio Gradle builds)
|
||||
* quick "webview" which can serve as starting point for people that can to PWA and websites
|
||||
* Assisted initial configuration provides access to configure almost all types of [Android app permissions](app/.Makefile.scripts/make--app-config.sh#L127)
|
||||
* no need to have Kotlin, Gradle setup
|
||||
/
|
||||
## basic info
|
||||
|
||||
This repo should allow to generate an empty "android app". by simply cloning this repo and
|
||||
```
|
||||
./build-android-app.sh
|
||||
```
|
||||
It does so via:
|
||||
|
||||
1. building a container (in any of the runtime/daemons it finds: i.e. docker,podman,etc..)
|
||||
2. running this container having the `./app` folder being mounted within as `/app`
|
||||
3. executing the [`app/Makefile`](app/Makefile) which will then:
|
||||
4. either work with the configuration stored in an `app/app-config.sh` in case such file exists or
|
||||
5. if not go through a `whiptail` text menu wizzard to configure a new empty app. (Makefile recipe: `./app-config.sh`)
|
||||
6. it will then download the required android sdk files as necessary (Makefile recipe: `./android-sdk/installed`)
|
||||
7. go through the further steps to setup the blank app.
|
||||
|
||||
|
||||
|
||||
## files and purpose
|
||||
|
||||
Upon `clone` of this repo the `app` folder is setup with these files:
|
||||
```
|
||||
# The (GNU) Makefile which...
|
||||
app/Makefile
|
||||
# ... has recipes that call scripts in folder....
|
||||
app/.Makefile.scripts
|
||||
# .. which creates an `app-config.sh`, a file to keep
|
||||
# the configuration (app name,lable,api-levels,permissions etc)...
|
||||
app/.Makefile.scripts/make--app-config.sh
|
||||
# .. which creates an `AndroidManifest.xml`
|
||||
app/.Makefile.scripts/make--AndroidManifest.xml
|
||||
# .. which creates an `AppActivity.java` file (which just setup a Webview and loads `assets/index.html`)
|
||||
app/.Makefile.scripts/make--AppActivity.java.sh
|
||||
# .. which installs the necessary Android SDK in the correct versions
|
||||
app/.Makefile.scripts/make--android-sdk.sh
|
||||
app/assets
|
||||
# the index.html file
|
||||
app/assets/index.html
|
||||
app/res
|
||||
app/res/drawable
|
||||
# the icon of the app
|
||||
app/res/drawable/appicon.xml
|
||||
```
|
||||
|
||||
Upon further `./build-android-app.sh` execution more folders will appear
|
||||
```
|
||||
# a folder in which the Android-sdk stuff (installed via sdkmanager) is stored
|
||||
android-sdk
|
||||
# folders used during build...
|
||||
# ... for temporary files
|
||||
bin/
|
||||
obj/
|
||||
result/
|
||||
# app configuration resulting from text whiptail menu
|
||||
app-config.sh
|
||||
# the Manifest file as resulted from data from app-config.sh
|
||||
AndroidManifest.xml
|
||||
```
|
||||

|
||||
|
|
|
@ -8,31 +8,21 @@ test -f app-config.sh && {
|
|||
echo "package $APP_PACKAGE;"
|
||||
|
||||
cat << 'APPACTIVITYJAVA'
|
||||
|
||||
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.view.*;
|
||||
// for WebView,WebMessage,WebMessagePort,
|
||||
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;
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
|
||||
|
@ -70,6 +60,9 @@ public class AppActivity extends Activity {
|
|||
myWebSettings.setBuiltInZoomControls(true);
|
||||
myWebSettings.setDisplayZoomControls(false);
|
||||
myWebSettings.setJavaScriptEnabled(true);
|
||||
myWebSettings.setDomStorageEnabled(true);
|
||||
myWebSettings.setDatabaseEnabled(true);
|
||||
myWebSettings.setDatabasePath("/data/data/" + myWebView.getContext().getPackageName() + "/databases/");
|
||||
myWebView.addJavascriptInterface(this, "myJavaScriptInterface");
|
||||
// load the html from assets file
|
||||
String html = readFileFromAssets("index.html");
|
||||
|
@ -85,4 +78,3 @@ public class AppActivity extends Activity {
|
|||
return "this is good";
|
||||
}
|
||||
}
|
||||
APPACTIVITYJAVA
|
||||
|
|
15
app/Makefile
15
app/Makefile
|
@ -47,23 +47,28 @@ include Makefile.app-config
|
|||
|
||||
|
||||
# make the resources "R.java" thing
|
||||
./src/PACKAGE/R.java : $(shell find ./res -type f) app-config.sh ./AndroidManifest.xml ./android-sdk/installed
|
||||
./src/$(PACKAGE)/R.java : $(shell find ./res -type f) app-config.sh ./AndroidManifest.xml ./android-sdk/installed | ./src/$(PACKAGE)
|
||||
$(BUILDTOOLS)/aapt package \
|
||||
-v -f -m -S ./res -J ./src -M ./AndroidManifest.xml \
|
||||
-I $(ANDROID_JAR)
|
||||
|
||||
# generate the AppActivity.java (template)
|
||||
./src/package/AppActivity.java: app-config.sh
|
||||
# generate the AppActivity.java (template
|
||||
# the "|" denotes an "order-only" prerequiste (as in https://stackoverflow.com/a/58040049/1711186)
|
||||
./src/$(PACKAGE)/AppActivity.java: app-config.sh | ./src/$(PACKAGE)
|
||||
./.Makefile.scripts/make--AppActivity.java.sh > $@
|
||||
|
||||
./src/$(PACKAGE):
|
||||
mkdir -p $@
|
||||
|
||||
# install the necessary android sdks
|
||||
./android-sdk/installed: app-config.sh
|
||||
./android-sdk/installed: app-config.sh ./.Makefile.scripts/make--android-sdk.sh
|
||||
./.Makefile.scripts/make--android-sdk.sh
|
||||
|
||||
# generate the AndroidManifest.xml
|
||||
./AndroidManifest.xml: app-config.sh
|
||||
./AndroidManifest.xml: app-config.sh ./.Makefile.scripts/make--AndroidManifest.xml
|
||||
./.Makefile.scripts/make--AndroidManifest.xml
|
||||
|
||||
# !!this step (when/if) run will trigger a restart of the "make" as the rules tartget is included
|
||||
Makefile.app-config: app-config.sh Makefile
|
||||
source app-config.sh; \
|
||||
tee $@ << MAKEFILE_APP_CONFIG
|
||||
|
|
|
@ -4,50 +4,286 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
||||
</head>
|
||||
<style>
|
||||
@keyframes wobble {
|
||||
0% {
|
||||
transform: scale(0.1);
|
||||
/* transform: rotate(-5deg);
|
||||
transform: rotate(5deg);*/
|
||||
opacity:0.0
|
||||
}
|
||||
22% {
|
||||
transform: scale(2.0);
|
||||
/*/ transform: rotate(-5deg);*/
|
||||
opacity:1.0
|
||||
}
|
||||
99% {
|
||||
transform: scale(1.0);
|
||||
/*/ transform: rotate(-5deg);*/
|
||||
opacity:0.0
|
||||
}
|
||||
<style>
|
||||
* {
|
||||
font-family: sans;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
body,html{
|
||||
margin:0;
|
||||
padding:0
|
||||
}
|
||||
|
||||
.infinityScroll{
|
||||
height:50vh;
|
||||
overflow:scroll;
|
||||
border:1px solid red;
|
||||
position:relative;
|
||||
}
|
||||
.space{
|
||||
background:linear-gradient(white, blue);
|
||||
height:300%;
|
||||
opacity:0.5;
|
||||
}
|
||||
</style>
|
||||
<body style=" background: radial-gradient(#111, #000);">
|
||||
<h1> SUPER</h1>
|
||||
<script>
|
||||
var count=0;
|
||||
window.addEventListener("load",()=>{
|
||||
function insert(){
|
||||
count++;
|
||||
var h2 = document.createElement("h2");
|
||||
h2.textContent=" "+ count + ". javascript works"
|
||||
h2.style.color="#"+Math.random().toString(16).slice(3,9);
|
||||
// h2.style.transform="rotate("+((Math.random()*3600)|0)/10+"deg)";
|
||||
h2.style.fontSize=""+Math.random()*20+"pt";
|
||||
h2.style.textAlign="center";
|
||||
h2.style.animation="wobble 5s ease-in-out infinite alternate"
|
||||
document.body.insertBefore(h2,document.body.children[(document.body.children.length*Math.random())|0])
|
||||
// h2.scrollIntoView({"behavior":"smooth"});
|
||||
if(document.body.children.length>15){
|
||||
document.body.children[(document.body.children.length*Math.random())|0].remove();
|
||||
<style>
|
||||
* {
|
||||
font-family: sans;
|
||||
}
|
||||
:root {
|
||||
touch-action: pan-x pan-y;
|
||||
height: 100%
|
||||
}
|
||||
body{
|
||||
background-color:#ffa;
|
||||
margin:0
|
||||
}
|
||||
.monthview {
|
||||
background-color:rgba(255,255,255,0.5);
|
||||
border:2px solid rgba(0,0,0,0.5);
|
||||
border-radius: 10%;
|
||||
margin:5px;
|
||||
min-height: 20vw;
|
||||
/*margin:5px;*/
|
||||
/* scroll-snap-align: center;*/
|
||||
overflow:hidden;
|
||||
}
|
||||
.label{
|
||||
background-color:#a44;
|
||||
color:#fff;
|
||||
font-size:10vw;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.days{
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
}
|
||||
|
||||
.days div {
|
||||
text-align:center;
|
||||
height: 2cm;
|
||||
align:content;
|
||||
border:1px solid grey;
|
||||
}
|
||||
|
||||
|
||||
#mainDiv{
|
||||
box-sizing:border-box;
|
||||
margin:0;
|
||||
/* scroll-snap-type: y mandatory;*/
|
||||
height:100vh;
|
||||
overflow:scroll;
|
||||
border: 1px solid red;
|
||||
margin:0;
|
||||
width:100vw;
|
||||
}
|
||||
.orig{
|
||||
|
||||
border: 3px solid blue;
|
||||
}
|
||||
.weekend{
|
||||
color:red;
|
||||
background-color:white;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<div id="mainDiv" class="infinityScroll"></div>
|
||||
|
||||
<script>
|
||||
// goals:
|
||||
// 1. no/huge limits
|
||||
// 1.1. scroll basis is a number (and hence scroll can continues as long as number can increase/descrease)
|
||||
// 2. both directions
|
||||
// 3. resetability
|
||||
|
||||
// answers
|
||||
// todo: at center of scroll will be a div containing the number
|
||||
// todo: onnce scrolling settled, the position reached is the new center
|
||||
|
||||
// questions
|
||||
// Q1: what is to be scrolled ?
|
||||
// A1: elements that are set to `.infinityScroll` class
|
||||
// Q2: how is the scroll to infinity implemented (exponential stuff)
|
||||
|
||||
window.addEventListener("load",()=>{
|
||||
|
||||
var locale = window.navigator.userLanguage || window.navigator.language || "en";
|
||||
|
||||
|
||||
function createMonthView(date = new Date()){
|
||||
var monthviewDiv = document.createElement("div")
|
||||
monthviewDiv.className = "monthview";
|
||||
var labelDiv = document.createElement("div");
|
||||
labelDiv.textContent = date.toLocaleString(locale,{"month":"long","year":"numeric"})
|
||||
labelDiv.className = "label"
|
||||
monthviewDiv.appendChild(labelDiv);
|
||||
var daysDiv = document.createElement("div");
|
||||
daysDiv.className="days";
|
||||
monthviewDiv.appendChild(daysDiv);
|
||||
// mainDiv.appendChild(monthviewDiv);
|
||||
monthviewDiv.scrollIntoView();
|
||||
// var weekendDiv = document.createElement("div");
|
||||
// weekendDiv.className = "weekend";
|
||||
// daysDiv.appendChild(weekendDiv);
|
||||
var day = new Date(date);
|
||||
day.setDate(1);
|
||||
while(day.getMonth() == date.getMonth()){
|
||||
var dayDiv = document.createElement("div");
|
||||
dayDiv.className = "day";
|
||||
var column = day.getDay();
|
||||
column = column == 0 ? 7: column;
|
||||
if(column>=6){
|
||||
dayDiv.classList.add("weekend");
|
||||
}
|
||||
dayDiv.style.gridColumn = column;
|
||||
dayDiv.textContent = day.getDate()
|
||||
daysDiv.appendChild(dayDiv);
|
||||
day.setDate(day.getDate()+1);
|
||||
}
|
||||
return monthviewDiv;
|
||||
}
|
||||
|
||||
|
||||
function makeInfinityScroll(element){
|
||||
element.nn=element.nn||{"position":0};
|
||||
var mdate;
|
||||
//var before = document.createElement("div");
|
||||
//var after = document.createElement("div");
|
||||
//after.style.backgroundColor="blue";
|
||||
//var center = document.createElement("div");
|
||||
//before.className = after.className = "space";
|
||||
//after.style.background="linear-gradient(yellow,red)";
|
||||
//center.textContent="center "+element.nn.position;
|
||||
//center.style.border="1px solid red";
|
||||
//center = createMonthView();
|
||||
//center.nnindex=0;
|
||||
//element.appendChild(after);
|
||||
|
||||
for(var i = -3; i< 3; i++){
|
||||
mdate = new Date();
|
||||
mdate.setMonth(mdate.getMonth()+i);
|
||||
var nextMonth=createMonthView(mdate);
|
||||
nextMonth.nnindex=i;
|
||||
element.appendChild(nextMonth);
|
||||
nextMonth.classList.add('orig');
|
||||
}
|
||||
|
||||
element.children[3].scrollIntoView({block:"center"});
|
||||
|
||||
|
||||
setTimeout(()=>{
|
||||
|
||||
//console.log(element.firstChild,element.firstChild.nnindex);
|
||||
var beforeind=0;
|
||||
console.log("fc",element,element.firstChild);
|
||||
if(element.firstChild.nnindex){
|
||||
beforeind = element.firstChild.nnindex
|
||||
}
|
||||
mdate = new Date();
|
||||
mdate.setMonth(mdate.getMonth()+beforeind-1);
|
||||
var newBefore = createMonthView(mdate);
|
||||
newBefore.nnindex=beforeind-1;
|
||||
// newBefore.className="space";
|
||||
console.log("prio "+element.scrollTop,""+element.scrollHeight,element.clientHeight);
|
||||
// var savscrolltop = element.scrollTop;
|
||||
element.insertBefore(newBefore,element.firstChild);
|
||||
// element.scrollTop=savscrolltop-newBefore.clientHeight;
|
||||
|
||||
// var beforeind=0;
|
||||
// console.log("fc",element,element.firstChild);
|
||||
// if(element.firstChild.nnindex){
|
||||
// beforeind = element.firstChild.nnindex
|
||||
// }
|
||||
// mdate = new Date();
|
||||
// mdate.setMonth(mdate.getMonth()+beforeind-1);
|
||||
// var newBefore = createMonthView(mdate);
|
||||
// newBefore.nnindex=beforeind-1;
|
||||
//// newBefore.className="space";
|
||||
// console.log("prio "+element.scrollTop,""+element.scrollHeight,element.clientHeight);
|
||||
// var savscrolltop = element.scrollTop;
|
||||
// element.insertBefore(newBefore,element.firstChild);
|
||||
// element.scrollTop=savscrolltop-newBefore.clientHeight;
|
||||
//
|
||||
// console.log("POST "+element.scrollTop,""+element.scrollHeight,element.clientHeight);
|
||||
//
|
||||
},100000);
|
||||
|
||||
|
||||
element.addEventListener("scroll",()=>{
|
||||
if(element.scrollUpdateSchedule!=1){
|
||||
element.scrollUpdateSchedule=1;
|
||||
|
||||
requestAnimationFrame(()=>{
|
||||
element.scrollUpdateSchedule=0;
|
||||
//if(element.scrollTop/element.scrollHeight < 0.2){
|
||||
// console.log("ch",element.clientHeight,element.scrollTop);
|
||||
if(element.scrollTop < 3* element.clientHeight)
|
||||
{
|
||||
console.log(element.scrollTop)
|
||||
//console.log(element.firstChild,element.firstChild.nnindex);
|
||||
var beforeind=0;
|
||||
if(element.firstChild.nnindex){
|
||||
beforeind = element.firstChild.nnindex
|
||||
}
|
||||
mdate = new Date();
|
||||
mdate.setMonth(mdate.getMonth()+beforeind-1);
|
||||
var newBefore = createMonthView(mdate);
|
||||
newBefore.nnindex=beforeind-1;
|
||||
// newBefore.className="space";
|
||||
console.log("prio"+element.scrollTop,element.scrollHeight);
|
||||
element.insertBefore(newBefore,element.firstChild);
|
||||
console.log("POST"+element.scrollTop,element.scrollHeight);
|
||||
|
||||
// while(element.scrollHeight>200*element.clientHeight){
|
||||
// element.children[element.children.length-1].remove();
|
||||
// console.log("REMOVED"+element.scrollTop);
|
||||
// }
|
||||
|
||||
}
|
||||
setTimeout(()=>{window.requestAnimationFrame(insert);},100);
|
||||
|
||||
}
|
||||
insert();
|
||||
},false);
|
||||
</script>
|
||||
</body>
|
||||
});
|
||||
}
|
||||
},false);
|
||||
}
|
||||
/////-- if(element.scrollTop > element.scrollHeight - 4 * element.clientHeight)
|
||||
/////-- {
|
||||
/////-- //var afterind = element.children[element.children.length].nnindex || 0;
|
||||
/////-- var afterind=0;
|
||||
/////-- if(element.children[element.children.length-1].nnindex){
|
||||
/////-- afterind = element.children[element.children.length-1].nnindex;
|
||||
/////-- }
|
||||
/////-- var newAfter = document.createElement("div");
|
||||
/////--// newBefore.className="space";
|
||||
/////-- newAfter.innerHTML=new Array(200).fill("a").map((a,i)=>{return afterind+"-"+i;}).join("<br>");
|
||||
/////-- newAfter.nnindex=afterind+1;
|
||||
/////-- console.log("prio"+element.scrollTop);
|
||||
/////-- element.appendChild(newAfter);
|
||||
/////-- console.log("POST"+element.scrollTop);
|
||||
/////--
|
||||
/////--
|
||||
/////--// while(element.scrollHeight>200*element.clientHeight){
|
||||
/////--// element.children[0].remove();
|
||||
/////--// console.log("REMOVED"+element.scrollTop);
|
||||
/////--// }
|
||||
/////--
|
||||
/////--
|
||||
/////-- }
|
||||
/////--// console.log(Date.now(),"Udpate",element.scrollTop);
|
||||
/////-- console.log(Date.now(),"Udpate",element.scrollTop/element.scrollHeight,"AAA",element.scrollTop,">",element.scrollHeight - 4 * element.clientHeight,element.scrollHeight);
|
||||
/////-- element.scrollUpdateSchedule=0;
|
||||
/////-- });
|
||||
/////-- }
|
||||
// console.log(element.scrollTop);
|
||||
// },false);
|
||||
// }
|
||||
|
||||
document.querySelectorAll('.infinityScroll').forEach(makeInfinityScroll);
|
||||
|
||||
},false);
|
||||
|
||||
window.addEventListener("error",(error)=>{ document.body.innerHTML = "<h1>error</h1><pre>" + error.filename + "\nline:" + error.lineno + "\n"+error.message +"</pre>"; },false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
182
app/assets/index.html.1
Normal file
182
app/assets/index.html.1
Normal file
|
@ -0,0 +1,182 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
||||
</head>
|
||||
<style>
|
||||
:root {
|
||||
touch-action: pan-x pan-y;
|
||||
height: 100%
|
||||
}
|
||||
@keyframes wobble {
|
||||
0% {
|
||||
transform: scale(0.1);
|
||||
opacity:0.0
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.0);
|
||||
opacity:1.0
|
||||
}
|
||||
}
|
||||
body{
|
||||
background-color:#ffa;
|
||||
}
|
||||
div {
|
||||
margin: 0em;
|
||||
padding: 2em;
|
||||
}
|
||||
#target {
|
||||
background: white;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
<!--<body onload="init();" style="touch-action:none">-->
|
||||
<body onload="init();">
|
||||
<div id="target">
|
||||
Touch and Hold with 2 pointers, then pinch in or out.<br />
|
||||
The background color will change to pink if the pinch is opening (Zoom In)
|
||||
or changes to lightblue if the pinch is closing (Zoom out).
|
||||
</div>
|
||||
<!-- UI for logging/debugging -->
|
||||
<button id="log" onclick="enableLog(event);">Start/Stop event logging</button>
|
||||
<button id="clearlog" onclick="clearLog(event);">Clear the log</button>
|
||||
<p></p>
|
||||
<output></output>
|
||||
|
||||
<a href="https://server.alexmahr.de/calender">calender</a>
|
||||
<h1>Webview</h1>
|
||||
<pre id="output">output</pre>
|
||||
<script>
|
||||
window.addEventListener("error",(error)=>{
|
||||
document.body.innerHTML = "<h1>error</h1><pre>" +
|
||||
error.filename +
|
||||
"\nline:" + error.lineno +
|
||||
"\n"+error.message +"</pre>";
|
||||
},false);
|
||||
|
||||
['touchstart','touchmove','touchend'].forEach((evname)=>{
|
||||
window.addEventListener(evname,(ev)=>{
|
||||
output.textContent+=evname+"\n" ;
|
||||
ev.preventDefault();
|
||||
},false);
|
||||
});
|
||||
window.addEventListener("load",()=>{
|
||||
var count = localStorage.getItem("app-opened-count")|| 0;
|
||||
count++;
|
||||
localStorage.setItem("app-opened-count",count);
|
||||
var h2 = document.createElement("h2");
|
||||
h2.textContent = "Javascript works! (app was opened " + count + " times)";
|
||||
h2.style.animation="wobble 1s ease-in-out 0s 1 forwards normal running"
|
||||
document.body.appendChild(h2);
|
||||
},false);
|
||||
|
||||
// Global vars to cache event state
|
||||
const evCache = [];
|
||||
let prevDiff = -1;
|
||||
|
||||
function init() {
|
||||
// Install event handlers for the pointer target
|
||||
const el = document.getElementById("target");
|
||||
el.onpointerdown = pointerdownHandler;
|
||||
el.onpointermove = pointermoveHandler;
|
||||
|
||||
// Use same handler for pointer{up,cancel,out,leave} events since
|
||||
// the semantics for these events - in this app - are the same.
|
||||
el.onpointerup = pointerupHandler;
|
||||
el.onpointercancel = pointerupHandler;
|
||||
el.onpointerout = pointerupHandler;
|
||||
el.onpointerleave = pointerupHandler;
|
||||
}
|
||||
function pointerdownHandler(ev) {
|
||||
// The pointerdown event signals the start of a touch interaction.
|
||||
// This event is cached to support 2-finger gestures
|
||||
evCache.push(ev);
|
||||
log("pointerDown", ev);
|
||||
}
|
||||
function pointermoveHandler(ev) {
|
||||
// This function implements a 2-pointer horizontal pinch/zoom gesture.
|
||||
//
|
||||
// If the distance between the two pointers has increased (zoom in),
|
||||
// the target element's background is changed to "pink" and if the
|
||||
// distance is decreasing (zoom out), the color is changed to "lightblue".
|
||||
//
|
||||
// This function sets the target element's border to "dashed" to visually
|
||||
// indicate the pointer's target received a move event.
|
||||
log("pointerMove", ev);
|
||||
ev.target.style.border = "dashed";
|
||||
|
||||
// Find this event in the cache and update its record with this event
|
||||
const index = evCache.findIndex(
|
||||
(cachedEv) => cachedEv.pointerId === ev.pointerId,
|
||||
);
|
||||
evCache[index] = ev;
|
||||
|
||||
// If two pointers are down, check for pinch gestures
|
||||
if (evCache.length === 2) {
|
||||
// Calculate the distance between the two pointers
|
||||
const curDiff = Math.abs(evCache[0].clientX - evCache[1].clientX);
|
||||
|
||||
if (prevDiff > 0) {
|
||||
if (curDiff > prevDiff) {
|
||||
// The distance between the two pointers has increased
|
||||
log("Pinch moving OUT -> Zoom in", ev);
|
||||
ev.target.style.background = "pink";
|
||||
}
|
||||
if (curDiff < prevDiff) {
|
||||
// The distance between the two pointers has decreased
|
||||
log("Pinch moving IN -> Zoom out", ev);
|
||||
ev.target.style.background = "lightblue";
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the distance for the next move event
|
||||
prevDiff = curDiff;
|
||||
}
|
||||
}
|
||||
function pointerupHandler(ev) {
|
||||
log(ev.type, ev);
|
||||
// Remove this pointer from the cache and reset the target's
|
||||
// background and border
|
||||
removeEvent(ev);
|
||||
ev.target.style.background = "white";
|
||||
ev.target.style.border = "1px solid black";
|
||||
|
||||
// If the number of pointers down is less than two then reset diff tracker
|
||||
if (evCache.length < 2) {
|
||||
prevDiff = -1;
|
||||
}
|
||||
}
|
||||
function removeEvent(ev) {
|
||||
// Remove this event from the target's cache
|
||||
const index = evCache.findIndex(
|
||||
(cachedEv) => cachedEv.pointerId === ev.pointerId,
|
||||
);
|
||||
evCache.splice(index, 1);
|
||||
}
|
||||
// Log events flag
|
||||
let logEvents = false;
|
||||
|
||||
// Logging/debugging functions
|
||||
function enableLog(ev) {
|
||||
logEvents = !logEvents;
|
||||
}
|
||||
|
||||
function log(prefix, ev) {
|
||||
if (!logEvents) return;
|
||||
const o = document.getElementsByTagName("output")[0];
|
||||
o.innerText += `${prefix}:
|
||||
pointerID = ${ev.pointerId}
|
||||
pointerType = ${ev.pointerType}
|
||||
isPrimary = ${ev.isPrimary}
|
||||
`;
|
||||
}
|
||||
|
||||
function clearLog(event) {
|
||||
const o = document.getElementsByTagName("output")[0];
|
||||
o.textContent = "";
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
182
app/assets/index.html.1.html
Normal file
182
app/assets/index.html.1.html
Normal file
|
@ -0,0 +1,182 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
||||
</head>
|
||||
<style>
|
||||
:root {
|
||||
touch-action: pan-x pan-y;
|
||||
height: 100%
|
||||
}
|
||||
@keyframes wobble {
|
||||
0% {
|
||||
transform: scale(0.1);
|
||||
opacity:0.0
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.0);
|
||||
opacity:1.0
|
||||
}
|
||||
}
|
||||
body{
|
||||
background-color:#ffa;
|
||||
}
|
||||
div {
|
||||
margin: 0em;
|
||||
padding: 2em;
|
||||
}
|
||||
#target {
|
||||
background: white;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
<!--<body onload="init();" style="touch-action:none">-->
|
||||
<body onload="init();">
|
||||
<div id="target">
|
||||
Touch and Hold with 2 pointers, then pinch in or out.<br />
|
||||
The background color will change to pink if the pinch is opening (Zoom In)
|
||||
or changes to lightblue if the pinch is closing (Zoom out).
|
||||
</div>
|
||||
<!-- UI for logging/debugging -->
|
||||
<button id="log" onclick="enableLog(event);">Start/Stop event logging</button>
|
||||
<button id="clearlog" onclick="clearLog(event);">Clear the log</button>
|
||||
<p></p>
|
||||
<output></output>
|
||||
|
||||
<a href="https://server.alexmahr.de/calender">calender</a>
|
||||
<h1>Webview</h1>
|
||||
<pre id="output">output</pre>
|
||||
<script>
|
||||
window.addEventListener("error",(error)=>{
|
||||
document.body.innerHTML = "<h1>error</h1><pre>" +
|
||||
error.filename +
|
||||
"\nline:" + error.lineno +
|
||||
"\n"+error.message +"</pre>";
|
||||
},false);
|
||||
|
||||
['touchstart','touchmove','touchend'].forEach((evname)=>{
|
||||
window.addEventListener(evname,(ev)=>{
|
||||
output.textContent+=evname+"\n" ;
|
||||
ev.preventDefault();
|
||||
},false);
|
||||
});
|
||||
window.addEventListener("load",()=>{
|
||||
var count = localStorage.getItem("app-opened-count")|| 0;
|
||||
count++;
|
||||
localStorage.setItem("app-opened-count",count);
|
||||
var h2 = document.createElement("h2");
|
||||
h2.textContent = "Javascript works! (app was opened " + count + " times)";
|
||||
h2.style.animation="wobble 1s ease-in-out 0s 1 forwards normal running"
|
||||
document.body.appendChild(h2);
|
||||
},false);
|
||||
|
||||
// Global vars to cache event state
|
||||
const evCache = [];
|
||||
let prevDiff = -1;
|
||||
|
||||
function init() {
|
||||
// Install event handlers for the pointer target
|
||||
const el = document.getElementById("target");
|
||||
el.onpointerdown = pointerdownHandler;
|
||||
el.onpointermove = pointermoveHandler;
|
||||
|
||||
// Use same handler for pointer{up,cancel,out,leave} events since
|
||||
// the semantics for these events - in this app - are the same.
|
||||
el.onpointerup = pointerupHandler;
|
||||
el.onpointercancel = pointerupHandler;
|
||||
el.onpointerout = pointerupHandler;
|
||||
el.onpointerleave = pointerupHandler;
|
||||
}
|
||||
function pointerdownHandler(ev) {
|
||||
// The pointerdown event signals the start of a touch interaction.
|
||||
// This event is cached to support 2-finger gestures
|
||||
evCache.push(ev);
|
||||
log("pointerDown", ev);
|
||||
}
|
||||
function pointermoveHandler(ev) {
|
||||
// This function implements a 2-pointer horizontal pinch/zoom gesture.
|
||||
//
|
||||
// If the distance between the two pointers has increased (zoom in),
|
||||
// the target element's background is changed to "pink" and if the
|
||||
// distance is decreasing (zoom out), the color is changed to "lightblue".
|
||||
//
|
||||
// This function sets the target element's border to "dashed" to visually
|
||||
// indicate the pointer's target received a move event.
|
||||
log("pointerMove", ev);
|
||||
ev.target.style.border = "dashed";
|
||||
|
||||
// Find this event in the cache and update its record with this event
|
||||
const index = evCache.findIndex(
|
||||
(cachedEv) => cachedEv.pointerId === ev.pointerId,
|
||||
);
|
||||
evCache[index] = ev;
|
||||
|
||||
// If two pointers are down, check for pinch gestures
|
||||
if (evCache.length === 2) {
|
||||
// Calculate the distance between the two pointers
|
||||
const curDiff = Math.abs(evCache[0].clientX - evCache[1].clientX);
|
||||
|
||||
if (prevDiff > 0) {
|
||||
if (curDiff > prevDiff) {
|
||||
// The distance between the two pointers has increased
|
||||
log("Pinch moving OUT -> Zoom in", ev);
|
||||
ev.target.style.background = "pink";
|
||||
}
|
||||
if (curDiff < prevDiff) {
|
||||
// The distance between the two pointers has decreased
|
||||
log("Pinch moving IN -> Zoom out", ev);
|
||||
ev.target.style.background = "lightblue";
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the distance for the next move event
|
||||
prevDiff = curDiff;
|
||||
}
|
||||
}
|
||||
function pointerupHandler(ev) {
|
||||
log(ev.type, ev);
|
||||
// Remove this pointer from the cache and reset the target's
|
||||
// background and border
|
||||
removeEvent(ev);
|
||||
ev.target.style.background = "white";
|
||||
ev.target.style.border = "1px solid black";
|
||||
|
||||
// If the number of pointers down is less than two then reset diff tracker
|
||||
if (evCache.length < 2) {
|
||||
prevDiff = -1;
|
||||
}
|
||||
}
|
||||
function removeEvent(ev) {
|
||||
// Remove this event from the target's cache
|
||||
const index = evCache.findIndex(
|
||||
(cachedEv) => cachedEv.pointerId === ev.pointerId,
|
||||
);
|
||||
evCache.splice(index, 1);
|
||||
}
|
||||
// Log events flag
|
||||
let logEvents = false;
|
||||
|
||||
// Logging/debugging functions
|
||||
function enableLog(ev) {
|
||||
logEvents = !logEvents;
|
||||
}
|
||||
|
||||
function log(prefix, ev) {
|
||||
if (!logEvents) return;
|
||||
const o = document.getElementsByTagName("output")[0];
|
||||
o.innerText += `${prefix}:
|
||||
pointerID = ${ev.pointerId}
|
||||
pointerType = ${ev.pointerType}
|
||||
isPrimary = ${ev.isPrimary}
|
||||
`;
|
||||
}
|
||||
|
||||
function clearLog(event) {
|
||||
const o = document.getElementsByTagName("output")[0];
|
||||
o.textContent = "";
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
105
app/assets/pinch.js
Normal file
105
app/assets/pinch.js
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Global vars to cache event state
|
||||
const evCache = [];
|
||||
let prevDiff = -1;
|
||||
|
||||
function init() {
|
||||
// Install event handlers for the pointer target
|
||||
const el = document.getElementById("target");
|
||||
el.onpointerdown = pointerdownHandler;
|
||||
el.onpointermove = pointermoveHandler;
|
||||
|
||||
// Use same handler for pointer{up,cancel,out,leave} events since
|
||||
// the semantics for these events - in this app - are the same.
|
||||
el.onpointerup = pointerupHandler;
|
||||
el.onpointercancel = pointerupHandler;
|
||||
el.onpointerout = pointerupHandler;
|
||||
el.onpointerleave = pointerupHandler;
|
||||
}
|
||||
function pointerdownHandler(ev) {
|
||||
// The pointerdown event signals the start of a touch interaction.
|
||||
// This event is cached to support 2-finger gestures
|
||||
evCache.push(ev);
|
||||
log("pointerDown", ev);
|
||||
}
|
||||
function pointermoveHandler(ev) {
|
||||
// This function implements a 2-pointer horizontal pinch/zoom gesture.
|
||||
//
|
||||
// If the distance between the two pointers has increased (zoom in),
|
||||
// the target element's background is changed to "pink" and if the
|
||||
// distance is decreasing (zoom out), the color is changed to "lightblue".
|
||||
//
|
||||
// This function sets the target element's border to "dashed" to visually
|
||||
// indicate the pointer's target received a move event.
|
||||
log("pointerMove", ev);
|
||||
ev.target.style.border = "dashed";
|
||||
|
||||
// Find this event in the cache and update its record with this event
|
||||
const index = evCache.findIndex(
|
||||
(cachedEv) => cachedEv.pointerId === ev.pointerId,
|
||||
);
|
||||
evCache[index] = ev;
|
||||
|
||||
// If two pointers are down, check for pinch gestures
|
||||
if (evCache.length === 2) {
|
||||
// Calculate the distance between the two pointers
|
||||
const curDiff = Math.abs(evCache[0].clientX - evCache[1].clientX);
|
||||
|
||||
if (prevDiff > 0) {
|
||||
if (curDiff > prevDiff) {
|
||||
// The distance between the two pointers has increased
|
||||
log("Pinch moving OUT -> Zoom in", ev);
|
||||
ev.target.style.background = "pink";
|
||||
}
|
||||
if (curDiff < prevDiff) {
|
||||
// The distance between the two pointers has decreased
|
||||
log("Pinch moving IN -> Zoom out", ev);
|
||||
ev.target.style.background = "lightblue";
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the distance for the next move event
|
||||
prevDiff = curDiff;
|
||||
}
|
||||
}
|
||||
function pointerupHandler(ev) {
|
||||
log(ev.type, ev);
|
||||
// Remove this pointer from the cache and reset the target's
|
||||
// background and border
|
||||
removeEvent(ev);
|
||||
ev.target.style.background = "white";
|
||||
ev.target.style.border = "1px solid black";
|
||||
|
||||
// If the number of pointers down is less than two then reset diff tracker
|
||||
if (evCache.length < 2) {
|
||||
prevDiff = -1;
|
||||
}
|
||||
}
|
||||
function removeEvent(ev) {
|
||||
// Remove this event from the target's cache
|
||||
const index = evCache.findIndex(
|
||||
(cachedEv) => cachedEv.pointerId === ev.pointerId,
|
||||
);
|
||||
evCache.splice(index, 1);
|
||||
}
|
||||
// Log events flag
|
||||
let logEvents = false;
|
||||
|
||||
// Logging/debugging functions
|
||||
function enableLog(ev) {
|
||||
logEvents = !logEvents;
|
||||
}
|
||||
|
||||
function log(prefix, ev) {
|
||||
if (!logEvents) return;
|
||||
const o = document.getElementsByTagName("output")[0];
|
||||
o.innerText += `${prefix}:
|
||||
pointerID = ${ev.pointerId}
|
||||
pointerType = ${ev.pointerType}
|
||||
isPrimary = ${ev.isPrimary}
|
||||
`;
|
||||
}
|
||||
|
||||
function clearLog(event) {
|
||||
const o = document.getElementsByTagName("output")[0];
|
||||
o.textContent = "";
|
||||
}
|
|
@ -36,7 +36,11 @@ RUN <<EOF
|
|||
cat > /bin/makefile-bash-wrapper.sh << 'WRAPPER'
|
||||
#!/bin/bash
|
||||
printf $'\033[0;32m''#----------------------------------------\n'$'\033[0m' >&2
|
||||
bash "$@"
|
||||
bash -e "$@" || {
|
||||
EXITCODE=$?
|
||||
printf $'\033[0;31m''ERROR EXITCODE='"$EXITCODE"'\n'$'\033[0m' >&2
|
||||
exit $EXITCODE
|
||||
}
|
||||
printf '\n\n\n\n' >&2
|
||||
WRAPPER
|
||||
chmod u+x /bin/makefile-bash-wrapper.sh
|
||||
|
@ -44,6 +48,7 @@ EOF
|
|||
DOCKERFILEEOF
|
||||
}
|
||||
|
||||
printf $'\033[0;33m'"$(date -Iseconds) starting build"'$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n'$'\033[0m'
|
||||
diff Dockerfile <(DockerfileContent) 2>/dev/null > /dev/null || {
|
||||
test -f Dockerfile && {
|
||||
read -p 'reset/start Dockerfile[Y/n]' YES
|
||||
|
|
BIN
screenshots/screenshot.01.avif
Normal file
BIN
screenshots/screenshot.01.avif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
Loading…
Add table
Reference in a new issue