Skip to content

Commit 8ce2284

Browse files
committed
Implement android builds and include assets
- Create `android-resources` directory that holds app assets - Initialize `android-template` for using as a base for app builds - Handler copies compiled `js` files into template and invokes `cordova` A template directory is initialized with `cordova create` and assets are copied in from `android-resources`. Includes main view HTML, CSS, and App Icons. The handler clones the template directory to `data/<mode>/android/` and copies in the `js` files from the compilation step. `cordova build` is invoked and the generated `.apk` file is fetched as a binary blob and downloaded.
1 parent 4665c67 commit 8ce2284

File tree

14 files changed

+360
-23
lines changed

14 files changed

+360
-23
lines changed

android-resources/config.xml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version='1.0' encoding='utf-8'?>
2+
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3+
<name>CodeWorld App</name>
4+
<description>
5+
CodeWorld App
6+
</description>
7+
<author href="https://code.world">
8+
CodeWorld
9+
</author>
10+
<content src="index.html" />
11+
<access origin="*" />
12+
<allow-intent href="http://*/*" />
13+
<allow-intent href="https://*/*" />
14+
<allow-intent href="tel:*" />
15+
<allow-intent href="sms:*" />
16+
<allow-intent href="mailto:*" />
17+
<allow-intent href="geo:*" />
18+
<platform name="android">
19+
<allow-intent href="market:*" />
20+
<icon density="mdpi" src="res/icon/android/mipmap-mdpi/codeworld.png" />
21+
<icon density="hdpi" src="res/icon/android/mipmap-hdpi/codeworld.png" />
22+
<icon density="xhdpi" src="res/icon/android/mipmap-xhdpi/codeworld.png" />
23+
<icon density="xxhdpi" src="res/icon/android/mipmap-xxhdpi/codeworld.png" />
24+
<icon density="xxxhdpi" src="res/icon/android/mipmap-xxxhdpi/codeworld.png" />
25+
</platform>
26+
<plugin name="cordova-plugin-whitelist" spec="^1.3.2" />
27+
</widget>
3 KB
Loading
1.73 KB
Loading
4.06 KB
Loading
7.54 KB
Loading
10.6 KB
Loading
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
* {
2+
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
3+
}
4+
5+
body {
6+
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
7+
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
8+
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
9+
background-color:#E4E4E4;
10+
background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
11+
background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
12+
background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
13+
background-image:-webkit-gradient(
14+
linear,
15+
left top,
16+
left bottom,
17+
color-stop(0, #A7A7A7),
18+
color-stop(0.51, #E4E4E4)
19+
);
20+
background-attachment:fixed;
21+
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
22+
font-size:12px;
23+
height:100%;
24+
margin:0px;
25+
padding:0px;
26+
text-transform:uppercase;
27+
width:100%;
28+
}
29+
30+
/* Portrait layout (default) */
31+
.app {
32+
position:absolute; /* position in the center of the screen */
33+
left:50%;
34+
top:30%;
35+
height:50px; /* text area height */
36+
width:225px; /* text area width */
37+
text-align:center;
38+
}
39+
40+
canvas {
41+
position: relative;
42+
left: -50%;
43+
top: -50%;
44+
}
45+
46+
.app-inner {
47+
position: relative;
48+
left: -50%;
49+
top: -50%;
50+
}
51+
52+
/* Landscape layout (with min-width) */
53+
@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
54+
.app {
55+
background-position:left center;
56+
padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */
57+
margin:-90px 0px 0px -198px; /* offset vertical: half of image height */
58+
/* offset horizontal: half of image width and text area width */
59+
}
60+
}
61+
62+
h1 {
63+
font-size:24px;
64+
font-weight:normal;
65+
margin:0px;
66+
overflow:visible;
67+
padding:0px;
68+
text-align:center;
69+
}
70+
71+
.event {
72+
border-radius:4px;
73+
-webkit-border-radius:4px;
74+
color:#FFFFFF;
75+
font-size:12px;
76+
margin:0px 30px;
77+
padding:2px 0px;
78+
}
79+
80+
.event.listening {
81+
background-color:#333333;
82+
display:block;
83+
}
84+
85+
.event.received {
86+
background-color:#4B946A;
87+
display:none;
88+
}
89+
90+
@keyframes fade {
91+
from { opacity: 1.0; }
92+
50% { opacity: 0.4; }
93+
to { opacity: 1.0; }
94+
}
95+
96+
@-webkit-keyframes fade {
97+
from { opacity: 1.0; }
98+
50% { opacity: 0.4; }
99+
to { opacity: 1.0; }
100+
}
101+
102+
.blink {
103+
animation:fade 3000ms infinite;
104+
-webkit-animation:fade 3000ms infinite;
105+
}

android-resources/www/index.html

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default,Array.prototype.find,Number.isFinite,Number.isInteger,console,console.log,document.head,performance.now"></script>
4+
<head>
5+
<title>CodeWorld</title>
6+
<style type="text/css">
7+
* { margin: 0; overflow: hidden }
8+
#screen {
9+
cursor: default;
10+
width: 100vw;
11+
height: 100vw;
12+
max-height: 100vh;
13+
max-width: 100vh;
14+
}
15+
</style>
16+
</head>
17+
<body style="text-align: center">
18+
<canvas id="screen"></canvas>
19+
</body>
20+
<script type="text/javascript">
21+
function addMessage(err, str) {
22+
if (window.parent && window.parent.addToMessage) {
23+
var message = window.parent.addToMessage(str);
24+
25+
if (err) {
26+
var message = window.parent.document.getElementById('message');
27+
message.classList.add('error');
28+
}
29+
} else {
30+
console.log(str);
31+
}
32+
}
33+
34+
function showCanvas() {
35+
if (!window.parent) {
36+
return;
37+
}
38+
39+
var runner = window.parent.document.getElementById('runner');
40+
if (!runner) {
41+
return;
42+
}
43+
44+
runner.style.display = '';
45+
runner.focus();
46+
runner.contentWindow.focus();
47+
}
48+
49+
function start() {
50+
h$base_writeStdout = function(fd, fdo, buf, buf_offset, n, c) {
51+
addMessage(false, h$decodeUtf8(buf, n, buf_offset));
52+
c(n);
53+
};
54+
h$base_writeStderr = function(fd, fdo, buf, buf_offset, n, c) {
55+
addMessage(false, h$decodeUtf8(buf, n, buf_offset));
56+
c(n);
57+
};
58+
h$log = function() {
59+
var s = '';
60+
for(var i=0;i<arguments.length;i++) { s = s + arguments[i]; }
61+
addMessage(false, s+'\n');
62+
};
63+
h$errorMsg = function(str) {
64+
for(var i=1;i<arguments.length;i++) {
65+
str = str.replace(/%s/, arguments[i]);
66+
}
67+
addMessage(true, '\n' + str);
68+
};
69+
h$base_stdout_fd.write = h$base_writeStdout;
70+
h$base_stderr_fd.write = h$base_writeStderr;
71+
72+
h$run(h$mainZCZCMainzimain);
73+
}
74+
75+
function reportRuntimeError(err, str) {
76+
if (window.parent.addToMessage) {
77+
var message = window.parent.addToMessage('\n' + str);
78+
79+
if (err) {
80+
var message = window.parent.document.getElementById('message');
81+
message.classList.add('error');
82+
}
83+
} else {
84+
console.log(str);
85+
}
86+
}
87+
88+
var loadScript = document.createElement('script');
89+
loadScript.setAttribute('type', 'text/javascript');
90+
loadScript.setAttribute('src', 'js/runjs.js');
91+
loadScript.onload = function() {
92+
start();
93+
};
94+
document.head.appendChild(loadScript);
95+
</script>
96+
97+
</html>
98+
99+

codeworld-server/src/AndroidExport.hs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,36 @@ import System.FilePath
2424

2525
import Util
2626

27-
initCordovaProject :: BuildMode -> ProgramId ->IO ()
27+
buildAndroid :: BuildMode -> ProgramId -> IO()
28+
buildAndroid mode programId = do
29+
initCordovaProject mode programId
30+
copySource mode programId
31+
buildApk mode programId
32+
return ()
33+
34+
35+
initCordovaProject :: BuildMode -> ProgramId -> IO ()
2836
initCordovaProject mode programId = do
29-
putStrLn $ androidRootDir mode
30-
checkIfBuildExists <- doesDirectoryExist $ androidRootDir mode </> sourceBase programId
37+
let buildDir = androidBuildDir mode programId
38+
checkIfBuildExists <- doesDirectoryExist buildDir
3139
case checkIfBuildExists of
32-
True -> do
33-
putStrLn "Build Exists"
40+
True -> return ()
3441
False -> do
35-
createDirectory $ androidRootDir mode </> sourceBaseDir programId
36-
readProcess "cordova" ["create", (androidRootDir mode </> sourceBase programId)] ""
37-
putStrLn "Build Doesn't Exist"
42+
checkIfParentExists <- doesDirectoryExist $ androidRootDir mode </> sourceParent programId
43+
case checkIfParentExists of
44+
True -> return ()
45+
False -> do
46+
createDirectory $ androidRootDir mode </> sourceParent programId
47+
copyDirIfExists "android-template" (androidRootDir mode </> sourceBase programId)
48+
return ()
49+
50+
copySource :: BuildMode -> ProgramId -> IO ()
51+
copySource mode programId = do
52+
copyFile (buildRootDir mode </> targetFile programId) (androidBuildDir mode programId </> "www" </> "js" </> "runjs.js")
53+
54+
buildApk :: BuildMode -> ProgramId -> IO ()
55+
buildApk mode programId = do
56+
currwd <- getCurrentDirectory
57+
setCurrentDirectory $ androidBuildDir mode programId
58+
readProcess "cordova" ["build", "android"] ""
59+
setCurrentDirectory currwd

codeworld-server/src/Main.hs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ site clientId =
122122
("moveProject", moveProjectHandler clientId),
123123
("compile", compileHandler),
124124
("exportAndroid", exportAndroidHandler),
125+
("getAndroid", getAndroidHandler),
125126
("saveXMLhash", saveXMLHashHandler),
126127
("loadXML", loadXMLHandler),
127128
("loadSource", loadSourceHandler),
@@ -321,7 +322,7 @@ compileHandler = do
321322
writeDeployLink mode deployId programId
322323
compileIfNeeded mode programId
323324
unless success $ modifyResponse $ setResponseCode 500
324-
modifyResponse $ setContentType "text/plain"
325+
modifyResponse $ setContentType "application/json"
325326
let result = CompileResult (unProgramId programId) (unDeployId deployId)
326327
writeLBS (encode result)
327328

@@ -358,18 +359,25 @@ runHandler = do
358359

359360
exportAndroidHandler :: Snap()
360361
exportAndroidHandler = do
361-
mode <- getBuildMode
362-
Just source <- getParam "source"
363-
let programId = sourceToProgramId source
364-
deployId = sourceToDeployId source
365-
success <- liftIO $ do
366-
ensureProgramDir mode programId
367-
B.writeFile (buildRootDir mode </> sourceFile programId) source
368-
writeDeployLink mode deployId programId
369-
compileIfNeeded mode programId
370-
unless success $ modifyResponse $ setResponseCode 500
371-
modifyResponse $ setContentType "text/plain"
372-
liftIO $ initCordovaProject mode programId
362+
mode <- getBuildMode
363+
Just source <- getParam "source"
364+
let programId = sourceToProgramId source
365+
deployId = sourceToDeployId source
366+
success <- liftIO $ do
367+
ensureProgramDir mode programId
368+
B.writeFile (buildRootDir mode </> sourceFile programId) source
369+
writeDeployLink mode deployId programId
370+
compileIfNeeded mode programId
371+
unless success $ modifyResponse $ setResponseCode 500
372+
liftIO $ buildAndroid mode programId
373+
let result = CompileResult (unProgramId programId) (unDeployId deployId)
374+
writeLBS (encode result)
375+
376+
getAndroidHandler :: Snap()
377+
getAndroidHandler = do
378+
mode <- getBuildMode
379+
programId <- getHashParam True mode
380+
serveFileAs "application/vnd.android.package-archive" (apkFile mode programId)
373381

374382
runMessageHandler :: Snap ()
375383
runMessageHandler = do

0 commit comments

Comments
 (0)