feat(android): implement file sending logic
This commit is contained in:
@@ -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,5 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
ext.kotlin_version = '1.9.22'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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'),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user