mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
More WIP
This commit is contained in:
parent
2426fe6214
commit
91809787eb
@ -31,8 +31,11 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
@ -42,6 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.btle.BtLEQueue;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
|
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.RemoteFileSystemCache;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.ZipFile;
|
import nodomain.freeyourgadget.gadgetbridge.util.ZipFile;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.util.ZipFileException;
|
import nodomain.freeyourgadget.gadgetbridge.util.ZipFileException;
|
||||||
@ -64,6 +68,9 @@ public class AdaBleFsProfile<T extends AbstractBTLEDeviceSupport> extends Abstra
|
|||||||
|
|
||||||
final byte STATUS_OK = 0x01;
|
final byte STATUS_OK = 0x01;
|
||||||
|
|
||||||
|
private enum TriState {TRUE, FALSE, UNKNOWN};
|
||||||
|
|
||||||
|
|
||||||
static final UUID UUID_SERVICE_FS = UUID.fromString("0000febb-0000-1000-8000-00805f9b34fb");
|
static final UUID UUID_SERVICE_FS = UUID.fromString("0000febb-0000-1000-8000-00805f9b34fb");
|
||||||
static final UUID UUID_CHARACTERISTIC_FS_VERSION = UUID.fromString("adaf0100-4669-6c65-5472-616e73666572");
|
static final UUID UUID_CHARACTERISTIC_FS_VERSION = UUID.fromString("adaf0100-4669-6c65-5472-616e73666572");
|
||||||
static final public UUID UUID_CHARACTERISTIC_FS_TRANSFER = UUID.fromString("adaf0200-4669-6c65-5472-616e73666572");
|
static final public UUID UUID_CHARACTERISTIC_FS_TRANSFER = UUID.fromString("adaf0200-4669-6c65-5472-616e73666572");
|
||||||
@ -76,12 +83,17 @@ public class AdaBleFsProfile<T extends AbstractBTLEDeviceSupport> extends Abstra
|
|||||||
int locationToWriteTo;
|
int locationToWriteTo;
|
||||||
int chunkSize;
|
int chunkSize;
|
||||||
|
|
||||||
|
private RemoteFileSystemCache remoteFs;
|
||||||
|
|
||||||
|
List<String> listingDirectory;
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AdaBleFsProfile.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AdaBleFsProfile.class);
|
||||||
|
|
||||||
public AdaBleFsProfile(T support) {
|
public AdaBleFsProfile(T support) {
|
||||||
super(support);
|
super(support);
|
||||||
adaBleFsQueue = new LinkedList<>();
|
adaBleFsQueue = new LinkedList<>();
|
||||||
chunkSize = 20; // Default MTU for android, I believe?
|
chunkSize = 20; // Default MTU for android, I believe?
|
||||||
|
remoteFs = new RemoteFileSystemCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadResources(Uri uri, Context context, BtLEQueue queue) {
|
public void loadResources(Uri uri, Context context, BtLEQueue queue) {
|
||||||
@ -221,12 +233,55 @@ public class AdaBleFsProfile<T extends AbstractBTLEDeviceSupport> extends Abstra
|
|||||||
for(int counter = 0; counter < length; counter++) {
|
for(int counter = 0; counter < length; counter++) {
|
||||||
stringBytes[counter] = returned[28+counter];
|
stringBytes[counter] = returned[28+counter];
|
||||||
}
|
}
|
||||||
// Just throwing away this path?
|
Map<String, Object> relativeRoot = directoryTree;
|
||||||
|
for(String path: listingDirectory) {
|
||||||
|
relativeRoot = (Map<String, Object>) relativeRoot.get(path);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
String path = new String(stringBytes, "UTF-8");
|
String path = new String(stringBytes, "UTF-8");
|
||||||
|
Map<String, Object> here = directoryTree; // TODO Paths returned are relative to where we requested paths from so we need to alter this
|
||||||
|
// TODO Remove first / ?
|
||||||
|
// Split path on /
|
||||||
|
String soFar = "";
|
||||||
|
final String[] paths = path.split("/");
|
||||||
|
for(int counter = 0; counter < paths.length; counter++) {
|
||||||
|
final String dir = paths[counter];
|
||||||
|
soFar = soFar + "/" + dir;
|
||||||
|
if (here.containsKey(dir)) {
|
||||||
|
Object entry = here.get(dir);
|
||||||
|
if (counter < (paths.length-1) || isDirectory) {
|
||||||
|
// If not at last entry in paths, or if the response says this is a directory
|
||||||
|
if (entry instanceof HashMap) {
|
||||||
|
// All good, continue;
|
||||||
|
} else {
|
||||||
|
// our map doesn't list directory, but this is?
|
||||||
|
LOG.warn("GadgetBridge cache thinks " + soFar + " is a file, but device thinks it's a directory.");
|
||||||
|
here.put(dir, new HashMap<String, Object>());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We are at the last string in paths, and response says this isn't a file
|
||||||
|
if (entry instanceof String) {
|
||||||
|
// All good, continue
|
||||||
|
} else {
|
||||||
|
LOG.warn("GadgetBridge cache thinks " + soFar + " is a directory, but device thinks it's a file.");
|
||||||
|
here.put(dir, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Our cache doesn't know of soFar
|
||||||
|
if (counter < (paths.length-1) || isDirectory) {
|
||||||
|
here.put(dir, new HashMap<String, Object>());
|
||||||
|
} else {
|
||||||
|
here.put(dir, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
// Pretty sure this branch will never happen
|
// Pretty sure this branch will never happen
|
||||||
}
|
}
|
||||||
|
if (entryNum == totalEntries) {
|
||||||
|
relativeRoot.put(".", ".");
|
||||||
|
}
|
||||||
return entryNum == totalEntries;
|
return entryNum == totalEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +362,41 @@ public class AdaBleFsProfile<T extends AbstractBTLEDeviceSupport> extends Abstra
|
|||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void uploadFileInitialise() {
|
||||||
|
final AdaBleFsAction nextAction = adaBleFsQueue.getFirst();
|
||||||
|
// Check if nextAction.filenameorpath directory exists
|
||||||
|
// get directory
|
||||||
|
// check directory exists, including parents
|
||||||
|
// remove file if it already exists
|
||||||
|
// then uploadFileStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriState directoryExists(String[] directoryList) {
|
||||||
|
Map<String, Object> here = directoryTree;
|
||||||
|
String[] soFar;
|
||||||
|
String totalPath = "/";
|
||||||
|
for(String directory: directoryList) {
|
||||||
|
if (here.containsKey(directory)) {
|
||||||
|
Object entry = here.get(directory);
|
||||||
|
if (entry instanceof HashMap) {
|
||||||
|
here = (Map<String, Object>) entry;
|
||||||
|
totalPath += directory + "/";
|
||||||
|
} else {
|
||||||
|
// Not a HashMap, must be a string then and so this is a file, not a directory
|
||||||
|
return TriState.FALSE;
|
||||||
|
}
|
||||||
|
} else if (here.containsKey(".")) {
|
||||||
|
// We have listed this directory, and the requested path does not exist
|
||||||
|
return TriState.FALSE;
|
||||||
|
}
|
||||||
|
// Request directory listing for here
|
||||||
|
totalPath += directory;
|
||||||
|
requestListDirectory(totalPath);
|
||||||
|
return TriState.UNKNOWN;
|
||||||
|
}
|
||||||
|
return TriState.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
private void uploadFileStart() {
|
private void uploadFileStart() {
|
||||||
bytesOfFileWritten = 0;
|
bytesOfFileWritten = 0;
|
||||||
// TODO Should this be the time we upload, or should it somehow be the timestamp of the
|
// TODO Should this be the time we upload, or should it somehow be the timestamp of the
|
||||||
@ -346,15 +436,14 @@ public class AdaBleFsProfile<T extends AbstractBTLEDeviceSupport> extends Abstra
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestListDirectory() {
|
private void requestListDirectory(String path) {
|
||||||
final AdaBleFsAction nextAction = adaBleFsQueue.getFirst();
|
|
||||||
Vector<Byte> command = new Vector<Byte>();
|
Vector<Byte> command = new Vector<Byte>();
|
||||||
command.add(REQUEST_LIST_DIRECTORY);
|
command.add(REQUEST_LIST_DIRECTORY);
|
||||||
command.add(PADDING_BYTE);
|
command.add(PADDING_BYTE);
|
||||||
command.add((byte) (nextAction.filenameorpath.length() & 0xFF)); // 16-bit path length
|
command.add((byte) (path.length() & 0xFF)); // 16-bit path length
|
||||||
command.add((byte) ((nextAction.filenameorpath.length() >> 8) & 0xFF));
|
command.add((byte) ((path.length() >> 8) & 0xFF));
|
||||||
for(int count = 0; count < nextAction.filenameorpath.length(); count++) {
|
for(int count = 0; count < path.length(); count++) {
|
||||||
command.add((byte) nextAction.filenameorpath.charAt(count));
|
command.add((byte) path.charAt(count));
|
||||||
}
|
}
|
||||||
// send command, wait for response
|
// send command, wait for response
|
||||||
// Is there a better way to construct a bytes[] ?
|
// Is there a better way to construct a bytes[] ?
|
||||||
@ -366,6 +455,7 @@ public class AdaBleFsProfile<T extends AbstractBTLEDeviceSupport> extends Abstra
|
|||||||
builder.write(getCharacteristic(UUID_CHARACTERISTIC_FS_TRANSFER), bytes);
|
builder.write(getCharacteristic(UUID_CHARACTERISTIC_FS_TRANSFER), bytes);
|
||||||
builder.read(getCharacteristic(UUID_CHARACTERISTIC_FS_TRANSFER));
|
builder.read(getCharacteristic(UUID_CHARACTERISTIC_FS_TRANSFER));
|
||||||
builder.queue(getQueue());
|
builder.queue(getQueue());
|
||||||
|
listingDirectory = Arrays.asList(path.split("/"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
private void deleteFile() {
|
private void deleteFile() {
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
/* Copyright (C) 2016-2021 Andreas Shimokawa, Carsten Pfeiffer, João
|
||||||
|
Paulo Barraca, JohnnySun
|
||||||
|
|
||||||
|
This file is part of Gadgetbridge.
|
||||||
|
|
||||||
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Gadgetbridge is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RemoteFileSystemCache {
|
||||||
|
|
||||||
|
private Directory root;
|
||||||
|
|
||||||
|
public RemoteFileSystemCache() {
|
||||||
|
root = new Directory("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFile(String full_path) {
|
||||||
|
root.addFile(full_path.substring(1)); // skip first slash
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDirectory(String full_path) {
|
||||||
|
root.addDirectory(full_path.substring(1)); // skip first slash
|
||||||
|
}
|
||||||
|
public boolean hasFile(String full_path) {
|
||||||
|
return root.hasFile(full_path);
|
||||||
|
}
|
||||||
|
private class Directory {
|
||||||
|
String name;
|
||||||
|
List<String> files;
|
||||||
|
List<Directory> directories;
|
||||||
|
|
||||||
|
Directory(String name) {
|
||||||
|
files = new ArrayList<>();
|
||||||
|
directories = new ArrayList<>();
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFile(String filename) {
|
||||||
|
int indexOfSlash = filename.indexOf('/');
|
||||||
|
if (indexOfSlash > 0) {
|
||||||
|
Directory nextDirectory = getDirectory(filename.substring(0, indexOfSlash)).get();
|
||||||
|
nextDirectory.addFile(filename.substring(indexOfSlash + 1));
|
||||||
|
} else {
|
||||||
|
files.add(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addDirectory(String name) {
|
||||||
|
int indexOfSlash = name.indexOf('/');
|
||||||
|
if (indexOfSlash > 0) {
|
||||||
|
Directory nextDirectory = getDirectory(name.substring(0, indexOfSlash)).get();
|
||||||
|
nextDirectory.addFile(name.substring(indexOfSlash + 1));
|
||||||
|
} else {
|
||||||
|
directories.add(new Directory(name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasFile(String full_path) {
|
||||||
|
// Remove leading slash, it may be left if we are root
|
||||||
|
String path;
|
||||||
|
if (full_path.charAt(0) == '/') {
|
||||||
|
path = full_path.substring(1);
|
||||||
|
} else {
|
||||||
|
path = full_path;
|
||||||
|
}
|
||||||
|
int indexOfSlash = path.indexOf('/');
|
||||||
|
if (indexOfSlash >= 0) {
|
||||||
|
Optional<Directory> nextDirectory = getDirectory(path.substring(0, indexOfSlash));
|
||||||
|
if (nextDirectory.isPresent()) {
|
||||||
|
String remains = path.substring(indexOfSlash + 1); // Skip past slash
|
||||||
|
return nextDirectory.get().hasFile(remains);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return files.contains(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<Directory> getDirectory(String name) {
|
||||||
|
for(Directory directory: directories) {
|
||||||
|
if (directory.name == name) {
|
||||||
|
return Optional.of(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user