Skip to content

Commit f6e8b08

Browse files
committed
ANDROID: file manager webui (wip)
1 parent cd6df0e commit f6e8b08

File tree

4 files changed

+75
-34
lines changed

4 files changed

+75
-34
lines changed

src/platform/android/app/src/main/java/net/sourceforge/smallbasic/MainActivity.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,7 @@ protected Response getFile(String path, boolean asset) throws IOException {
958958
if (file != null) {
959959
result = new Response(new FileInputStream(file), file.length());
960960
} else {
961-
throw new IOException("file not found");
961+
throw new IOException("File not found: " + path);
962962
}
963963
}
964964
return result;
@@ -985,21 +985,21 @@ protected void log(String message) {
985985
@Override
986986
protected void renameFile(String from, String to) throws IOException {
987987
if (to == null || !to.endsWith(".bas")) {
988-
throw new IOException("Invalid New File Name: " + to);
988+
throw new IOException("Invalid file name: " + to);
989989
}
990990
File toFile = getFile(getInternalStorage(), to);
991991
if (toFile == null) {
992992
toFile = getFile(getExternalStorage(), to);
993993
}
994994
if (toFile != null) {
995-
throw new IOException("New File Name already exists");
995+
throw new IOException("File already exists");
996996
}
997997
File fromFile = getFile(getInternalStorage(), from);
998998
if (fromFile == null) {
999999
fromFile = getFile(getExternalStorage(), from);
10001000
}
10011001
if (fromFile == null) {
1002-
throw new IOException("Old File Name does not exist");
1002+
throw new IOException("Previous file does not exist");
10031003
}
10041004
if (!fromFile.renameTo(new File(getExternalStorage(), to))) {
10051005
throw new IOException("File rename failed");
@@ -1010,9 +1010,9 @@ protected void renameFile(String from, String to) throws IOException {
10101010
protected void saveFile(String fileName, byte[] content) throws IOException {
10111011
File file = new File(getExternalStorage(), fileName);
10121012
if (file.exists()) {
1013-
throw new IOException("File already exists");
1013+
throw new IOException("File already exists: " + fileName);
10141014
} else if (file.isDirectory()) {
1015-
throw new IOException("Invalid File Name: " + fileName);
1015+
throw new IOException("Invalid file name: " + fileName);
10161016
}
10171017
copy(new ByteArrayInputStream(content), new FileOutputStream(file));
10181018
}

src/platform/android/app/src/main/java/net/sourceforge/smallbasic/WebServer.java

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package net.sourceforge.smallbasic;
22

3+
import javax.xml.ws.Response;
34
import java.io.BufferedOutputStream;
45
import java.io.ByteArrayInputStream;
56
import java.io.ByteArrayOutputStream;
@@ -69,23 +70,28 @@ public void run() {
6970
*/
7071
private Response handleDownload(Map<String, Collection<String>> data) throws IOException {
7172
Collection<String> fileNames = data.get("f");
72-
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
73-
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
74-
if (fileNames == null) {
75-
fileNames = Collections.emptyList();
76-
}
77-
78-
for (String fileName : fileNames) {
79-
Response response = getFile(fileName, false);
80-
ZipEntry entry = new ZipEntry(fileName);
81-
zipOutputStream.putNextEntry(entry);
82-
response.toStream(zipOutputStream);
83-
zipOutputStream.closeEntry();
73+
Response result;
74+
if (fileNames == null || fileNames.size() == 0) {
75+
result = handleStatus(false, "File list is empty");
76+
} else if (fileNames.size() == 1) {
77+
// plain text download single file
78+
result = getFile(fileNames.iterator().next(), false);
79+
} else {
80+
// download multiple as zip
81+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
82+
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
83+
for (String fileName : fileNames) {
84+
Response response = getFile(fileName, false);
85+
ZipEntry entry = new ZipEntry(fileName);
86+
zipOutputStream.putNextEntry(entry);
87+
response.toStream(zipOutputStream);
88+
zipOutputStream.closeEntry();
89+
}
90+
zipOutputStream.finish();
91+
zipOutputStream.close();
92+
result = new Response(new ByteArrayInputStream(outputStream.toByteArray()), outputStream.size());
8493
}
85-
86-
zipOutputStream.finish();
87-
zipOutputStream.close();
88-
return new Response(new ByteArrayInputStream(outputStream.toByteArray()), outputStream.size());
94+
return result;
8995
}
9096

9197
/**

src/platform/android/webui/server/src/main/java/net/sourceforge/smallbasic/Server.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import sun.misc.IOUtils;
44

5+
import java.io.ByteArrayInputStream;
56
import java.io.File;
7+
import java.io.FileOutputStream;
68
import java.io.IOException;
79
import java.io.InputStream;
10+
import java.io.OutputStream;
811
import java.nio.file.Files;
912
import java.nio.file.attribute.BasicFileAttributes;
1013
import java.nio.file.attribute.FileTime;
@@ -14,7 +17,7 @@
1417
import java.util.Objects;
1518

1619
public class Server {
17-
private static final String BASIC_HOME = "../basic";
20+
private static final String BASIC_HOME = "../basic/";
1821

1922
public static void main(String[] args ) {
2023
// ln -s ../../../../../../../../app/src/main/java/net/sourceforge/smallbasic/WebServer.java .
@@ -69,15 +72,15 @@ protected void log(String message, Exception exception) {
6972
@Override
7073
protected void renameFile(String from, String to) throws IOException {
7174
if (to == null || !to.endsWith(".bas")) {
72-
throw new IOException("Invalid New File Name: " + to);
75+
throw new IOException("Invalid File Name: " + to);
7376
}
7477
File toFile = new File(BASIC_HOME, to);
7578
if (toFile.exists()) {
76-
throw new IOException("New File Name already exists");
79+
throw new IOException("File Name already exists");
7780
}
7881
File fromFile = new File(BASIC_HOME, from);
7982
if (!fromFile.exists()) {
80-
throw new IOException("Old File Name does not exist");
83+
throw new IOException("Previous File Name does not exist");
8184
}
8285
if (!fromFile.renameTo(new File(BASIC_HOME, to))) {
8386
throw new IOException("File rename failed");
@@ -86,9 +89,27 @@ protected void renameFile(String from, String to) throws IOException {
8689

8790
@Override
8891
protected void saveFile(String fileName, byte[] content) throws IOException {
89-
throw new IOException("Failed to save file: " + fileName);
92+
File file = new File(BASIC_HOME, fileName);
93+
if (file.exists()) {
94+
throw new IOException("File already exists: " + fileName);
95+
} else if (file.isDirectory()) {
96+
throw new IOException("Invalid File Name: " + fileName);
97+
}
98+
copy(new ByteArrayInputStream(content), Files.newOutputStream(file.toPath()));
9099
}
91100

101+
private void copy(InputStream in, OutputStream out) throws IOException {
102+
try {
103+
byte[] buf = new byte[1024];
104+
int len;
105+
while ((len = in.read(buf)) > 0) {
106+
out.write(buf, 0, len);
107+
}
108+
} finally {
109+
out.close();
110+
in.close();
111+
}
112+
}
92113
};
93114
webServer.run(8080, "ABC123");
94115
}

src/platform/android/webui/src/App.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import {
1818

1919
import {
2020
DataGrid,
21+
GridToolbarColumnsButton,
2122
GridToolbarContainer,
22-
GridToolbarFilterButton,
2323
GridToolbarDensitySelector,
24+
GridToolbarExport,
25+
GridToolbarFilterButton,
2426
} from '@mui/x-data-grid';
2527

2628
import {
@@ -113,14 +115,15 @@ function GridToolbarDownload(props) {
113115
let color = useTheme().palette.primary.main;
114116
let args = "";
115117
let join = "";
118+
let download = props.selections.length === 1 ? props.rows[props.selections[0]].fileName : "download.zip";
116119

117120
props.selections.forEach(i => {
118121
args += join + "f=" + encodeURIComponent(props.rows[i].fileName);
119122
join = "&";
120123
});
121124

122125
return !props.selections.length ? null : (
123-
<a download="download.zip" href={`./api/download?${args}`} style={{color: color, textDecoration: 'none'}}>
126+
<a download={download} href={`./api/download?${args}`} style={{color: color, textDecoration: 'none'}}>
124127
<Box sx={{display: 'flex', marginTop: '1px', alignItems: 'center'}} >
125128
<DownloadIcon />
126129
<Typography variant="caption">
@@ -139,8 +142,8 @@ function ErrorMessage(props) {
139142
};
140143

141144
return (
142-
<Snackbar open={props.error !== null} autoHideDuration={6000} onClose={handleClose}>
143-
<Alert onClose={handleClose} severity="error" sx={{width: '100%'}}>
145+
<Snackbar open={props.error !== null} autoHideDuration={5000} onClose={handleClose}>
146+
<Alert onClose={handleClose} severity={props.severity} sx={{width: '100%'}}>
144147
{props.error}
145148
</Alert>
146149
</Snackbar>
@@ -149,18 +152,27 @@ function ErrorMessage(props) {
149152

150153
function GridToolbarUpload(props) {
151154
const [error, setError] = useState(null);
155+
const [severity, setSeverity] = useState("error");
152156

153157
const handleUpload = (event) => {
158+
const length = event.target.files.length;
159+
setSeverity("error");
154160
copyFiles(event, (newRows) => {
155161
props.setRows(newRows);
162+
setSeverity("success");
163+
setError(`Uploaded ${length} files`);
156164
}, (error) => {
157165
setError(error);
166+
// show any successful uploads
167+
getFiles((newRows) => {
168+
props.setRows(newRows);
169+
}, (error)=> setError(error));
158170
});
159171
};
160172

161173
return (
162174
<Fragment>
163-
<ErrorMessage error={error} setError={setError} />
175+
<ErrorMessage error={error} setError={setError} severity={severity}/>
164176
<Button color="primary" size="small" component="label" sx={{marginLeft: '-4px'}}>
165177
<input accept=".bas" hidden multiple type="file" onChange={handleUpload}/>
166178
<UploadIcon />
@@ -174,6 +186,8 @@ function AppToolbar(props) {
174186
return (
175187
<GridToolbarContainer>
176188
<GridToolbarFilterButton />
189+
<GridToolbarColumnsButton />
190+
<GridToolbarExport />
177191
<GridToolbarDensitySelector />
178192
<GridToolbarUpload {...props}/>
179193
<GridToolbarDownload {...props}/>
@@ -211,7 +225,7 @@ function FileList(props) {
211225
<DataGrid rows={props.rows}
212226
columns={columns}
213227
onCellEditCommit={(params) => onCellEditCommit(props, params, setError)}
214-
pageSize={5}
228+
autoPageSize
215229
components={{Toolbar: AppToolbar}}
216230
componentsProps={{toolbar: toolbarProps}}
217231
onSelectionModelChange={(model) => setSelectionModel(model)}
@@ -295,7 +309,7 @@ export default function App() {
295309
<Box>
296310
<Link target="new" href="https://smallbasic.github.io" color="inherit">
297311
<Typography variant="h6" component="div" sx={{flexGrow: 1}}>
298-
https://smallbasic.github.io
312+
smallbasic.github.io
299313
</Typography>
300314
</Link>
301315
</Box>

0 commit comments

Comments
 (0)