Merge pull request 'File sharing Manager + File Sharing Fixes' (#505) from filesharing-persist into trunk
continuous-integration/drone/push Build is passing Details

Reviewed-on: #505
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
This commit is contained in:
Sarah Jamie Lewis 2022-07-06 21:19:20 +00:00
commit 44fba12d21
26 changed files with 273 additions and 20 deletions

View File

@ -1 +1 @@
2022-06-27-15-00-v1.8.0
2022-07-06-15-49-v1.8.0-4-g4417347

View File

@ -1 +1 @@
2022-06-27-19-01-v1.8.0
2022-07-06-19-50-v1.8.0-4-g4417347

View File

@ -313,6 +313,7 @@ class MainActivity: FlutterActivity() {
result.success(Cwtch.sendInvitation(profile, conversation.toLong(), target.toLong()))
return
}
"ShareFile" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
@ -321,6 +322,27 @@ class MainActivity: FlutterActivity() {
return
}
"GetSharedFiles" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val conversation: Int = call.argument("conversation") ?: 0
result.success(Cwtch.getSharedFiles(profile, conversation.toLong()))
return
}
"RestartSharing" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val filepath: String = call.argument("filekey") ?: ""
result.success(Cwtch.restartSharing(profile, filepath))
return
}
"StopSharing" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val filepath: String = call.argument("filekey") ?: ""
result.success(Cwtch.stopSharing(profile, filepath))
return
}
"CreateProfile" -> {
val nick: String = call.argument("nick") ?: ""
val pass: String = call.argument("pass") ?: ""

View File

@ -59,6 +59,16 @@ abstract class Cwtch {
// ignore: non_constant_identifier_names
Future<dynamic> ShareFile(String profile, int handle, String filepath);
// ignore: non_constant_identifier_names
Future<dynamic> GetSharedFiles(String profile, int handle);
// ignore: non_constant_identifier_names
void RestartSharing(String profile, String filekey);
// ignore: non_constant_identifier_names
void StopSharing(String profile, String filekey);
// ignore: non_constant_identifier_names
void DownloadFile(String profile, int handle, String filepath, String manifestpath, String filekey);
// android-only

View File

@ -68,6 +68,9 @@ typedef GetJsonBlobFromStrIntStrFn = Pointer<Utf8> Function(Pointer<Utf8>, int,
typedef get_json_blob_from_str_str_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32);
typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int);
typedef get_json_blob_from_str_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Int32);
typedef GetJsonBlobFromStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, int);
typedef get_json_blob_from_str_int_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Int32, Int32);
typedef GetJsonBlobFromStrIntIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, int, int);
@ -841,4 +844,40 @@ class CwtchFfi implements Cwtch {
_UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(result);
return debugResult;
}
@override
Future<String> GetSharedFiles(String profile, int handle) async {
var getSharedFiles = library.lookup<NativeFunction<get_json_blob_from_str_int_function>>("c_GetSharedFiles");
final GetSharedFiles = getSharedFiles.asFunction<GetJsonBlobFromStrIntFn>();
final utf8profile = profile.toNativeUtf8();
Pointer<Utf8> jsonMessageBytes = GetSharedFiles(utf8profile, utf8profile.length, handle);
String jsonMessage = jsonMessageBytes.toDartString();
_UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(jsonMessageBytes);
malloc.free(utf8profile);
return jsonMessage;
}
@override
void RestartSharing(String profile, String filekey) {
var restartSharingC = library.lookup<NativeFunction<void_from_string_string_function>>("c_RestartSharing");
// ignore: non_constant_identifier_names
final RestartSharing = restartSharingC.asFunction<VoidFromStringStringFn>();
final utf8profile = profile.toNativeUtf8();
final ut8filekey = filekey.toNativeUtf8();
RestartSharing(utf8profile, utf8profile.length, ut8filekey, ut8filekey.length);
malloc.free(utf8profile);
malloc.free(ut8filekey);
}
@override
void StopSharing(String profile, String filekey) {
var stopSharingC = library.lookup<NativeFunction<void_from_string_string_function>>("c_StopSharing");
// ignore: non_constant_identifier_names
final StopSharing = stopSharingC.asFunction<VoidFromStringStringFn>();
final utf8profile = profile.toNativeUtf8();
final ut8filekey = filekey.toNativeUtf8();
StopSharing(utf8profile, utf8profile.length, ut8filekey, ut8filekey.length);
malloc.free(utf8profile);
malloc.free(ut8filekey);
}
}

View File

@ -333,4 +333,19 @@ class CwtchGomobile implements Cwtch {
// we don't implement it
return Future.value("{}");
}
@override
Future GetSharedFiles(String profile, int handle) {
return cwtchPlatform.invokeMethod("GetSharedFiles", {"ProfileOnion": profile, "conversation": handle});
}
@override
void RestartSharing(String profile, String filekey) {
cwtchPlatform.invokeMethod("RestartSharing", {"ProfileOnion": profile, "filekey": filekey});
}
@override
void StopSharing(String profile, String filekey) {
cwtchPlatform.invokeMethod("StopSharing", {"ProfileOnion": profile, "filekey": filekey});
}
}

View File

@ -1,6 +1,9 @@
{
"@@locale": "cy",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Almaeneg \/ Deutsch",
"localePt": "Portiwgaleg \/ Portuguesa",
"localeRo": "Rwmaneg \/ Română",

View File

@ -1,6 +1,9 @@
{
"@@locale": "da",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Tysk \/ Deutsch",
"localeEn": "Engelsk \/ English",
"localeFr": "Fransk \/ Français",

View File

@ -1,6 +1,9 @@
{
"@@locale": "de",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeEn": "Englisch \/ English",
"localePl": "Polnisch \/ Polski",
"localeIt": "Italienisch \/ Italiana",

View File

@ -1,6 +1,9 @@
{
"@@locale": "el",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Γερμανός \/ Deutsch",
"localeEn": "English \/ English",
"localeLb": "Λουξεμβουργιανά",

View File

@ -1,6 +1,9 @@
{
"@@locale": "en",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "German \/ Deutsch",
"localeEn": "English \/ English",
"localeLb": "Luxembourgish \/ Lëtzebuergesch",

View File

@ -1,6 +1,9 @@
{
"@@locale": "es",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Alemán \/ Deutsch",
"settingImagePreviewsDescription": "Las imágenes se descargarán y visualizarán automáticamente. Ten en cuenta que las previsualizaciones pueden generar vulnerabilidades de seguridad, no deberías habilitar este experimento si usas Cwtch con contactos que no son de confianza. Las imágenes de perfil están planeadas para Cwtch 1.6.",
"tooltipBackToMessageEditing": "Volver a Edición de mensajes",

View File

@ -1,6 +1,9 @@
{
"@@locale": "fr",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Allemand \/ Deutsch",
"localeDa": "Danois \/ Dansk",
"localePt": "Portugais \/ Portuguesa",

View File

@ -1,6 +1,9 @@
{
"@@locale": "it",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Tedesco \/ Deutsch",
"settingImagePreviewsDescription": "Le immagini e le immagini del profilo verranno scaricate e visualizzate in anteprima automaticamente. Ti consigliamo di non abilitare questo esperimento se usi Cwtch con contatti non attendibili.",
"localeNo": "Norvegese \/ Norsk",

View File

@ -1,6 +1,9 @@
{
"@@locale": "lb",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Däitsch \/ Deutsch",
"localeEn": "Englesch",
"localeLb": "Lëtzebuergesch",

View File

@ -1,6 +1,9 @@
{
"@@locale": "no",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Tysk \/ Deutsch",
"localeEn": "Engelsk",
"localeLb": "Luxemburgsk",

View File

@ -1,6 +1,9 @@
{
"@@locale": "pl",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Niemiecki \/ Deutsch",
"serverLabel": "Server",
"deleteBtn": "Usuń",

View File

@ -1,6 +1,9 @@
{
"@@locale": "pt",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Alemao \/ Deutsch",
"localeEn": "English \/ English",
"localeLb": "Luxembourgish \/ Lëtzebuergesch",

View File

@ -1,6 +1,9 @@
{
"@@locale": "ro",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"localeDe": "Germană",
"localeEn": "Engleză",
"localeLb": "Luxemburgheză",

View File

@ -1,6 +1,9 @@
{
"@@locale": "ru",
"@@last_modified": "2022-07-04T19:42:38+02:00",
"@@last_modified": "2022-07-06T20:42:11+02:00",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"exportProfile": "Экспорт профиля",
"notificationContentContactInfo": "Показать текст сообщения",
"notificationContentSimpleEvent": "Без подробностей",

View File

@ -7,8 +7,10 @@ class FileDownloadProgress {
String? downloadedTo;
DateTime? timeStart;
DateTime? timeEnd;
DateTime? requested;
FileDownloadProgress(this.chunksTotal, this.timeStart);
double progress() {
return 1.0 * chunksDownloaded / chunksTotal;
}

View File

@ -291,12 +291,27 @@ class ProfileInfoState extends ChangeNotifier {
}
bool downloadInterrupted(String fileKey) {
return this._downloads.containsKey(fileKey) && this._downloads[fileKey]!.interrupted;
if (this._downloads.containsKey(fileKey)) {
if (this._downloads[fileKey]!.interrupted) {
return true;
}
if (this._downloads[fileKey]!.requested != null) {
if (DateTime.now().difference(this._downloads[fileKey]!.requested!) > Duration(minutes: 1)) {
this._downloads[fileKey]!.requested = null;
this._downloads[fileKey]!.interrupted = true;
return true;
}
}
}
return false;
}
void downloadMarkResumed(String fileKey) {
if (this._downloads.containsKey(fileKey)) {
this._downloads[fileKey]!.interrupted = false;
this._downloads[fileKey]!.requested = DateTime.now();
notifyListeners();
}
}

View File

@ -0,0 +1,82 @@
import 'dart:convert';
import 'package:cwtch/config.dart';
import 'package:cwtch/cwtch/cwtch.dart';
import 'package:cwtch/main.dart';
import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/models/contact.dart';
import 'package:cwtch/models/profile.dart';
import 'package:cwtch/settings.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../cwtch_icons_icons.dart';
class FileSharingView extends StatefulWidget {
@override
_FileSharingViewState createState() => _FileSharingViewState();
}
class _FileSharingViewState extends State<FileSharingView> {
@override
Widget build(BuildContext context) {
var handle = Provider.of<ContactInfoState>(context).nickname;
if (handle.isEmpty) {
handle = Provider.of<ContactInfoState>(context).onion;
}
var profileHandle = Provider.of<ProfileInfoState>(context).onion;
return Scaffold(
appBar: AppBar(
title: Text(handle + " » " + AppLocalizations.of(context)!.manageSharedFiles),
),
body: FutureBuilder(
future: Provider.of<FlwtchState>(context, listen: false).cwtch.GetSharedFiles(profileHandle, Provider.of<ContactInfoState>(context).identifier),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> sharedFiles = jsonDecode(snapshot.data as String);
sharedFiles.sort((a, b) {
return a["DateShared"].toString().compareTo(b["DateShared"].toString());
});
var fileList = ScrollablePositionedList.separated(
itemScrollController: ItemScrollController(),
itemCount: sharedFiles.length,
shrinkWrap: true,
physics: BouncingScrollPhysics(),
semanticChildCount: sharedFiles.length,
itemBuilder: (context, index) {
String filekey = sharedFiles[index]["FileKey"];
EnvironmentConfig.debugLog("$sharedFiles " + sharedFiles[index].toString());
return SwitchListTile(
title: Text(sharedFiles[index]["Path"]),
subtitle: Text(sharedFiles[index]["DateShared"]),
value: sharedFiles[index]["Active"],
activeTrackColor: Provider.of<Settings>(context).theme.defaultButtonColor,
inactiveTrackColor: Provider.of<Settings>(context).theme.defaultButtonDisabledColor,
secondary: Icon(CwtchIcons.attach_file_24px, color: Provider.of<Settings>(context).current().mainTextColor),
onChanged: (newValue) {
setState(() {
if (newValue) {
Provider.of<FlwtchState>(context, listen: false).cwtch.RestartSharing(profileHandle, filekey);
} else {
Provider.of<FlwtchState>(context, listen: false).cwtch.StopSharing(profileHandle, filekey);
}
});
});
},
separatorBuilder: (BuildContext context, int index) {
return Divider(height: 1);
},
);
return fileList;
}
return Container();
},
),
);
}
}

View File

@ -32,6 +32,7 @@ import '../constants.dart';
import '../main.dart';
import '../settings.dart';
import '../widgets/messagelist.dart';
import 'filesharingview.dart';
import 'groupsettingsview.dart';
class MessageView extends StatefulWidget {
@ -97,6 +98,12 @@ class _MessageViewState extends State<MessageView> {
var showMessageFormattingPreview = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
var showFileSharing = Provider.of<Settings>(context).isExperimentEnabled(FileSharingExperiment);
var appBarButtons = <Widget>[];
if (showFileSharing) {
appBarButtons.add(
IconButton(splashRadius: Material.defaultSplashRadius / 2, icon: Icon(Icons.folder_shared), tooltip: AppLocalizations.of(context)!.manageSharedFiles, onPressed: _pushFileSharingSettings));
}
if (Provider.of<ContactInfoState>(context).isOnline()) {
if (showFileSharing) {
appBarButtons.add(IconButton(
@ -119,6 +126,7 @@ class _MessageViewState extends State<MessageView> {
},
));
}
appBarButtons.add(IconButton(
splashRadius: Material.defaultSplashRadius / 2,
icon: Icon(CwtchIcons.send_invite, size: 24),
@ -200,6 +208,23 @@ class _MessageViewState extends State<MessageView> {
return true;
}
void _pushFileSharingSettings() {
var profileInfoState = Provider.of<ProfileInfoState>(context, listen: false);
var contactInfoState = Provider.of<ContactInfoState>(context, listen: false);
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (builderContext, a1, a2) {
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: profileInfoState), ChangeNotifierProvider.value(value: contactInfoState)],
child: FileSharingView(),
);
},
transitionsBuilder: (c, anim, a2, child) => FadeTransition(opacity: anim, child: child),
transitionDuration: Duration(milliseconds: 200),
),
);
}
void _pushContactSettings() {
var profileInfoState = Provider.of<ProfileInfoState>(context, listen: false);
var contactInfoState = Provider.of<ContactInfoState>(context, listen: false);

View File

@ -249,8 +249,9 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
borderRadius: BorderRadius.horizontal(left: Radius.circular(180), right: Radius.circular(180))),
primary: Provider.of<Settings>(context).theme.backgroundMainColor,
),
child:
Text(AppLocalizations.of(context)!.importProfile, semanticsLabel: AppLocalizations.of(context)!.importProfile, style: TextStyle(color: Provider.of<Settings>(context).theme.mainTextColor, fontWeight: FontWeight.bold)),
child: Text(AppLocalizations.of(context)!.importProfile,
semanticsLabel: AppLocalizations.of(context)!.importProfile,
style: TextStyle(color: Provider.of<Settings>(context).theme.mainTextColor, fontWeight: FontWeight.bold)),
onPressed: () {
// 10GB profiles should be enough for anyone?
showFilePicker(context, MaxGeneralFileSharingSize, (file) {

View File

@ -266,7 +266,7 @@ class FileBubbleState extends State<FileBubble> {
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.SetMessageAttribute(profileOnion, conversation, 0, idx, "file-downloaded", "true");
ContactInfoState? contact = Provider.of<ProfileInfoState>(context, listen: false).contactList.findContact(Provider.of<MessageMetadata>(context).senderHandle);
ContactInfoState? contact = Provider.of<ProfileInfoState>(context, listen: false).contactList.findContact(Provider.of<MessageMetadata>(context, listen: false).senderHandle);
if (contact != null) {
Provider.of<FlwtchState>(context, listen: false).cwtch.DownloadFile(profileOnion, contact.identifier, file.path, manifestPath, widget.fileKey());
}