moar fileshare plz

This commit is contained in:
erinn 2021-09-29 13:31:01 -07:00
parent cb3c161277
commit 8fe577afd4
10 changed files with 107 additions and 53 deletions

View File

@ -194,6 +194,11 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
Log.i("FlwtchWorker::DownloadFile", "DownloadFile("+filepath+", "+manifestpath+")")
Cwtch.downloadFile(profile, handle, filepath, manifestpath, filekey)
}
"CheckDownloadStatus" -> {
val profile = (a.get("ProfileOnion") as? String) ?: ""
val fileKey = (a.get("fileKey") as? String) ?: ""
Cwtch.checkDownloadStatus(profile, fileKey)
}
"SendProfileEvent" -> {
val onion = (a.get("onion") as? String) ?: ""
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""

View File

@ -46,6 +46,8 @@ abstract class Cwtch {
void DownloadFile(String profile, String handle, String filepath, String manifestpath, String filekey);
// ignore: non_constant_identifier_names
void CreateDownloadableFile(String profile, String handle, String filenameSuggestion, String filekey);
// ignore: non_constant_identifier_names
void CheckDownloadStatus(String profile, String fileKey);
// ignore: non_constant_identifier_names
void ArchiveConversation(String profile, String handle);

View File

@ -330,7 +330,7 @@ class CwtchNotifier {
profileCN.getProfile(data["ProfileOnion"])?.downloadUpdate(data["FileKey"], int.parse(data["Progress"]));
break;
case "FileDownloaded":
profileCN.getProfile(data["ProfileOnion"])?.downloadMarkFinished(data["FileKey"]);
profileCN.getProfile(data["ProfileOnion"])?.downloadMarkFinished(data["FileKey"], data["FilePath"]);
break;
default:
EnvironmentConfig.debugLog("unhandled event: $type");

View File

@ -397,6 +397,19 @@ class CwtchFfi implements Cwtch {
// android only - do nothing
}
@override
// ignore: non_constant_identifier_names
void CheckDownloadStatus(String profileOnion, String fileKey) {
var checkDownloadStatus = library.lookup<NativeFunction<string_string_to_void_function>>("c_CheckDownloadStatus");
// ignore: non_constant_identifier_names
final CheckDownloadStatus = checkDownloadStatus.asFunction<VoidFromStringStringFn>();
final u1 = profileOnion.toNativeUtf8();
final u2 = fileKey.toNativeUtf8();
CheckDownloadStatus(u1, u1.length, u2, u2.length);
malloc.free(u1);
malloc.free(u2);
}
@override
// ignore: non_constant_identifier_names
void ResetTor() {

View File

@ -148,6 +148,12 @@ class CwtchGomobile implements Cwtch {
cwtchPlatform.invokeMethod("CreateDownloadableFile", {"ProfileOnion": profileOnion, "handle": contactHandle, "filename": filenameSuggestion, "filekey": filekey});
}
@override
// ignore: non_constant_identifier_names
void CheckDownloadStatus(String profileOnion, String fileKey) {
cwtchPlatform.invokeMethod("CheckDownloadStatus", {"ProfileOnion": profileOnion, "fileKey": fileKey});
}
@override
// ignore: non_constant_identifier_names
void ResetTor() {

View File

@ -381,14 +381,17 @@ class ProfileInfoState extends ChangeNotifier {
}
}
void downloadMarkFinished(String fileKey) {
void downloadMarkFinished(String fileKey, String finalPath) {
if (!downloadActive(fileKey)) {
print("error: received download completion notice for unknown download "+fileKey);
} else {
this._downloads[fileKey]!.timeEnd = DateTime.now();
this._downloads[fileKey]!.complete = true;
notifyListeners();
// happens as a result of a CheckDownloadStatus call,
// invoked from a historical (timeline) download message
// so setting numChunks correctly shouldn't matter
this.downloadInit(fileKey, 1);
}
this._downloads[fileKey]!.timeEnd = DateTime.now();
this._downloads[fileKey]!.downloadedTo = finalPath;
this._downloads[fileKey]!.complete = true;
notifyListeners();
}
bool downloadActive(String fileKey) {
@ -407,8 +410,12 @@ class ProfileInfoState extends ChangeNotifier {
return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.progress() : 0.0;
}
String? downloadFinalPath(String fileKey) {
return this._downloads.containsKey(fileKey) ? this._downloads[fileKey]!.downloadedTo : null;
}
String downloadSpeed(String fileKey) {
if (!downloadActive(fileKey)) {
if (!downloadActive(fileKey) || this._downloads[fileKey]!.chunksDownloaded == 0) {
return "0 B/s";
}
var bytes = this._downloads[fileKey]!.chunksDownloaded * 4096;
@ -425,6 +432,7 @@ class FileDownloadProgress {
int chunksTotal = 1;
bool complete = false;
bool gotManifest = false;
String? downloadedTo;
DateTime? timeStart;
DateTime? timeEnd;

View File

@ -35,38 +35,39 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, String
try {
var rawMessageEnvelopeFuture = Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, index);
return rawMessageEnvelopeFuture.then((dynamic rawMessageEnvelope) {
dynamic messageWrapper = jsonDecode(rawMessageEnvelope);
// There are 2 conditions in which this error condition can be met:
// 1. The application == nil, in which case this instance of the UI is already
// broken beyond repair, and will either be replaced by a new version, or requires a complete
// restart.
// 2. This index was incremented and we happened to fetch the timeline prior to the messages inclusion.
// This should be rare as Timeline addition/fetching is mutex protected and Dart itself will pipeline the
// calls to libCwtch-go - however because we use goroutines on the backend there is always a chance that one
// will find itself delayed.
// The second case is recoverable by tail-recursing this future.
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
return Future.delayed(Duration(seconds: 2), () {
print("Tail recursive call to messageHandler called. This should be a rare event. If you see multiples of this log over a short period of time please log it as a bug.");
return messageHandler(context, profileOnion, contactHandle, index).then((value) => value);
});
}
// Construct the initial metadata
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
var senderHandle = messageWrapper['PeerID'];
var senderImage = messageWrapper['ContactImage'];
var flags = int.parse(messageWrapper['Flags'].toString(), radix: 2);
var ackd = messageWrapper['Acknowledged'];
var error = messageWrapper['Error'] != null;
String? signature;
// If this is a group, store the signature
if (contactHandle.length == GroupConversationHandleLength) {
signature = messageWrapper['Signature'];
}
var metadata = MessageMetadata(profileOnion, contactHandle, index, timestamp, senderHandle, senderImage, signature, flags, ackd, error);
var metadata = MessageMetadata(profileOnion, contactHandle, index, DateTime.now(), "", "", null, 0, false, true);
try {
dynamic messageWrapper = jsonDecode(rawMessageEnvelope);
// There are 2 conditions in which this error condition can be met:
// 1. The application == nil, in which case this instance of the UI is already
// broken beyond repair, and will either be replaced by a new version, or requires a complete
// restart.
// 2. This index was incremented and we happened to fetch the timeline prior to the messages inclusion.
// This should be rare as Timeline addition/fetching is mutex protected and Dart itself will pipeline the
// calls to libCwtch-go - however because we use goroutines on the backend there is always a chance that one
// will find itself delayed.
// The second case is recoverable by tail-recursing this future.
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
return Future.delayed(Duration(seconds: 2), () {
print("Tail recursive call to messageHandler called. This should be a rare event. If you see multiples of this log over a short period of time please log it as a bug.");
return messageHandler(context, profileOnion, contactHandle, index).then((value) => value);
});
}
// Construct the initial metadata
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
var senderHandle = messageWrapper['PeerID'];
var senderImage = messageWrapper['ContactImage'];
var flags = int.parse(messageWrapper['Flags'].toString());
var ackd = messageWrapper['Acknowledged'];
var error = messageWrapper['Error'] != null;
String? signature;
// If this is a group, store the signature
if (contactHandle.length == GroupConversationHandleLength) {
signature = messageWrapper['Signature'];
}
metadata = MessageMetadata(profileOnion, contactHandle, index, timestamp, senderHandle, senderImage, signature, flags, ackd, error);
dynamic message = jsonDecode(messageWrapper['Message']);
var content = message['d'] as dynamic;
var overlay = int.parse(message['o'].toString());
@ -86,6 +87,7 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, String
return MalformedMessage(metadata);
}
} catch (e) {
print("an error! " + e.toString());
return MalformedMessage(metadata);
}
});

View File

@ -39,7 +39,7 @@ class FileBubbleState extends State<FileBubble> {
@override
Widget build(BuildContext context) {
var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
//isAccepted = Provider.of<ProfileInfoState>(context).contactList.getContact(widget.inviteTarget) != null;
var flagStarted = Provider.of<MessageMetadata>(context).flags & 0x02 > 0;
var borderRadiousEh = 15.0;
var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment);
var prettyDate = DateFormat.yMd(Platform.localeName).add_jm().format(Provider.of<MessageMetadata>(context).timestamp);
@ -66,27 +66,40 @@ class FileBubbleState extends State<FileBubble> {
? senderFileChrome(
AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash, widget.fileSize)
: (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize, Provider.of<ProfileInfoState>(context).downloadSpeed(widget.fileKey())));
Widget wdgDecorations;
if (!showFileSharing) {
wdgDecorations = Text('\u202F');
} else if (fromMe) {
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, prettyDate: prettyDate);
} else if (Provider.of<ProfileInfoState>(context).downloadComplete(widget.fileKey())) {
wdgDecorations = Center(
widthFactor: 1,
child: Wrap(children: [
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.openFolderButton + '\u202F'), onPressed: _btnAccept)),
]));
// in this case, whatever marked download.complete would have also set the path
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey())!;
wdgDecorations = Text('Saved to: ' + path + '\u202F');
} else if (Provider.of<ProfileInfoState>(context).downloadActive(widget.fileKey())) {
if (!Provider.of<ProfileInfoState>(context).downloadGotManifest(widget.fileKey())) {
wdgDecorations = Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F');
if (!Provider.of<ProfileInfoState>(context).downloadGotManifest(
widget.fileKey())) {
wdgDecorations = Text(
AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F');
} else {
wdgDecorations = LinearProgressIndicator(
value: Provider.of<ProfileInfoState>(context).downloadProgress(widget.fileKey()),
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
value: Provider.of<ProfileInfoState>(context).downloadProgress(
widget.fileKey()),
color: Provider
.of<Settings>(context)
.theme
.defaultButtonActiveColor(),
);
}
} else if (flagStarted) {
// in this case, the download was done in a previous application launch,
// so we probably have to request an info lookup
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey());
if (path == null) {
wdgDecorations = Text('Checking download status...' + '\u202F');
Provider.of<FlwtchState>(context, listen: false).cwtch.CheckDownloadStatus(Provider.of<ProfileInfoState>(context, listen: false).onion, widget.fileKey());
} else {
wdgDecorations = Text('Saved to: ' + (path??"null") + '\u202F');
}
} else {
wdgDecorations = Center(
widthFactor: 1,
@ -135,10 +148,13 @@ class FileBubbleState extends State<FileBubble> {
File? file;
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
var handle = Provider.of<MessageMetadata>(context, listen: false).senderHandle;
var contact = Provider.of<ContactInfoState>(context, listen: false).onion;
var idx = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
if (Platform.isAndroid) {
//todo: would be better to only call downloadInit if CreateDownloadableFile results in a user-pick (they might cancel)
Provider.of<ProfileInfoState>(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil());
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x02);
Provider.of<MessageMetadata>(context, listen: false).flags |= 0x02;
Provider.of<FlwtchState>(context, listen: false).cwtch.CreateDownloadableFile(profileOnion, handle, widget.nameSuggestion, widget.fileKey());
} else {
try {
@ -148,6 +164,8 @@ class FileBubbleState extends State<FileBubble> {
print("saving to " + file.path);
var manifestPath = file.path + ".manifest";
Provider.of<ProfileInfoState>(context, listen: false).downloadInit(widget.fileKey(), (widget.fileSize / 4096).ceil());
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x02);
Provider.of<MessageMetadata>(context, listen: false).flags |= 0x02;
Provider.of<FlwtchState>(context, listen: false).cwtch.DownloadFile(profileOnion, handle, file.path, manifestPath, widget.fileKey());
}
} catch (e) {

View File

@ -131,7 +131,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
var contact = Provider.of<ContactInfoState>(context, listen: false).onion;
var idx = Provider.of<MessageMetadata>(context, listen: false).messageIndex;
Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateMessageFlags(profileOnion, contact, idx, Provider.of<MessageMetadata>(context, listen: false).flags | 0x01);
Provider.of<MessageMetadata>(context).flags |= 0x01;
Provider.of<MessageMetadata>(context, listen: false).flags |= 0x01;
});
}

View File

@ -87,7 +87,7 @@ class _MessageListState extends State<MessageList> {
// Already includes MessageRow,,
return message.getWidget(context);
} else {
return MessageLoadingBubble();
return Text('');//MessageLoadingBubble();
}
},
);