feat(android): implement file sending logic

This commit is contained in:
ksenia312
2024-02-01 17:27:00 +01:00
parent 5870dc2577
commit 46451ef447
22 changed files with 606 additions and 75 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
namespace "com.xenikii.nearby_service_example"
compileSdk flutter.compileSdkVersion
compileSdk 34
ndkVersion flutter.ndkVersion
compileOptions {
+1 -1
View File
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.7.10'
ext.kotlin_version = '1.9.22'
repositories {
google()
mavenCentral()
+4
View File
@@ -26,6 +26,10 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
+31
View File
@@ -0,0 +1,31 @@
part of '../main.dart';
class ActionDialog {
ActionDialog._();
static Future<bool?> show(
BuildContext context, {
required String title,
required String subtitle,
}) {
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: Text(title),
content: Text(subtitle),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Yes'),
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('No'),
),
],
);
},
);
}
}
+114 -17
View File
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
@@ -11,6 +12,8 @@ import 'components/app_snack_bar.dart';
part 'components/action_button.dart';
part 'components/action_dialog.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final service = AppService();
@@ -197,6 +200,8 @@ class AppService extends ChangeNotifier {
StreamSubscription? peersSubscription;
StreamSubscription? connectedDeviceSubscription;
final _filesAccepts = <String, Future<bool?>>{};
@override
void dispose() {
stopListeningAll();
@@ -373,14 +378,27 @@ class AppService extends ChangeNotifier {
Future<void> startCommunicationChannel({
ValueChanged<ReceivedNearbyMessage>? listener,
ValueChanged<File>? onFileSaved,
}) async {
final eventListener = NearbyServiceStreamListener(
onCreated: (_) {
onCreated: () {
updateState(AppState.communicationChannelCreated);
},
onData: (event) {
onMessage: (event) {
listener?.call(event);
},
onFile: (event) async {
final content = event.content;
final fileAcceptFuture = _filesAccepts[content.id];
if (fileAcceptFuture != null && (await fileAcceptFuture == true)) {
final downloadsDir = Directory('storage/emulated/0/Download');
final newFile = await event.file.copy(
'${downloadsDir.path}/${content.id}_${content.fileName}',
);
onFileSaved?.call(newFile);
}
},
onError: (e, [StackTrace? s]) {
stopListeningAll();
},
@@ -394,16 +412,30 @@ class AppService extends ChangeNotifier {
);
}
void send(String message) {
void sendMessage(String message) {
if (connectedDevice == null) return;
_nearbyService.send(
OutgoingNearbyMessage(
value: message,
content: NearbyMessageTextContent(value: message),
receiver: connectedDevice!.info,
),
);
}
void sendFile(String filePath) {
if (connectedDevice == null) return;
_nearbyService.send(
OutgoingNearbyMessage(
content: NearbyMessageFileContent(filePath: filePath),
receiver: connectedDevice!.info,
),
);
}
void setFileAcceptFuture(String id, Future<bool?> future) {
_filesAccepts[id] = future;
}
Future<void> disconnect(NearbyDevice device) async {
try {
await _nearbyService.disconnect(device);
@@ -740,15 +772,11 @@ class _ConnectedBody extends StatelessWidget {
if (service.communicationChannelState !=
CommunicationChannelState.loading)
_ActionButton(
onTap: () => service.startCommunicationChannel(
listener: (event) => AppShackBar.show(
Scaffold.of(context).context,
event.value,
subtitle: 'From ${event.sender.displayName} '
'(ID: ${event.sender.id})',
),
),
title: 'Start communicate',
onTap: () => service.startCommunicationChannel(
listener: (event) => _listener(context, event),
onFileSaved: (file) => _onFileSaved(context, file),
),
)
else
Text(
@@ -762,6 +790,37 @@ class _ConnectedBody extends StatelessWidget {
},
);
}
void _listener(BuildContext context, ReceivedNearbyMessage message) {
final senderSubtitle = 'From ${message.sender.displayName} '
'(ID: ${message.sender.id})';
message.content.get(
onText: (content) {
AppShackBar.show(
Scaffold.of(context).context,
content.value,
subtitle: senderSubtitle,
);
},
onFile: (content) {
context.read<AppService>().setFileAcceptFuture(
content.id,
ActionDialog.show(
context,
title: 'File request ${content.fileName}',
subtitle: senderSubtitle,
),
);
},
);
}
void _onFileSaved(BuildContext context, File file) {
AppShackBar.show(
Scaffold.of(context).context,
'File saved to ${file.path}',
);
}
}
class _ConnectedSocketBody extends StatefulWidget {
@@ -773,6 +832,7 @@ class _ConnectedSocketBody extends StatefulWidget {
class _ConnectedSocketBodyState extends State<_ConnectedSocketBody> {
String message = '';
String filePath = '';
@override
Widget build(BuildContext context) {
@@ -797,6 +857,7 @@ class _ConnectedSocketBodyState extends State<_ConnectedSocketBody> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: TextField(
onChanged: (value) => setState(() {
message = value;
@@ -810,15 +871,51 @@ class _ConnectedSocketBodyState extends State<_ConnectedSocketBody> {
),
),
const SizedBox(width: 10),
_ActionButton(
title: 'Send',
onTap: () {
service.send(message);
},
Flexible(
child: _ActionButton(
title: 'Send',
onTap: () {
service.sendMessage(message);
},
),
),
],
),
),
const SizedBox(height: 10),
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 2,
child: _ActionButton(
type: _ActionButtonType.warning,
title: 'Choose a file',
onTap: () async {
final result = await FilePicker.platform.pickFiles();
if (result != null && result.isSinglePick) {
setState(() {
filePath = result.paths.first!;
});
}
},
),
),
const SizedBox(width: 10),
Flexible(
child: _ActionButton(
title: 'Send',
onTap: () {
service.sendFile(filePath);
},
),
),
],
),
),
const SizedBox(height: 10),
Text('Selected file: $filePath'),
],
);
},
+4 -3
View File
@@ -5,16 +5,17 @@ description: Demonstrates how to use the nearby_service plugin.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: '>=3.0.6 <4.0.0'
sdk: '>=3.0.0 <4.0.0'
dependencies:
provider: ^6.1.1
file_picker: ^6.1.1
flutter:
sdk: flutter
nearby_service:
path: ../
permission_handler: ^11.0.1
provider: ^6.1.1
dev_dependencies: