Merge branch 'trunk' of git.openprivacy.ca:flutter/flutter_app into androidservice
This commit is contained in:
commit
89507a8aa6
|
@ -1 +1 @@
|
||||||
v0.0.2-45-g4f625c7-2021-05-31-23-30
|
v0.0.2-49-g6a0e839-2021-06-02-19-40
|
|
@ -26,6 +26,7 @@ import io.flutter.plugin.common.EventChannel
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
class MainActivity: FlutterActivity() {
|
||||||
|
|
||||||
|
@ -60,6 +61,7 @@ class MainActivity: FlutterActivity() {
|
||||||
val ainfo = this.applicationContext.packageManager.getApplicationInfo(
|
val ainfo = this.applicationContext.packageManager.getApplicationInfo(
|
||||||
"im.cwtch.flwtch", // Must be app name
|
"im.cwtch.flwtch", // Must be app name
|
||||||
PackageManager.GET_SHARED_LIBRARY_FILES)
|
PackageManager.GET_SHARED_LIBRARY_FILES)
|
||||||
|
|
||||||
return ainfo.nativeLibraryDir
|
return ainfo.nativeLibraryDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx1536M
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.enableR8=true
|
android.enableR8=true
|
||||||
|
android.bundle.enableUncompressedNativeLibs=false
|
||||||
|
|
|
@ -82,6 +82,7 @@ class CwtchFfi implements Cwtch {
|
||||||
Map<String, String> envVars = Platform.environment;
|
Map<String, String> envVars = Platform.environment;
|
||||||
if (Platform.isLinux) {
|
if (Platform.isLinux) {
|
||||||
home = (envVars['HOME'])!;
|
home = (envVars['HOME'])!;
|
||||||
|
bundledTor = "./tor";
|
||||||
} else if (Platform.isWindows) {
|
} else if (Platform.isWindows) {
|
||||||
home = (envVars['UserProfile'])!;
|
home = (envVars['UserProfile'])!;
|
||||||
bundledTor = "Tor\\Tor\\tor.exe";
|
bundledTor = "Tor\\Tor\\tor.exe";
|
||||||
|
|
|
@ -456,7 +456,6 @@ class MessageState extends ChangeNotifier {
|
||||||
} else {
|
} else {
|
||||||
var parts = message['d'].toString().split("||");
|
var parts = message['d'].toString().split("||");
|
||||||
if (parts.length == 2) {
|
if (parts.length == 2) {
|
||||||
print("jsondecoding: " + utf8.fuse(base64).decode(parts[1].substring(5)));
|
|
||||||
var jsonObj = jsonDecode(utf8.fuse(base64).decode(parts[1].substring(5)));
|
var jsonObj = jsonDecode(utf8.fuse(base64).decode(parts[1].substring(5)));
|
||||||
this._inviteTarget = jsonObj['GroupID'];
|
this._inviteTarget = jsonObj['GroupID'];
|
||||||
this._inviteNick = jsonObj['GroupName'];
|
this._inviteNick = jsonObj['GroupName'];
|
||||||
|
|
|
@ -16,11 +16,10 @@ class NullNotificationsManager implements NotificationsManager {
|
||||||
// the standard dbus-powered linux desktop notifications.
|
// the standard dbus-powered linux desktop notifications.
|
||||||
class LinuxNotificationsManager implements NotificationsManager {
|
class LinuxNotificationsManager implements NotificationsManager {
|
||||||
int previous_id = 0;
|
int previous_id = 0;
|
||||||
|
final NotificationsClient client = NotificationsClient();
|
||||||
LinuxNotificationsManager() {}
|
LinuxNotificationsManager() {}
|
||||||
Future<void> notify(String message) async {
|
Future<void> notify(String message) async {
|
||||||
var client = NotificationsClient();
|
|
||||||
var icon_path = Uri.file(path.join(path.current, "cwtch.png"));
|
var icon_path = Uri.file(path.join(path.current, "cwtch.png"));
|
||||||
client.notify('New Message from Peer!', appName: "cwtch", appIcon: icon_path.toString(), replacesId: this.previous_id).then((Notification value) => previous_id = value.id);
|
client.notify('New Message from Peer!', appName: "cwtch", appIcon: icon_path.toString(), replacesId: this.previous_id).then((Notification value) => previous_id = value.id);
|
||||||
client.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cwtch/opaque.dart';
|
import 'package:cwtch/opaque.dart';
|
||||||
|
@ -89,7 +90,8 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
||||||
Provider.of<FlwtchState>(context).columns = [1];
|
Provider.of<FlwtchState>(context).columns = [1];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
items: ["Single", "Double (1:2)", "Double (1:4)"].map<DropdownMenuItem<String>>((String value) {
|
// TODO: Only allow in landscape?
|
||||||
|
items: (Platform.isAndroid ? ["Single"] : ["Single", "Double (1:2)", "Double (1:4)"]).map<DropdownMenuItem<String>>((String value) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
child: Text(value),
|
child: Text(value),
|
||||||
|
|
|
@ -46,11 +46,10 @@ class _MessageViewState extends State<MessageView> {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(Provider.of<ContactInfoState>(context).nickname),
|
title: Text(Provider.of<ContactInfoState>(context).nickname),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
|
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
|
||||||
IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
|
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
|
||||||
IconButton(icon: Icon(Icons.push_pin), onPressed: _pushContactSettings),
|
//IconButton(icon: Icon(Icons.push_pin), onPressed: _pushContactSettings),
|
||||||
IconButton(icon: Icon(Icons.settings), onPressed: _pushContactSettings),
|
IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.conversationSettings, onPressed: _pushContactSettings),
|
||||||
IconButton(icon: Icon(Icons.bug_report_outlined), onPressed: _debugResetContact),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList()),
|
body: Padding(padding: EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 108.0), child: MessageList()),
|
||||||
|
|
|
@ -132,6 +132,6 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
return DateFormat.yMd().format(date.toLocal());
|
return DateFormat.yMd().format(date.toLocal());
|
||||||
}
|
}
|
||||||
// Otherwise just state the time.
|
// Otherwise just state the time.
|
||||||
return DateFormat.Hms().format(date.toLocal());
|
return DateFormat.Hm().format(date.toLocal());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import '../settings.dart';
|
||||||
|
|
||||||
// Provides a styled Label
|
// Provides a styled Label
|
||||||
// Callers must provide a label text
|
// Callers must provide a label text
|
||||||
// TODO: Integrate this with a settings "zoom" / accessibility setting
|
|
||||||
class CwtchLabel extends StatefulWidget {
|
class CwtchLabel extends StatefulWidget {
|
||||||
CwtchLabel({required this.label});
|
CwtchLabel({required this.label});
|
||||||
final String label;
|
final String label;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:intl/intl.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
|
import 'messagebubbledecorations.dart';
|
||||||
|
|
||||||
// Like MessageBubble but for displaying chat overlay 100/101 invitations
|
// Like MessageBubble but for displaying chat overlay 100/101 invitations
|
||||||
// Offers the user an accept/reject button if they don't have a matching contact already
|
// Offers the user an accept/reject button if they don't have a matching contact already
|
||||||
|
@ -52,48 +53,14 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
||||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor())));
|
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor())));
|
||||||
|
|
||||||
// todo: translations
|
// todo: translations
|
||||||
var messageStr = "";
|
var wdgMessage = fromMe
|
||||||
if (fromMe) {
|
? senderInviteChrome("You sent an invitation for", isGroup ? "a group" : Provider.of<MessageState>(context).message, myKey)
|
||||||
//todo: get group name?
|
: inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : "This is a contact suggestion for:", Provider.of<MessageState>(context).inviteNick,
|
||||||
messageStr = "You sent an invitation for " + (isGroup ? "a group" : Provider.of<MessageState>(context).message ?? "");
|
Provider.of<MessageState>(context).inviteTarget, myKey);
|
||||||
} else {
|
|
||||||
String joinGroup = AppLocalizations.of(context)!.inviteToGroup;
|
|
||||||
messageStr = (isGroup ? joinGroup + (Provider.of<MessageState>(context).inviteNick ?? "") : "This is a contact suggestion for:") + "\n" + (Provider.of<MessageState>(context).inviteTarget ?? "");
|
|
||||||
}
|
|
||||||
var wdgMessage = Center(
|
|
||||||
widthFactor: 1,
|
|
||||||
child: SelectableText(
|
|
||||||
messageStr + '\u202F',
|
|
||||||
key: Key(myKey),
|
|
||||||
focusNode: _focus,
|
|
||||||
style: TextStyle(
|
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
|
||||||
));
|
|
||||||
|
|
||||||
Widget wdgDecorations;
|
Widget wdgDecorations;
|
||||||
if (fromMe) {
|
if (fromMe) {
|
||||||
wdgDecorations = Center(
|
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||||
widthFactor: 1.0,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(prettyDate,
|
|
||||||
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()),
|
|
||||||
textAlign: fromMe ? TextAlign.right : TextAlign.left),
|
|
||||||
!fromMe
|
|
||||||
? SizedBox(width: 1, height: 1)
|
|
||||||
: Padding(
|
|
||||||
padding: EdgeInsets.all(1.0),
|
|
||||||
child: Provider.of<MessageState>(context).ackd == true
|
|
||||||
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
|
|
||||||
: (Provider.of<MessageState>(context).error == true
|
|
||||||
? Icon(Icons.error_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
|
|
||||||
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)))
|
|
||||||
],
|
|
||||||
));
|
|
||||||
} else if (isAccepted) {
|
} else if (isAccepted) {
|
||||||
wdgDecorations = Text("Accepted!");
|
wdgDecorations = Text("Accepted!");
|
||||||
} else if (this.rejected) {
|
} else if (this.rejected) {
|
||||||
|
@ -158,4 +125,58 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
||||||
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, Provider.of<MessageState>(context, listen: false).message);
|
Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, Provider.of<MessageState>(context, listen: false).message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construct an invite chrome for the sender
|
||||||
|
Widget senderInviteChrome(String chrome, String targetName, String myKey) {
|
||||||
|
return Center(
|
||||||
|
widthFactor: 1,
|
||||||
|
child: Row(children: [
|
||||||
|
SelectableText(
|
||||||
|
chrome,
|
||||||
|
key: Key(myKey),
|
||||||
|
focusNode: _focus,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Provider.of<Settings>(context).theme.messageFromMeTextColor(),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
|
),
|
||||||
|
SelectableText(
|
||||||
|
targetName + '\u202F',
|
||||||
|
key: Key(myKey),
|
||||||
|
focusNode: _focus,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Provider.of<Settings>(context).theme.messageFromMeTextColor(),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
|
)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct an invite chrome
|
||||||
|
Widget inviteChrome(String chrome, String targetName, String targetId, String myKey) {
|
||||||
|
return Center(
|
||||||
|
widthFactor: 1,
|
||||||
|
child: Row(children: [
|
||||||
|
SelectableText(
|
||||||
|
chrome,
|
||||||
|
key: Key(myKey),
|
||||||
|
focusNode: _focus,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
|
),
|
||||||
|
SelectableText(
|
||||||
|
targetName,
|
||||||
|
key: Key(myKey),
|
||||||
|
focusNode: _focus,
|
||||||
|
style: TextStyle(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor()),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
|
)
|
||||||
|
]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import '../model.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import '../settings.dart';
|
import '../settings.dart';
|
||||||
|
import 'messagebubbledecorations.dart';
|
||||||
|
|
||||||
class MessageBubble extends StatefulWidget {
|
class MessageBubble extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
|
@ -51,28 +52,7 @@ class MessageBubbleState extends State<MessageBubble> {
|
||||||
textWidthBasis: TextWidthBasis.longestLine,
|
textWidthBasis: TextWidthBasis.longestLine,
|
||||||
);
|
);
|
||||||
|
|
||||||
var wdgDecorations = Center(
|
var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||||
widthFactor: 1.0,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(prettyDate,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 9.0,
|
|
||||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor(),
|
|
||||||
),
|
|
||||||
textAlign: fromMe ? TextAlign.right : TextAlign.left),
|
|
||||||
!fromMe
|
|
||||||
? SizedBox(width: 1, height: 1)
|
|
||||||
: Padding(
|
|
||||||
padding: EdgeInsets.all(1.0),
|
|
||||||
child: Provider.of<MessageState>(context).ackd == true
|
|
||||||
? Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
|
|
||||||
: (Provider.of<MessageState>(context).error == true
|
|
||||||
? Icon(Icons.error_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)
|
|
||||||
: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16)))
|
|
||||||
],
|
|
||||||
));
|
|
||||||
|
|
||||||
var error = Provider.of<MessageState>(context).error;
|
var error = Provider.of<MessageState>(context).error;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import '../settings.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
// Provides message decorations (acks/errors/dates etc.) for generic message bubble overlays (chats, invites etc.)
|
||||||
|
class MessageBubbleDecoration extends StatefulWidget {
|
||||||
|
MessageBubbleDecoration({required this.ackd, required this.errored, required this.prettyDate, required this.fromMe});
|
||||||
|
final String prettyDate;
|
||||||
|
final bool fromMe;
|
||||||
|
final bool ackd;
|
||||||
|
final bool errored;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_MessageBubbleDecoration createState() => _MessageBubbleDecoration();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MessageBubbleDecoration extends State<MessageBubbleDecoration> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
widthFactor: 1.0,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(widget.prettyDate,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 9.0, color: widget.fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor() : Provider.of<Settings>(context).theme.messageFromOtherTextColor()),
|
||||||
|
textAlign: widget.fromMe ? TextAlign.right : TextAlign.left),
|
||||||
|
!widget.fromMe
|
||||||
|
? SizedBox(width: 1, height: 1)
|
||||||
|
: Padding(
|
||||||
|
padding: EdgeInsets.all(1.0),
|
||||||
|
child: widget.ackd == true
|
||||||
|
? Tooltip(
|
||||||
|
message: AppLocalizations.of(context)!.acknowledgedLabel,
|
||||||
|
child: Icon(Icons.check_circle_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16))
|
||||||
|
: (widget.errored == true
|
||||||
|
? Tooltip(
|
||||||
|
message: AppLocalizations.of(context)!.couldNotSendMsgError,
|
||||||
|
child: Icon(Icons.error_outline, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16))
|
||||||
|
: Tooltip(
|
||||||
|
message: AppLocalizations.of(context)!.pendingLabel,
|
||||||
|
child: Icon(Icons.hourglass_bottom_outlined, color: Provider.of<Settings>(context).theme.messageFromMeTextColor(), size: 16))))
|
||||||
|
],
|
||||||
|
));
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 1.0.0+1
|
version: 1.0.0+9
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
|
Loading…
Reference in New Issue