Skip to content

Commit 8a738e7

Browse files
committed
android: add icon, add about, add addon settings, add privacy
1 parent 53414d6 commit 8a738e7

32 files changed

+297
-294
lines changed

android/app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ dependencies {
5757
implementation(libs.androidx.constraintlayout)
5858
implementation(libs.androidx.webkit)
5959
implementation(libs.brotlidec)
60+
implementation(libs.androidx.navigation.compose)
6061
testImplementation(libs.junit)
6162
androidTestImplementation(libs.androidx.junit)
6263
androidTestImplementation(libs.androidx.espresso.core)

android/app/src/main/assets/preload/editor.js

Lines changed: 68 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,86 @@ const {contextBridge, ipcRenderer} = require('electron');
22

33
contextBridge.exposeInMainWorld('EditorPreload', {
44
isInitiallyFullscreen: () => ipcRenderer.sendSync('is-initially-fullscreen'),
5-
getInitialFile: () => ipcRenderer.invoke('get-initial-file'),
5+
getInitialFile: async () => null,
66
getFile: (id) => ipcRenderer.invoke('get-file', id),
77
openedFile: (id) => ipcRenderer.invoke('opened-file', id),
88
closedFile: () => ipcRenderer.invoke('closed-file'),
99
showSaveFilePicker: (suggestedName) => ipcRenderer.invoke('show-save-file-picker', suggestedName),
1010
showOpenFilePicker: () => ipcRenderer.invoke('show-open-file-picker'),
1111
setLocale: (locale) => ipcRenderer.sendSync('set-locale', locale),
12-
setChanged: (changed) => ipcRenderer.invoke('set-changed', changed),
13-
openNewWindow: () => ipcRenderer.invoke('open-new-window'),
14-
openAddonSettings: (search) => ipcRenderer.invoke('open-addon-settings', search),
15-
openPackager: () => ipcRenderer.invoke('open-packager'),
16-
openDesktopSettings: () => ipcRenderer.invoke('open-desktop-settings'),
17-
openPrivacy: () => ipcRenderer.invoke('open-privacy'),
18-
openAbout: () => ipcRenderer.invoke('open-about'),
19-
getPreferredMediaDevices: () => ipcRenderer.invoke('get-preferred-media-devices'),
20-
getAdvancedCustomizations: () => ipcRenderer.invoke('get-advanced-customizations'),
21-
setExportForPackager: (callback) => {
22-
exportForPackager = callback;
23-
},
24-
setIsFullScreen: (isFullScreen) => ipcRenderer.invoke('set-is-full-screen', isFullScreen)
12+
setChanged: (changed) => {},
13+
// openNewWindow: () => ipcRenderer.invoke('open-new-window'),
14+
openAddonSettings: (search) => ipcRenderer.sendSync('open-addon-settings', search),
15+
//openPackager: () => ipcRenderer.invoke('open-packager'),
16+
//openDesktopSettings: () => ipcRenderer.invoke('open-desktop-settings'),
17+
openPrivacy: () => ipcRenderer.sendSync('open-privacy'),
18+
openAbout: () => ipcRenderer.sendSync('open-about'),
19+
//getPreferredMediaDevices: () => ipcRenderer.invoke('get-preferred-media-devices'),
20+
getAdvancedCustomizations: async () => ({}),
21+
setExportForPackager: (callback) => {},
22+
setIsFullScreen: (isFullScreen) => {}
2523
});
2624

27-
let exportForPackager = () => Promise.reject(new Error('exportForPackager missing'));
25+
//let exportForPackager = () => Promise.reject(new Error('exportForPackager missing'));
2826

29-
ipcRenderer.on('export-project-to-port', (e) => {
30-
const port = e.ports[0];
31-
exportForPackager()
32-
.then(({data, name}) => {
33-
port.postMessage({ data, name });
34-
})
35-
.catch((error) => {
36-
console.error(error);
37-
port.postMessage({ error: true });
38-
});
39-
});
27+
//ipcRenderer.on('export-project-to-port', (e) => {
28+
// const port = e.ports[0];
29+
// exportForPackager()
30+
// .then(({data, name}) => {
31+
// port.postMessage({ data, name });
32+
// })
33+
// .catch((error) => {
34+
// console.error(error);
35+
// port.postMessage({ error: true });
36+
// });
37+
//});
4038

41-
window.addEventListener('message', (e) => {
42-
if (e.source === window) {
43-
const data = e.data;
44-
if (data && typeof data.ipcStartWriteStream === 'string') {
45-
ipcRenderer.postMessage('start-write-stream', data.ipcStartWriteStream, e.ports);
46-
}
47-
}
48-
});
39+
//window.addEventListener('message', (e) => {
40+
// if (e.source === window) {
41+
// const data = e.data;
42+
// if (data && typeof data.ipcStartWriteStream === 'string') {
43+
// ipcRenderer.postMessage('start-write-stream', data.ipcStartWriteStream, e.ports);
44+
// }
45+
// }
46+
//});
4947

50-
ipcRenderer.on('enumerate-media-devices', (e) => {
51-
navigator.mediaDevices.enumerateDevices()
52-
.then((devices) => {
53-
e.sender.send('enumerated-media-devices', {
54-
devices: devices.map((device) => ({
55-
deviceId: device.deviceId,
56-
kind: device.kind,
57-
label: device.label
58-
}))
59-
});
60-
})
61-
.catch((error) => {
62-
console.error(error);
63-
e.sender.send('enumerated-media-devices', {
64-
error: `${error}`
65-
});
66-
});
67-
});
48+
//ipcRenderer.on('enumerate-media-devices', (e) => {
49+
// navigator.mediaDevices.enumerateDevices()
50+
// .then((devices) => {
51+
// e.sender.send('enumerated-media-devices', {
52+
// devices: devices.map((device) => ({
53+
// deviceId: device.deviceId,
54+
// kind: device.kind,
55+
// label: device.label
56+
// }))
57+
// });
58+
// })
59+
// .catch((error) => {
60+
// console.error(error);
61+
// e.sender.send('enumerated-media-devices', {
62+
// error: `${error}`
63+
// });
64+
// });
65+
//});
6866

69-
contextBridge.exposeInMainWorld('PromptsPreload', {
70-
alert: (message) => ipcRenderer.sendSync('alert', message),
71-
confirm: (message) => ipcRenderer.sendSync('confirm', message),
72-
});
67+
//contextBridge.exposeInMainWorld('PromptsPreload', {
68+
// alert: (message) => ipcRenderer.sendSync('alert', message),
69+
// confirm: (message) => ipcRenderer.sendSync('confirm', message),
70+
//});
7371

7472
// In some Linux environments, people may try to drag & drop files that we don't have access to.
7573
// Remove when https://github.com/electron/electron/issues/30650 is fixed.
76-
if (navigator.userAgent.includes('Linux')) {
77-
document.addEventListener('drop', (e) => {
78-
if (e.isTrusted) {
79-
for (const file of e.dataTransfer.files) {
80-
// Using webUtils is safe as we don't have a legacy build for Linux
81-
const {webUtils} = require('electron');
82-
const path = webUtils.getPathForFile(file);
83-
ipcRenderer.invoke('check-drag-and-drop-path', path);
84-
}
85-
}
86-
}, {
87-
capture: true
88-
});
89-
}
74+
//if (navigator.userAgent.includes('Linux')) {
75+
// document.addEventListener('drop', (e) => {
76+
// if (e.isTrusted) {
77+
// for (const file of e.dataTransfer.files) {
78+
// // Using webUtils is safe as we don't have a legacy build for Linux
79+
// const {webUtils} = require('electron');
80+
// const path = webUtils.getPathForFile(file);
81+
// ipcRenderer.invoke('check-drag-and-drop-path', path);
82+
// }
83+
// }
84+
// }, {
85+
// capture: true
86+
// });
87+
//}

android/app/src/main/assets/preload/ipc-init.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
const require = (function() {
2+
let globalIpcCounter = 0;
3+
const ipcInFlight = {};
4+
5+
AndroidIpcAsync.onmessage = (e) => {
6+
const data = e.data;
7+
const ipcObject = ipcInFlight[data.messageId];
8+
if (!ipcObject) {
9+
throw new Error(`Received async IPC with unknown ID ${data.messageId}`);
10+
}
11+
12+
if (data.success) {
13+
ipcObject.resolve(data.result);
14+
} else {
15+
ipcObject.reject(data.result);
16+
}
17+
18+
delete ipcObject[data.messageId];
19+
};
20+
221
const contextBridge = {
322
exposeInMainWorld: (objectName, objectImplementation) => {
423
window[objectName] = objectImplementation;
@@ -13,7 +32,22 @@ const require = (function() {
1332
}));
1433
return JSON.parse(response);
1534
},
16-
invoke: (method, ...args) => {}
35+
36+
invoke: (method, ...args) => new Promise((resolve, reject) => {
37+
const messageId = globalIpcCounter++;
38+
ipcInFlight[messageId] = {
39+
resolve,
40+
reject
41+
};
42+
43+
console.log('Sending', method, args);
44+
45+
AndroidIpcAsync.postMessage({
46+
messageId,
47+
method,
48+
arguments: args
49+
});
50+
}),
1751
};
1852

1953
return (moduleName) => {
20.3 KB
Loading
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.turbowarp.android
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.material3.Surface
7+
import androidx.compose.material3.Text
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Alignment
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.graphics.Color
12+
import androidx.compose.ui.text.style.TextAlign
13+
import androidx.compose.ui.unit.sp
14+
15+
@Composable
16+
fun AboutView() {
17+
Surface(
18+
modifier = Modifier.fillMaxSize(),
19+
color = Color(0xffff4c4c)
20+
) {
21+
Column (
22+
modifier = Modifier.fillMaxSize(),
23+
horizontalAlignment = Alignment.CenterHorizontally,
24+
verticalArrangement = Arrangement.Center
25+
) {
26+
Text(
27+
text = "TurboWarp for Android",
28+
fontSize = 30.sp,
29+
color = Color.White,
30+
textAlign = TextAlign.Center
31+
)
32+
Text(
33+
text = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME}",
34+
fontSize = 20.sp,
35+
color = Color.White,
36+
textAlign = TextAlign.Center
37+
)
38+
}
39+
}
40+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.turbowarp.android
2+
3+
import androidx.compose.runtime.Composable
4+
5+
@Composable
6+
fun AddonsView() {
7+
TurboWarpWebView(
8+
url = "https://editor.android-assets.turbowarp.org/addons/addons.html",
9+
)
10+
}

android/app/src/main/java/org/turbowarp/android/EditorView.kt

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
package org.turbowarp.android
22

3+
import android.os.Handler
4+
import android.os.Looper
5+
import androidx.compose.foundation.layout.Box
36
import androidx.compose.runtime.Composable
7+
import androidx.navigation.compose.NavHost
8+
import androidx.navigation.compose.composable
9+
import androidx.navigation.compose.rememberNavController
410
import org.json.JSONArray
511
import org.json.JSONObject
612

@@ -14,7 +20,9 @@ fun mapToJsonObject(map: Map<String, String>): JSONObject {
1420

1521
@Composable
1622
fun EditorView() {
17-
TurboWarpWebView(
23+
val navController = rememberNavController()
24+
25+
val editor = TurboWarpWebView(
1826
url = "https://editor.android-assets.turbowarp.org/gui/gui.html",
1927
preloads = listOf(
2028
"editor.js",
@@ -32,12 +40,50 @@ fun EditorView() {
3240
val result = JSONObject()
3341
val strings = L10N.getStrings()
3442
result.put("strings", mapToJsonObject(strings))
35-
println(result)
3643
return result
3744
}
3845

46+
if (method == "open-about") {
47+
Handler(Looper.getMainLooper()).post {
48+
navController.navigate("about")
49+
}
50+
return null
51+
}
52+
53+
if (method == "open-addon-settings") {
54+
Handler(Looper.getMainLooper()).post {
55+
navController.navigate("addons")
56+
}
57+
return null
58+
}
59+
60+
if (method == "open-privacy") {
61+
Handler(Looper.getMainLooper()).post {
62+
navController.navigate("privacy")
63+
}
64+
return null
65+
}
66+
3967
return null
4068
}
4169
}
4270
)
71+
72+
Box {
73+
editor
74+
NavHost(navController = navController, startDestination = "none") {
75+
composable("none") {
76+
// empty
77+
}
78+
composable("about") {
79+
AboutView()
80+
}
81+
composable("addons") {
82+
AddonsView()
83+
}
84+
composable("privacy") {
85+
PrivacyView()
86+
}
87+
}
88+
}
4389
}

android/app/src/main/java/org/turbowarp/android/MainActivity.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
66
import androidx.activity.enableEdgeToEdge
7+
import androidx.compose.foundation.layout.Box
8+
import androidx.compose.foundation.layout.safeDrawingPadding
9+
import androidx.compose.ui.Modifier
10+
import androidx.navigation.compose.rememberNavController
711
import androidx.webkit.WebViewFeature
812
import org.turbowarp.android.ui.theme.TurboWarpTheme
913

@@ -19,7 +23,9 @@ class MainActivity : ComponentActivity() {
1923
L10N.setup(applicationContext)
2024
setContent {
2125
TurboWarpTheme {
22-
EditorView()
26+
Box(modifier = Modifier.safeDrawingPadding()) {
27+
EditorView()
28+
}
2329
}
2430
}
2531
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.turbowarp.android
2+
3+
import androidx.compose.foundation.layout.fillMaxSize
4+
import androidx.compose.material3.Surface
5+
import androidx.compose.material3.Text
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
8+
import androidx.compose.ui.graphics.Color
9+
10+
@Composable
11+
fun PrivacyView() {
12+
Surface(
13+
modifier = Modifier.fillMaxSize(),
14+
color = Color.White
15+
) {
16+
Text("Testing")
17+
}
18+
}

android/app/src/main/java/org/turbowarp/android/TurboWarpWebView.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private class ServeBrotliAsset(
8585
val stream = readBrotliAssetAsStream(context, compressedAssetPath)
8686
makeFetchableResponse(stream, pathWithIndex)
8787
} catch (_: IOException) {
88-
// TODO: does this fall-through to remote or fallthrough?
88+
// TODO: does this fall-through to remote or error?
8989
null
9090
}
9191
}

0 commit comments

Comments
 (0)