Receive sharing intent

This commit is contained in:
Christian Pauly 2020-04-02 14:05:32 +02:00
parent 4bf7cb9f4b
commit 8c1ed0de1e
11 changed files with 167 additions and 77 deletions

View file

@ -1,4 +1,6 @@
# Version 0.11.0 - 2020-04-02 # Version 0.11.0 - 2020-04-02
### Features:
- Share content with FluffyChat
### Fixes: ### Fixes:
- Minor bugfixes - Minor bugfixes

View file

@ -16,7 +16,7 @@
android:icon="@mipmap/launcher_icon"> android:icon="@mipmap/launcher_icon">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTask"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
@ -38,6 +38,27 @@
android:scheme="https" android:scheme="https"
android:host="matrix.to"/> android:host="matrix.to"/>
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="document/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
</activity> </activity>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.

View file

@ -0,0 +1,67 @@
import 'package:bubble/bubble.dart';
import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/utils/matrix_file_extension.dart';
class ImageBubble extends StatefulWidget {
final Event event;
const ImageBubble(this.event, {Key key}) : super(key: key);
@override
_ImageBubbleState createState() => _ImageBubbleState();
}
class _ImageBubbleState extends State<ImageBubble> {
MatrixFile _file;
dynamic _error;
Future<MatrixFile> _getFile() async {
if (_file != null) return _file;
return widget.event.downloadAndDecryptAttachment();
}
@override
Widget build(BuildContext context) {
final int size = 400;
return Bubble(
padding: BubbleEdges.all(0),
radius: Radius.circular(10),
elevation: 0,
child: Container(
height: size.toDouble(),
width: size.toDouble(),
child: Builder(
builder: (BuildContext context) {
if (_error != null) {
return Center(
child: Text(
_error.toString(),
),
);
}
if (_file != null) {
return InkWell(
onTap: () => _file.open(),
child: Image.memory(
_file.bytes,
width: size.toDouble(),
height: size.toDouble(),
fit: BoxFit.fill,
),
);
}
_getFile().then((MatrixFile file) {
setState(() => _file = file);
}, onError: (error) {
setState(() => _error = error);
});
return Center(
child: CircularProgressIndicator(),
);
},
),
),
);
}
}

View file

@ -77,7 +77,16 @@ class ChatListItem extends StatelessWidget {
if (room.membership == Membership.join) { if (room.membership == Membership.join) {
if (Matrix.of(context).shareContent != null) { if (Matrix.of(context).shareContent != null) {
if (Matrix.of(context).shareContent["msgtype"] ==
"chat.fluffy.shared_file") {
await Matrix.of(context).tryRequestWithErrorToast(
room.sendFileEvent(
Matrix.of(context).shareContent["file"],
),
);
} else {
unawaited(room.sendEvent(Matrix.of(context).shareContent)); unawaited(room.sendEvent(Matrix.of(context).shareContent));
}
Matrix.of(context).shareContent = null; Matrix.of(context).shareContent = null;
} }
await Navigator.pushAndRemoveUntil( await Navigator.pushAndRemoveUntil(

View file

@ -394,7 +394,6 @@ class _InheritedMatrix extends InheritedWidget {
bool update = old.data.client.accessToken != this.data.client.accessToken || bool update = old.data.client.accessToken != this.data.client.accessToken ||
old.data.client.userID != this.data.client.userID || old.data.client.userID != this.data.client.userID ||
old.data.client.matrixVersions != this.data.client.matrixVersions || old.data.client.matrixVersions != this.data.client.matrixVersions ||
old.data.client.lazyLoadMembers != this.data.client.lazyLoadMembers ||
old.data.client.deviceID != this.data.client.deviceID || old.data.client.deviceID != this.data.client.deviceID ||
old.data.client.deviceName != this.data.client.deviceName || old.data.client.deviceName != this.data.client.deviceName ||
old.data.client.homeserver != this.data.client.homeserver; old.data.client.homeserver != this.data.client.homeserver;

View file

@ -1,6 +1,7 @@
import 'package:bubble/bubble.dart'; import 'package:bubble/bubble.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/audio_player.dart'; import 'package:fluffychat/components/audio_player.dart';
import 'package:fluffychat/components/image_bubble.dart';
import 'package:fluffychat/i18n/i18n.dart'; import 'package:fluffychat/i18n/i18n.dart';
import 'package:fluffychat/utils/event_extension.dart'; import 'package:fluffychat/utils/event_extension.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -26,37 +27,7 @@ class MessageContent extends StatelessWidget {
switch (event.messageType) { switch (event.messageType) {
case MessageTypes.Image: case MessageTypes.Image:
case MessageTypes.Sticker: case MessageTypes.Sticker:
final int size = 400; return ImageBubble(event);
return Bubble(
padding: BubbleEdges.all(0),
radius: Radius.circular(10),
elevation: 0,
child: Container(
height: size.toDouble(),
width: size.toDouble(),
child: FutureBuilder<MatrixFile>(
future: event.downloadAndDecryptAttachment(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
snapshot.error.toString(),
),
);
}
if (snapshot.hasData) {
return InkWell(
onTap: () => snapshot.data.open(),
child: Image.memory(snapshot.data.bytes),
);
}
return Center(
child: CircularProgressIndicator(),
);
},
),
),
);
case MessageTypes.Audio: case MessageTypes.Audio:
return AudioPlayer( return AudioPlayer(
event, event,

View file

@ -86,7 +86,6 @@ class Store extends StoreAPI {
newDeviceID: credentials["deviceID"], newDeviceID: credentials["deviceID"],
newDeviceName: credentials["deviceName"], newDeviceName: credentials["deviceName"],
newHomeserver: credentials["homeserver"], newHomeserver: credentials["homeserver"],
newLazyLoadMembers: credentials["lazyLoadMembers"],
newMatrixVersions: List<String>.from(credentials["matrixVersions"] ?? []), newMatrixVersions: List<String>.from(credentials["matrixVersions"] ?? []),
newToken: credentials["token"], newToken: credentials["token"],
newUserID: credentials["userID"], newUserID: credentials["userID"],
@ -104,7 +103,6 @@ class Store extends StoreAPI {
"deviceID": client.deviceID, "deviceID": client.deviceID,
"deviceName": client.deviceName, "deviceName": client.deviceName,
"homeserver": client.homeserver, "homeserver": client.homeserver,
"lazyLoadMembers": client.lazyLoadMembers,
"matrixVersions": client.matrixVersions, "matrixVersions": client.matrixVersions,
"token": client.accessToken, "token": client.accessToken,
"userID": client.userID, "userID": client.userID,
@ -175,7 +173,6 @@ class ExtendedStore extends Store implements ExtendedStoreAPI {
newUserID: clientList["matrix_id"], newUserID: clientList["matrix_id"],
newDeviceID: clientList["device_id"], newDeviceID: clientList["device_id"],
newDeviceName: clientList["device_name"], newDeviceName: clientList["device_name"],
newLazyLoadMembers: clientList["lazy_load_members"] == 1,
newMatrixVersions: newMatrixVersions:
clientList["matrix_versions"].toString().split(","), clientList["matrix_versions"].toString().split(","),
newPrevBatch: null, newPrevBatch: null,

View file

@ -40,10 +40,6 @@ class AppInfo extends StatelessWidget {
title: Text("Supported versions:"), title: Text("Supported versions:"),
subtitle: Text(client.matrixVersions.toString()), subtitle: Text(client.matrixVersions.toString()),
), ),
ListTile(
title: Text("Lazy Loading members enabled:"),
subtitle: Text(client.lazyLoadMembers.toString()),
),
ListTile( ListTile(
title: Text("Device name:"), title: Text("Device name:"),
subtitle: Text(client.deviceName), subtitle: Text(client.deviceName),

View file

@ -1,12 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:fluffychat/components/list_items/public_room_list_item.dart'; import 'package:fluffychat/components/list_items/public_room_list_item.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:uni_links/uni_links.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import '../components/dialogs/simple_dialogs.dart'; import '../components/dialogs/simple_dialogs.dart';
import '../components/theme_switcher.dart'; import '../components/theme_switcher.dart';
@ -103,35 +103,63 @@ class _ChatListState extends State<ChatList> {
}); });
setState(() => null); setState(() => null);
}); });
initUniLinks(); _initReceiveSharingINtent();
super.initState(); super.initState();
} }
StreamSubscription _intentDataStreamSubscription; StreamSubscription _intentDataStreamSubscription;
StreamSubscription _onUniLinksub; StreamSubscription _intentFileStreamSubscription;
Future<void> initUniLinks() async { void _processIncomingSharedFiles(List<SharedMediaFile> files) {
if (files?.isEmpty ?? true) return;
if (Navigator.of(context).canPop()) {
Navigator.of(context).popUntil((r) => r.isFirst);
}
final File file = File(files.first.path);
Matrix.of(context).shareContent = {
"msgtype": "chat.fluffy.shared_file",
"file": MatrixFile(
bytes: file.readAsBytesSync(),
path: file.path,
),
};
setState(() => null);
}
void _processIncomingSharedText(String text) {
if (text == null) return;
if (Navigator.of(context).canPop()) {
Navigator.of(context).popUntil((r) => r.isFirst);
}
if (text.startsWith("https://matrix.to/#/")) {
UrlLauncher(context, text).openMatrixToUrl();
return;
}
Matrix.of(context).shareContent = {
"msgtype": "m.text",
"body": text,
};
setState(() => null);
}
void _initReceiveSharingINtent() {
if (kIsWeb) return; if (kIsWeb) return;
_onUniLinksub ??= getLinksStream().listen(
(String initialLink) { // For sharing images coming from outside the app while the app is in the memory
try { _intentFileStreamSubscription = ReceiveSharingIntent.getMediaStream()
if (initialLink?.isEmpty ?? true) return; .listen(_processIncomingSharedFiles, onError: print);
if (initialLink.startsWith("https://matrix.to/#/")) {
UrlLauncher(context, initialLink).openMatrixToUrl(); // For sharing images coming from outside the app while the app is closed
} ReceiveSharingIntent.getInitialMedia().then(_processIncomingSharedFiles);
} on PlatformException {
debugPrint("initUniLinks failed during platform exception"); // For sharing or opening urls/text coming from outside the app while the app is in the memory
} _intentDataStreamSubscription = ReceiveSharingIntent.getTextStream()
}, .listen(_processIncomingSharedText, onError: print);
onError: (error) => Scaffold.of(context).showSnackBar(
SnackBar( // For sharing or opening urls/text coming from outside the app while the app is closed
content: Text( ReceiveSharingIntent.getInitialText().then(_processIncomingSharedText);
I18n.of(context).oopsSomethingWentWrong + " " + error.toString(),
),
),
),
);
} }
@override @override
@ -141,7 +169,7 @@ class _ChatListState extends State<ChatList> {
() => setState(() => null), () => setState(() => null),
); );
_intentDataStreamSubscription?.cancel(); _intentDataStreamSubscription?.cancel();
_onUniLinksub?.cancel(); _intentFileStreamSubscription?.cancel();
super.dispose(); super.dispose();
} }

View file

@ -124,8 +124,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "63a5d5dba37bdf3d1106fdf6f11760bfd7d2904c" ref: "31871235a59af16c7c163f767d7a7f8486457429"
resolved-ref: "63a5d5dba37bdf3d1106fdf6f11760bfd7d2904c" resolved-ref: "31871235a59af16c7c163f767d7a7f8486457429"
url: "https://gitlab.com/famedly/famedlysdk.git" url: "https://gitlab.com/famedly/famedlysdk.git"
source: git source: git
version: "0.0.1" version: "0.0.1"
@ -510,6 +510,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.0.5"
receive_sharing_intent:
dependency: "direct main"
description:
name: receive_sharing_intent
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.3"
share: share:
dependency: "direct main" dependency: "direct main"
description: description:
@ -641,13 +648,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.6" version: "1.1.6"
uni_links:
dependency: "direct main"
description:
name: uni_links
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
universal_html: universal_html:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -27,7 +27,7 @@ dependencies:
famedlysdk: famedlysdk:
git: git:
url: https://gitlab.com/famedly/famedlysdk.git url: https://gitlab.com/famedly/famedlysdk.git
ref: 63a5d5dba37bdf3d1106fdf6f11760bfd7d2904c ref: 31871235a59af16c7c163f767d7a7f8486457429
localstorage: ^3.0.1+4 localstorage: ^3.0.1+4
bubble: ^1.1.9+1 bubble: ^1.1.9+1
@ -47,7 +47,7 @@ dependencies:
flutter_secure_storage: ^3.3.1+1 flutter_secure_storage: ^3.3.1+1
http: ^0.12.0+4 http: ^0.12.0+4
universal_html: ^1.1.12 universal_html: ^1.1.12
uni_links: ^0.2.0 receive_sharing_intent: ^1.3.3
flutter_svg: ^0.17.1 flutter_svg: ^0.17.1
flutter_slidable: ^0.5.4 flutter_slidable: ^0.5.4
photo_view: ^0.9.2 photo_view: ^0.9.2