feat(example_full, lib, android): optimisation improving, current device data
This commit is contained in:
@@ -97,8 +97,9 @@ class NearbyServiceManager(private var context: Context) {
|
||||
}
|
||||
} catch (e: SecurityException) {
|
||||
if (!permissionsHandler.checkPermissions()) {
|
||||
Logger.e("No permission to call 'discover'")
|
||||
Logger.e("No permission to call 'getCurrentDevice'")
|
||||
permissionsHandler.requestPermissions()
|
||||
result.success(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ android {
|
||||
applicationId "com.example.nearby_service_example_full"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
minSdkVersion 24
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<application
|
||||
android:label="nearby_service_example_full"
|
||||
android:name="${applicationName}"
|
||||
|
||||
@@ -45,7 +45,6 @@ class AppService extends ChangeNotifier {
|
||||
await _nearbyService.initialize(
|
||||
data: NearbyInitializeData(iosDeviceName: iosDeviceName),
|
||||
);
|
||||
await getCurrentDeviceInfo();
|
||||
updateState(
|
||||
Platform.isAndroid ? AppState.permissions : AppState.selectClientType,
|
||||
);
|
||||
@@ -86,6 +85,7 @@ class AppService extends ChangeNotifier {
|
||||
if (result ?? false) {
|
||||
updateState(AppState.readyToDiscover);
|
||||
startListeningConnectionInfo();
|
||||
await getCurrentDeviceInfo();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -37,46 +37,60 @@ class App extends StatelessWidget {
|
||||
}),
|
||||
],
|
||||
),
|
||||
body: Consumer<AppService>(builder: (context, service, _) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 12, 16, 10),
|
||||
child: InfoPanel(),
|
||||
),
|
||||
Flexible(
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeLeft: true,
|
||||
child: Stepper(
|
||||
controlsBuilder: (context, _) => const SizedBox.shrink(),
|
||||
currentStep: service.state.step,
|
||||
steps: [
|
||||
...AppState.steps.map((e) {
|
||||
final builder = AppStepViewBuilder(state: e);
|
||||
final isActive = e == service.state;
|
||||
return Step(
|
||||
state: isActive
|
||||
? StepState.indexed
|
||||
: e.step < service.state.step
|
||||
? StepState.complete
|
||||
: StepState.disabled,
|
||||
title: builder.buildTitle(),
|
||||
subtitle: builder.buildSubtitle(),
|
||||
content: builder.buildContent(),
|
||||
isActive: isActive,
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
body: const _AppBody(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AppBody extends StatelessWidget {
|
||||
const _AppBody();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<AppService, AppState>(
|
||||
selector: (context, service) => service.state,
|
||||
builder: (context, state, _) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 12, 16, 10),
|
||||
child: InfoPanel(),
|
||||
),
|
||||
Flexible(
|
||||
child: MediaQuery.removePadding(
|
||||
context: context,
|
||||
removeLeft: true,
|
||||
child: Stepper(
|
||||
controlsBuilder: (context, _) => const SizedBox.shrink(),
|
||||
currentStep: state.step,
|
||||
steps: [
|
||||
...AppState.steps.map(
|
||||
(e) {
|
||||
final builder = AppStepViewBuilder(state: e);
|
||||
final isActive = e == state;
|
||||
return Step(
|
||||
state: isActive
|
||||
? StepState.indexed
|
||||
: e.step < state.step
|
||||
? StepState.complete
|
||||
: StepState.disabled,
|
||||
title: builder.buildTitle(),
|
||||
subtitle: builder.buildSubtitle(),
|
||||
content: builder.buildContent(),
|
||||
isActive: isActive,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service_example_full/domain/app_service.dart';
|
||||
import 'package:nearby_service_example_full/presentation/app.dart';
|
||||
import 'package:nearby_service_example_full/uikit/uikit.dart';
|
||||
import 'package:nearby_service_example_full/utils/files_saver.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../components/device_preview.dart';
|
||||
@@ -22,13 +24,13 @@ class _CommunicationViewState extends State<CommunicationView> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<AppService>(
|
||||
builder: (context, service, _) {
|
||||
final device = service.connectedDevice;
|
||||
return Selector<AppService, NearbyDevice?>(
|
||||
selector: (context, service) => service.connectedDevice,
|
||||
builder: (context, device, _) {
|
||||
if (device == null) {
|
||||
return Center(
|
||||
child: ActionButton(
|
||||
onTap: service.stopListeningAll,
|
||||
onTap: context.read<AppService>().stopListeningAll,
|
||||
title: 'Restart',
|
||||
),
|
||||
);
|
||||
@@ -69,7 +71,9 @@ class _CommunicationViewState extends State<CommunicationView> {
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
title: 'Send',
|
||||
onTap: () => service.sendTextRequest(message),
|
||||
onTap: () => context.read<AppService>().sendTextRequest(
|
||||
message,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -100,7 +104,7 @@ class _CommunicationViewState extends State<CommunicationView> {
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
title: 'Send',
|
||||
onTap: () => service.sendFilesRequest([
|
||||
onTap: () => context.read<AppService>().sendFilesRequest([
|
||||
...files
|
||||
.map((e) => e.path)
|
||||
.where((element) => element != null)
|
||||
@@ -122,11 +126,57 @@ class _CommunicationViewState extends State<CommunicationView> {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: [
|
||||
...files.where((element) => element.path != null).map(
|
||||
(e) => Image.file(
|
||||
(e) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: kGreenColor),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(11),
|
||||
child: FilesSaver.isImage(e.extension)
|
||||
? Image.file(
|
||||
File(e.path!),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
e.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: kGreenColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (FilesSaver.isImage(e.extension)) {
|
||||
return Image.file(
|
||||
File(e.path!),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: kBlueColor),
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
e.name,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: kBlueColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -13,63 +13,68 @@ class ConnectedView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<AppService>(
|
||||
builder: (context, service, _) {
|
||||
final device = service.connectedDevice;
|
||||
return device != null
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (!device.status.isConnected)
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text('Connection lost'),
|
||||
),
|
||||
)
|
||||
else if (!device.status.isConnected)
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Connection lost'),
|
||||
ActionButton(
|
||||
onTap: () {
|
||||
service.connect(device);
|
||||
},
|
||||
title: 'Reconnect',
|
||||
),
|
||||
],
|
||||
)
|
||||
else
|
||||
DevicePreview(device: device, largeView: true),
|
||||
const SizedBox(height: 10),
|
||||
if (service.communicationChannelState !=
|
||||
CommunicationChannelState.loading)
|
||||
ActionButton(
|
||||
title: 'Start communicate',
|
||||
onTap: () => service.startCommunicationChannel(
|
||||
listener: (event) => MessagesListener.call(
|
||||
context,
|
||||
event,
|
||||
),
|
||||
onFilesSaved: (files) => FilesListener.call(
|
||||
context,
|
||||
files,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Text(
|
||||
'Connecting socket.. '
|
||||
'${service.isAndroidGroupOwner != null ? service.isAndroidGroupOwner! ? "Waiting a client for connect" : "Waiting a server for connect" : "Waiting a connection"}',
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
},
|
||||
final device = context.select<AppService, NearbyDevice?>(
|
||||
(service) => service.connectedDevice,
|
||||
);
|
||||
final isAndroidGroupOwner = context.select<AppService, bool?>(
|
||||
(service) => service.isAndroidGroupOwner,
|
||||
);
|
||||
final communicationChannelState =
|
||||
context.select<AppService, CommunicationChannelState>(
|
||||
(service) => service.communicationChannelState,
|
||||
);
|
||||
return device != null
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (!device.status.isConnected)
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Text('Connection lost'),
|
||||
),
|
||||
)
|
||||
else if (!device.status.isConnected)
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('Connection lost'),
|
||||
ActionButton(
|
||||
onTap: () {
|
||||
context.read<AppService>().connect(device);
|
||||
},
|
||||
title: 'Reconnect',
|
||||
),
|
||||
],
|
||||
)
|
||||
else
|
||||
DevicePreview(device: device, largeView: true),
|
||||
const SizedBox(height: 10),
|
||||
if (!communicationChannelState.isLoading)
|
||||
ActionButton(
|
||||
title: 'Start communicate',
|
||||
onTap: () =>
|
||||
context.read<AppService>().startCommunicationChannel(
|
||||
listener: (event) => MessagesListener.call(
|
||||
context,
|
||||
event,
|
||||
),
|
||||
onFilesSaved: (files) => FilesListener.call(
|
||||
context,
|
||||
files,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
Text(
|
||||
'Connecting socket.. '
|
||||
'${isAndroidGroupOwner != null ? isAndroidGroupOwner ? "Waiting a client for connect" : "Waiting a server for connect" : "Waiting a connection"}',
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,14 @@ class PermissionsView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<AppService>(builder: (context, service, _) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ActionButton(
|
||||
onTap: service.requestPermissions,
|
||||
title: 'Request permissions',
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ActionButton(
|
||||
onTap: context.read<AppService>().requestPermissions,
|
||||
title: 'Request permissions',
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:nearby_service/nearby_service.dart';
|
||||
import 'package:nearby_service_example_full/domain/app_service.dart';
|
||||
import 'package:nearby_service_example_full/uikit/uikit.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -32,29 +33,35 @@ class _PeersBody extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<AppService>(
|
||||
builder: (context, service, _) {
|
||||
return (service.peers != null && service.peers!.isNotEmpty)
|
||||
? Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
...service.peers!.map(
|
||||
(e) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: DevicePreview(device: e),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
Platform.isAndroid || service.isIOSBrowser
|
||||
? 'No one here ('
|
||||
: "Wait until someone invites you!",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
return Selector<AppService, bool>(
|
||||
selector: (context, service) => service.isIOSBrowser,
|
||||
builder: (context, isIOSBrowser, _) {
|
||||
return Selector<AppService, List<NearbyDevice>?>(
|
||||
selector: (context, service) => service.peers,
|
||||
builder: (context, peers, _) {
|
||||
return (peers != null && peers.isNotEmpty)
|
||||
? Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
...peers.map(
|
||||
(e) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: DevicePreview(device: e),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
Platform.isAndroid || isIOSBrowser
|
||||
? 'No one here ('
|
||||
: "Wait until someone invites you!",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,19 @@ import 'package:path_provider/path_provider.dart';
|
||||
class FilesSaver {
|
||||
FilesSaver._();
|
||||
|
||||
static bool isImage(String? extension) {
|
||||
return _imageExtensions.contains(extension?.toLowerCase());
|
||||
}
|
||||
|
||||
static const _imageExtensions = [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'webp',
|
||||
'bmp',
|
||||
];
|
||||
|
||||
static Future<List<NearbyFileInfo>> savePack(
|
||||
ReceivedNearbyFilesPack pack) async {
|
||||
final files = <NearbyFileInfo>[];
|
||||
|
||||
@@ -2,4 +2,14 @@
|
||||
/// The status of the communication channel for data exchange.
|
||||
/// Use it to determine if you can send data over the communication channel or not.
|
||||
///
|
||||
enum CommunicationChannelState { notConnected, loading, connected }
|
||||
enum CommunicationChannelState {
|
||||
notConnected,
|
||||
loading,
|
||||
connected;
|
||||
|
||||
bool get isNotConnected => this == CommunicationChannelState.notConnected;
|
||||
|
||||
bool get isLoading => this == CommunicationChannelState.loading;
|
||||
|
||||
bool get isConnected => this == CommunicationChannelState.connected;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user