Add All the files from the touchpad_demonstrator Flutter App which can be build for Android and iOS

This commit is contained in:
Richard Stern
2019-06-04 16:51:24 +02:00
parent 26fcc2491c
commit a951e9518d
1340 changed files with 12844936 additions and 0 deletions
BIN
View File
Binary file not shown.
Executable
+97
View File
@@ -0,0 +1,97 @@
import 'dart:async';
import 'package:flutter/material.dart';
//pages:
import 'package:touch_demonstrator/pages/PageTouchPoints.dart';
//bloc logic:
import 'package:touch_demonstrator/src/blocs/appBloc.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
import 'package:screen/screen.dart';
import 'package:splashscreen/splashscreen.dart';
/*import 'package:flutter_launcher_icons/android.dart';
import 'package:flutter_launcher_icons/constants.dart';
import 'package:flutter_launcher_icons/custom_exceptions.dart';
import 'package:flutter_launcher_icons/ios.dart';
import 'package:flutter_launcher_icons/main.dart';
import 'package:flutter_launcher_icons/xml_templates.dart';*/
const polyColor = Color.fromRGBO(163, 31, 37, 1.0);
void main() {
runApp(MaterialApp(home: TouchDemonstrator(AppBloc())));
// runApp(MaterialApp(home: SplashPage()));
}
class SplashPage extends StatefulWidget {
@override
_SplashPageState createState() => new _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
@override
Widget build(BuildContext context) {
return SplashScreen(
seconds: 2,
navigateAfterSeconds: TouchDemonstrator(AppBloc()),
title: Text('PolyIC Touchpad Demonstrator App',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.0
),),
image: Image.asset('assets/PolyIC/PolyIC_Logo.png'),
backgroundColor: Colors.white,
styleTextUnderTheLoader: TextStyle(),
photoSize: 100.0,
// onClick: ()=>print("Flutter Egypt"),
loaderColor: polyColor,
);
}
}
class TouchDemonstrator extends StatelessWidget {
// final AppBloc bloc;
final AppBloc bloc;
TouchDemonstrator(this.bloc);
Future<bool> _checkIfScreenIsKeptOn() async {
var isKeptOn = await Screen.isKeptOn;
return isKeptOn;
}
Future<double> _checkBrightness() async {
var brightness = await Screen.brightness;
print('brightness: $brightness');
if (brightness < 0.1) {
brightness = 0.5;
Screen.setBrightness(brightness);
}
brightness = await Screen.brightness;
return brightness;
}
@override
Widget build(BuildContext context) {
Screen.keepOn(true);
_checkIfScreenIsKeptOn()
.then((isKeptOn) => print('Screen is kept on?: $isKeptOn'));
_checkBrightness().then((brightnessScreen) => print('Brightness is: $brightnessScreen'));
return BlocProvider(
bloc: bloc,
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Touch Demonstrator', // only for Android; iOS can't be used!
theme: ThemeData(
primaryColor: Color.fromRGBO(163, 31, 37, 1.0), //PolyIC red
accentColor:
Color.fromRGBO(163, 31, 37, 0.8), //PolyIC red, 80% brightness
),
home: PageTouchPoints(title: 'Touch Demonstrator',),
),
);
}
}
+1
View File
@@ -0,0 +1 @@
enum buttonsState{pressed,released}
+19
View File
@@ -0,0 +1,19 @@
// The base class for the different types of items the List can contain
abstract class ListItem {}
class TouchData implements ListItem {
int event;
int fingerNumber;
int x;
int y;
TouchData(this.event, this.fingerNumber, this.x, this.y);
void touchEvent(int E, int X, int Y) {
this.event = E;
this.x = X;
this.y = Y;
}
}
+26
View File
@@ -0,0 +1,26 @@
import 'dart:async';
import 'package:vibrate/vibrate.dart';
bool canVibrate = false;
void vibrate(FeedbackType feedbackType) async{
canVibrate = await Vibrate.canVibrate;
print('_vibrate');
if (canVibrate) {
print('vibrate -- type: $feedbackType');
Vibrate.feedback(feedbackType);
}
}
void vibrateDelayed(FeedbackType feedbackType, Duration duration) async{
canVibrate = await Vibrate.canVibrate;
Future.delayed(duration,()
{
print('_vibrate');
if (canVibrate) {
print('vibrate -- type: $feedbackType');
Vibrate.feedback(feedbackType);
}
});
}
+146
View File
@@ -0,0 +1,146 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
import 'package:touch_demonstrator/model/touchData.dart';
import 'package:touch_demonstrator/src/blocs/bluetoothBloc.dart';
class DebugView extends StatefulWidget {
/// Screen that shows RAW data from the bluetooth device
@override
_DebugViewState createState() => _DebugViewState();
}
class _DebugViewState extends State<DebugView> {
ScrollController _scrollController;
static const TextStyle textStyleWhite = TextStyle(color: Colors.white);
static const TextStyle textStyleBlack = TextStyle(color: Colors.black);
@override
void initState() {
super.initState();
_scrollController = ScrollController();
}
void _scrollList() {
Future.delayed(Duration(milliseconds: 50), () {
_scrollController.animateTo(_scrollController.position.maxScrollExtent,
duration: new Duration(milliseconds: 500), curve: Curves.easeOut);
});
}
@override
Widget build(BuildContext context) {
final BluetoothBloc bloc = BlocProvider.of(context).bluetoothBlocGetter;
return Scaffold(
appBar: AppBar(
title: Text('Debug View Page'),
),
body: SafeArea(
child: Column(
children: <Widget>[
_buildHeaderDebugPage(context),
// Divider(),
StreamBuilder<List<TouchData>>(
key: Key('ListTouchData'),
stream: bloc.getHistory$,
builder: (BuildContext context,
AsyncSnapshot<List<TouchData>> snapshot) {
if (snapshot.hasData) {
_scrollList();
print('${snapshot.data.length}');
Color colorOfContainer;
return Expanded(
child: SizedBox(
height: 50.0,
child: ListView.builder(
reverse: false,
controller: _scrollController,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.length,
itemExtent: 33.0,
itemBuilder: (context, i) {
return buildRowOfTouchData(
colorOfContainer, i, snapshot);
}),
));
} else {
return Container();
}
}),
],
),
),
);
}
Container _buildHeaderDebugPage(BuildContext context) {
return Container(
color: Theme.of(context).accentColor,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
debugListItem('#', flexItem: 1),
debugListItem('Finger'),
debugListItem('Event'),
debugListItem('X'),
debugListItem('Y'),
],
),
),
);
}
Container buildRowOfTouchData(Color colorOfContainer, int _index,
AsyncSnapshot<List<TouchData>> snapshot) {
var _textStyleDebug;
var _fingerNrText = '${snapshot.data[_index].fingerNumber}';
var _eventText;
var _xText = '${snapshot.data[_index].x.toString().padLeft(4, ' ')}';
var _yText = '${snapshot.data[_index].y.toString().padLeft(4, ' ')}';
if ((snapshot.data[_index].event) == 1) {
_eventText = 'MOVE';
_textStyleDebug = textStyleBlack;
colorOfContainer = Colors.white;
} else {
_textStyleDebug = textStyleWhite;
if ((snapshot.data[_index].event) == 4) {
colorOfContainer = Colors.teal;
_eventText = 'DOWN';
} else if((snapshot.data[_index].event) == 5){
_eventText = 'UP';
colorOfContainer = Colors.purple;
} else {
print('Default ${snapshot.data[_index]}');
return Container();
}
}
return Container(
color: colorOfContainer,
child: Row(
children: <Widget>[
debugListItem('$_index', flexItem: 1, styleText: _textStyleDebug),
debugListItem(_fingerNrText, styleText: _textStyleDebug),
debugListItem(_eventText, styleText: _textStyleDebug),
debugListItem(_xText, styleText: _textStyleDebug),
debugListItem(_yText, styleText: _textStyleDebug),
],
),
);
}
Expanded debugListItem(String textItem,
{int flexItem = 2, TextStyle styleText = textStyleWhite}) {
return Expanded(
flex: flexItem,
child: Center(
child: Text(textItem, style: styleText),
),
);
}
}
+180
View File
@@ -0,0 +1,180 @@
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/AppBarPageTouchPoints.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/SideBar.dart';
import 'dart:core';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
import 'package:touch_demonstrator/ui/pageTouchPoints/ScanResultBottomSheet.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/ButtonOrAnimation.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/Logo.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/Sensor.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:vibrate/vibrate.dart';
import 'package:touch_demonstrator/model/vibrate.dart';
class PageTouchPoints extends StatefulWidget {
PageTouchPoints({Key key, this.title}) : super(key: key);
final String title;
@override
_PageTouchPointsState createState() => new _PageTouchPointsState();
}
class _PageTouchPointsState extends State<PageTouchPoints> {
final GlobalKey<ScaffoldState> mScaffoldState =
new GlobalKey<ScaffoldState>();
static Modal modal;
static bool firstTime = true;
@override
void dispose() {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
bBloc.disconnect.add(null); //disconnect everything
super.dispose();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print('didChangeDependencies');
/// Manages Snackbar "no device found" and "Bluetooth off" and toggles the debugmode.
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
final debugBloc = BlocProvider.of(context).debugBlocGetter;
_showSnackbar(String textInSnackBar) {
/// Shows Snackbar for 0.5 s with String textInSnackBar in it
print('snackbar $textInSnackBar');
mScaffoldState.currentState.showSnackBar(SnackBar(
content: Text(
textInSnackBar,
style: TextStyle(fontSize: 15),
),
duration: Duration(milliseconds: 1000),
));
}
_toggleDebug(bool debugStatus) {
vibrate(FeedbackType.success);
_showSnackbar('Debug View: ${debugStatus ? 'enabled' : 'disabled'}!');
}
if (firstTime) {
firstTime = false;
modal = new Modal();
bBloc.devicesFound$.listen((device) {
if (device.length == 0) {
print('Snackbar: No device found');
_showSnackbar('No device found');
vibrateDelayed(FeedbackType.warning, Duration(milliseconds: 150));
} else {
modal.connectBottomSheet(context, device);
}
});
debugBloc.debugEnabledStatus$.listen((data) => _toggleDebug(data));
bBloc.bluetoothState$
.where((bluetoothState) => bluetoothState == BluetoothState.off)
.listen(
(bluetoothState) => _showBluetoothOffSnackbar(bluetoothState));
bBloc.bluetoothState$
.where((bluetoothState) => bluetoothState == BluetoothState.on)
.listen((_) => mScaffoldState.currentState.removeCurrentSnackBar());
} else {
print('REMOVE current snack bar');
mScaffoldState.currentState.removeCurrentSnackBar();
}
}
_showBluetoothOffSnackbar(BluetoothState bluetoothState) {
/// Shows Snackbar if Bluetooth is off to inform user.
var textInSnackbar = 'Bluetooth is off!';
print('snackbar $textInSnackbar');
mScaffoldState.currentState.showSnackBar(SnackBar(
backgroundColor: Colors.red,
content: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(Icons.warning),
Padding(
padding: EdgeInsets.only(left: 10.0),
child: Text(
textInSnackbar,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 25),
),
),
],
),
duration: Duration(days: 1),
));
}
@override
Widget build(BuildContext context) {
/// build UI for the main screen in the app
return Scaffold(
key: mScaffoldState,
// necessary for toast message
appBar: buildAppBarPageTouchPoints(context),
drawer: LeftDrawer(),
body: SafeArea(child: OrientationBuilder(builder: (context, orientation) {
return _buildBody(orientation);
// return _buildVerticalLayout();
})),
);
}
_buildBody(Orientation orientation) {
if (orientation == Orientation.portrait)
return _buildVerticalLayout();
else
return _buildHorizontalLayout();
}
static const _heightLogo = 100.0;
_buildVerticalLayout() {
/// Layout for vertical orientation
EdgeInsets devicePadding = MediaQuery.of(context).padding;
return Padding(
padding: const EdgeInsets.all(5.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(height: devicePadding.top),
LogoWithText(_heightLogo),
Expanded(child: Sensor()),
ConnectButtonWithAnimations(),
],
),
);
}
_buildHorizontalLayout() {
/// Layout for horizontal orientation
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
LogoWithText(_heightLogo),
ConnectButtonWithAnimations(),
],
),
Column(
children: <Widget>[
Sensor(),
],
),
],
),
]),
);
}
}
+52
View File
@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:async';
class SideBarContent extends StatefulWidget {
final String html;
final String title;
SideBarContent({this.title, this.html});
@override
SideBarContentState createState() {
return new SideBarContentState();
}
}
class SideBarContentState extends State<SideBarContent> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${widget.title}'),
),
body: SafeArea(
child: FutureBuilder<String>(
future: LocalLoader().loadLocal(widget.html),
builder: (context, snapshot) {
if (snapshot.hasData) {
return WebView(
// HTML code loaded from file
initialUrl: new Uri.dataFromString(snapshot.data,
mimeType: 'text/html')
.toString(),
javascriptMode: JavascriptMode.unrestricted,
);
} else if (snapshot.hasError) {
return Text("Error trying to load HTML FIle: ${snapshot.error}");
} else {
return Container();
}
})));
}
}
class LocalLoader {
Future<String> loadLocal(String html) async {
String response = await rootBundle.loadString(html);
// return await rootBundle.loadString('assets/yourFile.html');
return response;
}
}
+62
View File
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/uiComponents.dart';
import 'package:touch_demonstrator/ui/pageTouchPoints/Logo.dart';
EdgeInsets devicePadding;
class StartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
devicePadding = MediaQuery.of(context).padding;
return Scaffold(
appBar: AppBar(
leading: null,
title: Text('Demo App'),
),
body: SafeArea(child: OrientationBuilder(builder: (context, orientation) {
return orientation == Orientation.portrait
? _buildVerticalLayout(context)
: _buildHorizontalLayout(context);
})),
);
}
}
const heightLogo = 100.0;
_buildVerticalLayout(BuildContext context) => Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: devicePadding.top,
),
TextAboveLogo(),
Logo(heightLogo),
TouchPad(),
// Sensor(),
Button(),
],
),
),
);
_buildHorizontalLayout(BuildContext context) => Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
TouchPad(),
// Sensor(),
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextAboveLogo(),
Logo(heightLogo),
Button(),
],
),
),
],
));
+14
View File
@@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/src/blocs/appBloc.dart';
class BlocProvider extends InheritedWidget {
final AppBloc bloc;
BlocProvider({Key key, this.bloc, child}) : super(key: key, child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static AppBloc of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).bloc;
}
+28
View File
@@ -0,0 +1,28 @@
import 'package:touch_demonstrator/src/blocs/bluetoothBloc.dart';
import 'package:touch_demonstrator/src/blocs/debugCounterBloc.dart';
//import 'package:touch_demonstrator/src/blocs/touchDataBloc.dart';
class AppBloc {
DebugBloc _debugBloc;
// EvenCounterBloc _evenCounter;
BluetoothBloc _bluetoothBloc;
// TouchDataBloc _touchDataInputBloc;
AppBloc()
: _debugBloc = DebugBloc(),
_bluetoothBloc = BluetoothBloc();
// _touchDataInputBloc = TouchDataBloc();
DebugBloc get debugBlocGetter => _debugBloc;
// EvenCounterBloc get evenCounterBloc => _evenCounter;
BluetoothBloc get bluetoothBlocGetter => _bluetoothBloc;
// TouchDataBloc get touchInBlocGetter => _touchDataInputBloc;
}
+505
View File
@@ -0,0 +1,505 @@
import 'dart:async';
import 'dart:convert';
import 'package:rxdart/rxdart.dart';
import 'package:rxdart/subjects.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:touch_demonstrator/model/touchData.dart';
import 'package:touch_demonstrator/model/buttonData.dart';
import 'package:flutter_test/flutter_test.dart';
class BluetoothBloc {
FlutterBlue _flutterBlue = FlutterBlue.instance;
/// Scanning
StreamSubscription _scanSubscription;
Map<DeviceIdentifier, ScanResult> scanResults = Map();
/// State
StreamSubscription _stateSubscription;
final _state$ = BehaviorSubject<BluetoothState>();
final _isScanning$ = PublishSubject<bool>();
final _isConnected$ = BehaviorSubject<bool>();
final _deviceFound$ = PublishSubject<Map<DeviceIdentifier, ScanResult>>();
// final _touchMessage$ = BehaviorSubject<String>(seedValue: 'init');
final _singleTouchMessage$ = BehaviorSubject<TouchData>();
final _touchDataHistory$ = BehaviorSubject<List<TouchData>>();
final _touchVisualise$ = BehaviorSubject<List<TouchData>>();
final _buttonEnabledStatus$ = BehaviorSubject<List<bool>>();
final _batteryValue$ = BehaviorSubject<int>();
final _buttonVibration$ = PublishSubject<buttonsState>();
final _slider$ = PublishSubject<int>();
String touchMessage = "";
/// Device
BluetoothDevice device;
/// Device specifications
StreamSubscription deviceConnection;
StreamSubscription deviceStateSubscription;
Map<Guid, StreamSubscription> valueChangedSubscriptions = {};
BluetoothDeviceState deviceState = BluetoothDeviceState.disconnected;
// Stream Controllers
final _scanController = StreamController<void>();
final _stopController = StreamController<void>();
final _connectController = StreamController<ScanResult>();
final _disconnectController = StreamController<void>();
//SINKS (into bloc)
Sink<void> get scan => _scanController.sink;
Sink<void> get stop => _stopController.sink;
Sink<ScanResult> get connect => _connectController.sink;
Sink<void> get disconnect => _disconnectController.sink;
//STREAMS (output bloc)
Stream<BluetoothState> get bluetoothState$ => _state$.stream;
Stream<int> get batteryValue$ => _batteryValue$.stream;
Stream<TouchData> get singleTouchMessage => _singleTouchMessage$.stream;
Stream<List<TouchData>> get getTouchesToVisualise$ =>
_touchVisualise$.stream; //->
Stream<List<bool>> get buttonsState$ => _buttonEnabledStatus$.stream;
Stream<buttonsState> get vibrationButton$ => _buttonVibration$.stream;
Stream<int> get slider$ => _slider$.stream;
Stream<List<TouchData>> get getHistory$ =>
_touchDataHistory$.stream.debounce(Duration(milliseconds: 600)); //->
Stream<bool> get isScanning$ => _isScanning$.stream;
Stream<bool> get isConnected$ => _isConnected$.stream;
Stream<Map<DeviceIdentifier, ScanResult>> get devicesFound$ =>
_deviceFound$.stream.throttle(Duration(seconds: 2));
static const xMax = 1200;
String inputString = "";
final history = List<TouchData>();
static List<TouchData> _fingerTouches =
List.generate(5, (index) => TouchData(5, index, 1200, 1200));
static List<buttonsState> _buttonPressed = [
buttonsState.released,
buttonsState.released,
buttonsState.released
];
static List<bool> _buttonsState = [false, false, false];
// Listen to
BluetoothBloc() {
print('init: Bluetooth BLOC');
_flutterBlue.setLogLevel(LogLevel.warning);
// Listen to BLoC inputs
_flutterBlue.state.then((s) {
_state$.add(s);
});
_stateSubscription = _flutterBlue.onStateChanged().listen((s) {
_state$.add(s);
if (s == BluetoothState.off) {
_resetSensor();
}
});
_scanController.stream.listen((void _) {
_startScan();
});
_stopController.stream.listen((void _) => _stopScan);
_connectController.stream
.listen((ScanResult result) => _connect(result.device));
_disconnectController.stream.listen((void _) {
_disconnect();
});
_isConnected$.add(false);
}
void _startScan() {
print('Bluetooth Bloc: start Bluetooth Scan');
scanResults.clear();
_isScanning$.add(true);
_scanSubscription = _flutterBlue
.scan(
timeout: const Duration(seconds: 1),
)
.listen((scanResult) {
// print('Local Name: ${scanResult.advertisementData.localName}');
scanResults[scanResult.device.id] = scanResult;
// print(scanResult.device.id);
}, onDone: _stopScan);
}
void _stopScan() {
/// Stops any ongoing Bluetooth Search
print('BLOC: _stopScan()');
_scanSubscription?.cancel();
_scanSubscription = null;
_findDevice("Touchpad Demonstrator");
}
void _disconnect() {
/// Disconnects all Bluetooth connections
// Remove all value changed listeners
print('Bluetooth Bloc: disconnect');
valueChangedSubscriptions.forEach((uuid, sub) => sub.cancel());
valueChangedSubscriptions.clear();
deviceStateSubscription?.cancel();
deviceStateSubscription = null;
deviceConnection?.cancel();
deviceConnection = null;
device = null;
_resetSensor();
}
void _resetSensor() {
/// Resets all visual components of the app to initial state
_buttonsState = [false, false, false];
_slider$.add(101);
_buttonEnabledStatus$.add(_buttonsState);
_isConnected$.add(false);
_fingerTouches = [
TouchData(5, 0, 1200, 1200),
TouchData(5, 1, 1200, 1200),
TouchData(5, 2, 1200, 1200),
TouchData(5, 3, 1200, 1200),
TouchData(5, 4, 1200, 1200),
];
_touchVisualise$.add(_fingerTouches);
history.clear();
_touchDataHistory$.add(history);
_batteryValue$.add(null);
}
_findDevice(String deviceNameSearched) {
/// If one device found -> connect to it. If more are found -> Stream the results
var devicesFound = 0;
BluetoothDevice deviceFound;
// const String touchDemonstratorName = "Touchpad Demonstrator";
print("_findDevices()");
scanResults.forEach((k, v) {
if (v.advertisementData.localName.contains(deviceNameSearched)) {
devicesFound++;
deviceFound = v.device;
}
});
print('$devicesFound devices Found');
if (devicesFound == 1) {
// Only one device found -> connect directly.
_connect(deviceFound);
// _deviceFound$.add(scanResults);
} else if (devicesFound > 1) {
// More than one device found.
_deviceFound$.add(scanResults);
} else if (devicesFound == 0) {
// No device found.
scanResults.clear();
_deviceFound$.add(scanResults);
}
Future.delayed(const Duration(milliseconds: 700), () {
_isScanning$.add(false);
});
}
void _connect(BluetoothDevice d) async {
/// Connects device to BluetoothDevice D and subscribes to it.
print('_connect');
device = d;
deviceConnection = _flutterBlue
.connect(device, timeout: const Duration(seconds: 2))
.listen(null, onDone: _disconnect);
device.state.then((s) {
deviceState = s;
print('device state: $deviceState'); //change it immediately
});
// Subscribe to connection changes
deviceStateSubscription = device.onStateChanged().listen((s) {
print('statesubscription change: $s');
deviceState = s;
if (s == BluetoothDeviceState.connected) {
///discover services
device.discoverServices().then((services) {
// services = s;
_checkServiceAndCharacteristic(services);
});
} else if (s == BluetoothDeviceState.disconnected) {
_disconnect();
}
if (device != null) {
_isConnected$.add(true);
}
});
}
_checkServiceAndCharacteristic(List<BluetoothService> services) {
print("_lookForService()");
services.forEach((s) {
print(s.uuid.toString());
if (s.uuid.toString().toUpperCase().substring(4, 8) == "0001") {
// Bluetooth UART characteristic found!
print(
"Service found: ${s.uuid.toString().toUpperCase().substring(4, 8)} -> check Characteristic");
s.characteristics.forEach((c) {
if (c.uuid.toString().toUpperCase().substring(4, 8) == "0003") {
/* print(
"Characteristic found: ${f.uuid.toString().toUpperCase().substring(4, 8)}");*/
_subscribeTouchDataNotification(c);
}
});
}
else if(s.uuid.toString().toUpperCase().substring(4,8) == "180F"){
print('battery service found');
s.characteristics.forEach((c){
if(c.uuid.toString().toUpperCase().substring(4,8) == "2A19"){
_subscribeBatteryNotfications(c);
}
});
}
});
}
_subscribeBatteryNotfications(BluetoothCharacteristic c) async {
print('_subscribe battery: ${c.isNotifying}');
if (c.isNotifying) {
// Unsubscribe from subscription
await device.setNotifyValue(c, false);
valueChangedSubscriptions[c.uuid]?.cancel();
valueChangedSubscriptions.remove(c.uuid);
} else {
print('subscribe battery');
var _batteryValue = await device.readCharacteristic(c);
assert(_batteryValue[0] >= 0 && _batteryValue[0] <= 100);
print('battery right now: ${_batteryValue[0]} %');
_batteryValue$.add(_batteryValue[0]);
// ignore: cancel_subscriptions
final subBattery = device.onValueChanged(c).listen((d) {
print("battery: $d");
});
valueChangedSubscriptions[c.uuid] = subBattery;
}
}
_subscribeTouchDataNotification(BluetoothCharacteristic c) async {
if (c.isNotifying) {
// Unsubscribe from subscription
await device.setNotifyValue(c, false);
valueChangedSubscriptions[c.uuid]?.cancel();
valueChangedSubscriptions.remove(c.uuid);
} else {
// ignore: cancel_subscriptions
final sub = device.onValueChanged(c).listen((d) {
List<int> dFiltered = new List.from(d); // Copy List
dFiltered.removeWhere((item) =>
item < 48 && item != 40 && item != 41 && item != 24 ||
item > 57 && item != 124);
var dDecoded = utf8.decode(dFiltered);
_combineStringToMeasurement(dDecoded);
});
valueChangedSubscriptions[c.uuid] = sub;
}
}
void _combineStringToMeasurement(String dDecoded) {
// Combines one data set.
for (var x = 0; x < dDecoded.length; x++) {
if (touchMessage.length < 15) {
if (touchMessage.length == 0) {
if (dDecoded[x] == '(') {
touchMessage += dDecoded[x];
}
} else {
touchMessage += dDecoded[x];
}
}
if (touchMessage.length == 15) {
_extractTouchpointsFromString(touchMessage);
touchMessage = "";
} else if (touchMessage.length > 15) {
touchMessage = ""; // clear string
}
}
}
void _extractTouchpointsFromString(String inputString) {
// Gets data from data string and saves it
TouchData t = TouchData(0, 0, 0, 0);
final RegExp regExp = new RegExp(
r"\(\d\|\d\|\d{4}\|\d{4}\)",
caseSensitive: false,
multiLine: false,
);
var matches = regExp.allMatches(inputString);
for (var m in matches) {
t.event = int.tryParse(m.group(0)[1]);
t.fingerNumber = int.tryParse(m.group(0)[3]);
t.x = int.tryParse(m.group(0).substring(5, 9));
t.y = int.tryParse(m.group(0).substring(10, 14));
if (t.x < 1100 &&
t.y < 1100 &&
(t.fingerNumber <= 4) &&
(t.event == 1 || t.event == 4 || t.event == 5)) {
_checkButtons(t);
if (t.x >= 950) {
_checkSlider(t);
}
_fillSensorVisualisationStreams(t);
} else {
// print("Touch not Added!!!!!!!!!!!! $i ${t.e} ${t.f} ${t.x} ${t.y}");
}
}
}
void _fillSensorVisualisationStreams(TouchData t) {
_singleTouchMessage$.add(t);
_fingerTouches[t.fingerNumber].touchEvent(t.event, t.x, t.y);
_touchVisualise$.add(_fingerTouches);
//History:
history.add(t);
_touchDataHistory$.add(history);
}
static bool enableButtons = true;
Future _checkButtons(TouchData t) async {
// 4 == press down; 1 == move; 5 == up
final int button0Upper = 130, button0Lower = 320;
final int button1Upper = 320, button1Lower = 510;
final int button2Upper = 510, button2Lower = 730;
const Duration durationButtonDebounce = Duration(milliseconds: 50);
const Duration durationButtonPressMinimum = Duration(milliseconds: 100);
if (t.x < 100 &&
t.y >= button0Upper &&
t.y <= button2Lower &&
enableButtons) {
if (t.event == 5) {
// a button is released!
if (t.y >= button0Upper &&
t.y <= button0Lower &&
_buttonPressed[0] == buttonsState.pressed) {
_buttonsState[0] = !_buttonsState[0];
_buttonsState[1] = _buttonsState[2] = false;
_buttonPressed[0] =
buttonsState.released; // wait for a new button press now
print('bBloc: button released -> ButtonStatus: $_buttonsState');
} else if (t.y >= button1Upper &&
t.y <= button1Lower &&
_buttonPressed[1] == buttonsState.pressed) {
_buttonsState[1] = !_buttonsState[1];
_buttonsState[0] = _buttonsState[2] = false;
_buttonPressed[1] =
buttonsState.released; // wait for a new button press now
print('bBloc: button released -> ButtonStatus: $_buttonsState');
} else if (t.y >= button2Upper &&
t.y <= button2Lower &&
_buttonPressed[2] == buttonsState.pressed) {
_buttonsState[2] = !_buttonsState[2];
_buttonsState[0] = _buttonsState[1] = false;
_buttonPressed[2] =
buttonsState.released; // wait for a new button press now
print('bBloc: button released -> ButtonStatus: $_buttonsState');
}
_buttonEnabledStatus$.add(_buttonsState); // update visuals
_buttonVibration$.add(buttonsState.released);
enableButtons = false;
Future.delayed(durationButtonDebounce, () {
enableButtons = true;
});
} else if (t.event == 4) {
// finger pressed on a button -> send event out
var whichButtonPressed;
if (t.y >= button0Upper &&
t.y <= button0Lower &&
_buttonPressed[0] == buttonsState.released) {
whichButtonPressed = 0;
} else if (t.y >= button1Upper &&
t.y <= button1Lower &&
_buttonPressed[1] == buttonsState.released) {
whichButtonPressed = 1;
} else if (t.y >= button2Upper &&
t.y <= button2Lower &&
_buttonPressed[2] == buttonsState.released) {
whichButtonPressed = 2;
}
if (whichButtonPressed != null &&
whichButtonPressed >= 0 &&
whichButtonPressed <= 2) {
print('bBloc: Button pressed!');
_buttonVibration$.add(buttonsState.pressed);
Future.delayed(durationButtonPressMinimum, () {
_buttonPressed[whichButtonPressed] = buttonsState.pressed;
});
}
}
}
}
Future _checkSlider(TouchData t) async {
const double topSlider = 75.0;
const double bottomSlider = 940.0;
int sliderPercent;
const double range = bottomSlider - topSlider;
double sliderPosition;
double sliderValue;
if ((t.event == 4 || t.event == 1)) {
if (t.y > topSlider) {
sliderPosition = t.y - topSlider;
} else {
sliderPosition = 0;
}
sliderValue = sliderPosition / range; // 0...1
sliderPercent = 100 - (sliderValue * 100).round(); // %
if (sliderPercent > 100) sliderPercent = 100;
if (sliderPercent < 0) sliderPercent = 0;
_slider$.add(sliderPercent);
}
}
void dispose() {
print('cleanup');
_disconnect();
_scanController.close();
_stopController.close();
_connectController.close();
_disconnectController.close();
_touchDataHistory$.close();
_touchVisualise$.close();
_buttonEnabledStatus$.close();
_buttonVibration$.close();
_slider$.close();
_batteryValue$.close();
_isScanning$.close();
_isConnected$.close();
_singleTouchMessage$.close();
_stateSubscription?.cancel();
_stateSubscription = null;
_scanSubscription?.cancel();
_scanSubscription = null;
deviceConnection?.cancel();
deviceConnection = null;
}
int checkButtonPressed(TouchData t) {
return 1;
}
int parseData(String s) {
// _parseData(s);
return 1;
}
}
+42
View File
@@ -0,0 +1,42 @@
import 'dart:async';
import 'package:rxdart/rxdart.dart';
import 'package:rxdart/subjects.dart';
class DebugBloc {
int _counter = 0;
bool _debugScreenEnabled = false;
static const debugSwitchValue = 3;
final _incrementController = StreamController<void>();
final _debugDisableController = StreamController<void>();
final _debugEnabledStatus$ = BehaviorSubject<bool>();
DebugBloc() {
print('init: Debug BLOC');
_incrementController.stream.listen((void _) {
_counter = _counter + 1;
print('counterBloc: debug counter = $_counter / $debugSwitchValue');
if(_counter % debugSwitchValue == 0){
_debugScreenEnabled = !_debugScreenEnabled;
_debugEnabledStatus$.add(_debugScreenEnabled);
_counter = 0;
}
});
_debugDisableController.stream.listen((void _){
if(_debugScreenEnabled){
_counter = 0;
_debugScreenEnabled = false;
_debugEnabledStatus$.add(_debugScreenEnabled);
}
});
}
Sink<void> get debugCounterIncrement => _incrementController.sink;
Sink<void> get debugDisable => _debugDisableController.sink;
Stream<bool> get debugEnabledStatus$ => _debugEnabledStatus$.stream;
void dispose() {
_incrementController.close();
_debugDisableController.close();
_debugEnabledStatus$.close();
}
}
+104
View File
@@ -0,0 +1,104 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'package:rxdart/subjects.dart';
import 'package:touch_demonstrator/model/touchData.dart';
class TouchDataBloc {
final int xMax = 1200;
String inputString = "";
final history = List<TouchData>();
// final _touchData$ = BehaviorSubject<TouchData>(); // Touch Data from the input String
final _touchData$ = PublishSubject<TouchData>(); // Touch Data from the input String
final _touchVisualise$ = BehaviorSubject<Map<TouchData, Color>>();
final _touchDataHistory$ = BehaviorSubject<List<TouchData>>(); //All touches
// Controller Input of Data
final _inputTouchDataController = StreamController<String>();
// Controller for outputting
//SINKS (into bloc)
Sink<String> get inputTouchData => _inputTouchDataController.sink;
//STREAMS (output bloc)
Stream<TouchData> get outputTouchData$ => _touchData$.stream; //->
Stream<Map<TouchData,Color>> get outputVisualise$ => _touchVisualise$.stream; //->
Stream<List<TouchData>> get outputHistory$ => _touchDataHistory$.stream; //->
void dispose() {
_inputTouchDataController.close();
_touchData$.close();
_touchVisualise$.close();
_touchDataHistory$.close();
// _touchDataHistory$.close();
}
static List<Color> _colorOfTouches = [
Colors.blue,
Colors.red,
Colors.orange,
Colors.white,
Colors.yellow,
];
static List<TouchData> _lastFingerTouch = [
TouchData(5, 0, 1200, 1200),
TouchData(5, 1, 1200, 1200),
TouchData(5, 2, 1200, 1200),
TouchData(5, 3, 1200, 1200),
TouchData(5, 4, 1200, 1200),
];
Map<TouchData, Color> touchCollection = {
_lastFingerTouch[0]: _colorOfTouches[0],
_lastFingerTouch[1]: _colorOfTouches[1],
_lastFingerTouch[2]: _colorOfTouches[2],
_lastFingerTouch[3]: _colorOfTouches[3],
_lastFingerTouch[4]: _colorOfTouches[4],
};
TouchDataBloc() {
print('init: Touch Data BLOC');
_inputTouchDataController.stream.listen(_testTouchEventData);
}
void _updateStreams(TouchData t){
/* print("<- Update Streams (${t.e}|${t.f}|${t.x.toString().padLeft(4,'0')}|"
"${t.y.toString().padLeft(4,'0')})");*/
_lastFingerTouch[t.fingerNumber].touchEvent(t.event, t.x, t.y);
_touchData$.add(t); // Stream of single touches
_touchVisualise$.add(touchCollection);
//new:
history.add(t);
_touchDataHistory$.add(history);
}
void _testTouchEventData(String i) {
print('_testTouchEventData');
TouchData t = TouchData(0, 0, 0, 0);
inputString = i;
// print("input: $inputString");
RegExp regExp = new RegExp(
r"\(\d{1}\|\d{1}\|\d{4}\|\d{4}\)",
caseSensitive: false,
multiLine: false,
);
var matches = regExp.allMatches(inputString);
for (var m in matches) {
// print('${m.group(0)}');
t.event = int.tryParse(m.group(0)[1]);
t.fingerNumber = int.tryParse(m.group(0)[3]);
t.x = int.tryParse(m.group(0).substring(5, 9));
t.y = int.tryParse(m.group(0).substring(10, 14));
if (t.x < 1024 && t.y < 1024 && (t.fingerNumber <=4) && (t.event==1 || t.event==4 || t.event==5)){
_updateStreams(t);
print('straem update');
}
else{
print("Touch not Added!!!!!!!!!!!!");
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
class NoAnimationMaterialPageRoute<T> extends MaterialPageRoute<T> {
NoAnimationMaterialPageRoute({
@required WidgetBuilder builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
}) : super(
builder: builder,
maintainState: maintainState,
settings: settings,
fullscreenDialog: fullscreenDialog);
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return child;
}
}
+69
View File
@@ -0,0 +1,69 @@
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
import 'package:touch_demonstrator/pages/DebugPage.dart';
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/src/blocs/bluetoothBloc.dart';
import 'package:touch_demonstrator/src/blocs/debugCounterBloc.dart';
Widget buildAppBarPageTouchPoints(BuildContext context) {
void _pageRouteDebugView() {
print('Debug button pressed');
Navigator.push(
context, MaterialPageRoute(builder: (context) => DebugView()));
}
IconButton _buildDebugIconButton() {
return IconButton(
key: Key('debugButton'),
icon: Icon(Icons.bug_report),
onPressed: _pageRouteDebugView,
);
}
StreamBuilder<bool> titleAppBar() {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
var appBarText;
return StreamBuilder<bool>(
stream: bBloc.isConnected$,
initialData: false,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
snapshot.data
? appBarText = 'Connected'
: appBarText = 'Disconnected';
return AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> opacity) {
return FadeTransition(child: child, opacity: opacity);
},
child: Text(
'$appBarText',
key: ValueKey<String>(appBarText),
textDirection: TextDirection.ltr,
),
);
} else
return Text('Disconnected');
});
}
List<Widget> actionsAppBar() {
final debugBloc = BlocProvider.of(context).debugBlocGetter;
return <Widget>[
StreamBuilder<bool>(
stream: debugBloc.debugEnabledStatus$,
initialData: false,
builder: (BuildContext context, AsyncSnapshot<bool> snapshotDebug) {
if (snapshotDebug.hasData) {
return snapshotDebug.data ? _buildDebugIconButton() : Container();
} else
return Container();
},
),
];
}
return AppBar(
title: titleAppBar(),
actions: actionsAppBar(),
);
}
+126
View File
@@ -0,0 +1,126 @@
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
import 'package:flare_flutter/flare_actor.dart';
class ConnectButtonWithAnimations extends StatefulWidget {
@override
ConnectButtonWithAnimationsState createState() {
return new ConnectButtonWithAnimationsState();
}
}
class ConnectButtonWithAnimationsState
extends State<ConnectButtonWithAnimations>
with SingleTickerProviderStateMixin {
/// Builds Button if the app is not connected to a touch pad or animates search progress.
Animation animation;
AnimationController animationController;
static const _heightContainer = 60.0;
static const _widthContainer = 170.0;
static const EdgeInsets _containerEdgeInset = EdgeInsets.all(8.0);
static const durationOpacity = Duration(milliseconds: 150);
@override
Widget build(BuildContext context) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
return Hero(
tag: 'buttonHero',
child: Container(
margin: _containerEdgeInset,
height: _heightContainer,
width: _widthContainer,
child: StreamBuilder<BluetoothState>(
stream: bBloc.bluetoothState$,
initialData: BluetoothState.on,
builder: (_, AsyncSnapshot<BluetoothState> snapshot) {
if (snapshot.hasData) {
if (snapshot.data == BluetoothState.on) {
// Bluetooth is on, button enabled.
return StreamBuilder<bool>(
stream: bBloc.isScanning$,
initialData: false,
builder: (_, AsyncSnapshot<bool> snapshotIsScanning) {
if (snapshotIsScanning.hasData) {
return AnimatedSwitcher(
duration: durationOpacity,
transitionBuilder:
(Widget child, Animation<double> opacity) {
return Padding(
padding: EdgeInsets.all(8.0),
child: FadeTransition(
child: child, opacity: opacity),
);
},
child: (snapshotIsScanning.data)
? ScanAnimation()
: ConnectButton());
} else
return Container();
});
} else {
// Bluetooth is off, button disabled.
return Container();
}
}
}),
),
);
}
}
class ScanAnimation extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FlareActor(
'assets/Animations/loading2.flr',
animation: 'Loading',
fit: BoxFit.contain,
alignment: Alignment.center,
isPaused: false,
);
}
}
class ConnectButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
var _primaryColor = Theme.of(context).primaryColor;
var _buttonTextStyle = TextStyle(color: _primaryColor, fontSize: 20.0);
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
var _buttonText;
var _buttonFunction;
return StreamBuilder<bool>(
stream: bBloc.isConnected$,
initialData: false,
builder: (_, AsyncSnapshot<bool> snapshotIsConnected) {
if (snapshotIsConnected.hasData) {
if (snapshotIsConnected.data) {
_buttonText = Text(
"Disconnect",
style: _buttonTextStyle,
key: Key('DisconnectButton'), // for integration test
);
_buttonFunction = () => bBloc.disconnect.add(null);
} else {
_buttonText = Text(
"Connect",
style: _buttonTextStyle,
key: Key('ConnectButton'), // for integration test
);
_buttonFunction = () => bBloc.scan.add(null);
}
return Center(
child: OutlineButton(
key: Key('ConnectionButton'), // for integration test
borderSide: BorderSide(color: _primaryColor),
shape: BeveledRectangleBorder(),
onPressed: _buttonFunction,
child: _buttonText,
),
);
} else
return Container();
});
}
}
+65
View File
@@ -0,0 +1,65 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
class LogoWithText extends StatelessWidget {
final _heightLogo;
LogoWithText(this._heightLogo);
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TextAboveLogo(),
Logo(_heightLogo),
],
);
}
}
class Logo extends StatelessWidget {
final _heightLogo;
Logo(this._heightLogo);
_buildLogo() {
// returns Logo of PolyIC
return Container(
key: Key('Logo'), // for integration test
height: _heightLogo,
child: Center(
child: Image.asset(
'assets/PolyIC/PolyIC_Logo.png',
),
),
);
}
@override
Widget build(BuildContext context) {
final debugBloc = BlocProvider.of(context).debugBlocGetter;
return Hero(
tag: 'logoHero',
child: Padding(
padding: EdgeInsets.all(10.0),
child: GestureDetector(
onTap: () => debugBloc.debugCounterIncrement.add(null),
// onLongPress: () => debugBloc.debugEnable.add(null),
child: _buildLogo()),
),
);
}
}
class TextAboveLogo extends StatelessWidget {
/// Line of text above the logo that adds information to it.
static const _textStyle = TextStyle(fontSize: 18);
static const _buildText = Text('Touchpad Demonstrator by',
textDirection: TextDirection.ltr, style: _textStyle);
@override
Widget build(BuildContext context) => _buildText;
}
+101
View File
@@ -0,0 +1,101 @@
import 'package:flutter_blue/flutter_blue.dart';
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
class Modal {
_buildScanResult(
BuildContext context, Map<DeviceIdentifier, ScanResult> scanResults) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
return scanResults.values
.where((result) => result.advertisementData.localName
.contains('Touchpad Demonstrator'))
.map((result) => ScanResultTile(
result: result,
onTapCallback: () {
bBloc.connect.add(result);
print('Connect to ${result.device.name} pressed!');
Navigator.pop(context);
}))
.toList();
}
_buildHeaderBottomSheet(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor,
child: ListTile(
title: Center(
child: Text(
"CHOOSE TOUCHPAD",
style: TextStyle(color: Colors.white),
),
),
),
);
}
connectBottomSheet(
BuildContext context, Map<DeviceIdentifier, ScanResult> scanResults) {
print('Modal Sheet - Result Count: ${scanResults.length}');
var _bottomSheetElements = List<Widget>();
_bottomSheetElements.add(_buildHeaderBottomSheet(context));
_bottomSheetElements.addAll(_buildScanResult(context, scanResults));
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
child: Padding(
padding: EdgeInsets.all(0.0),
child: ListView(
shrinkWrap: true, children: _bottomSheetElements)));
});
}
}
class ScanResultTile extends StatelessWidget {
final ScanResult result;
final VoidCallback onTapCallback;
const ScanResultTile({Key key, this.result, this.onTapCallback})
: super(key: key);
_buildTitle(BuildContext context) {
var textOfTile;
(result.device.name.length > 0)
? textOfTile = result.device.name
: textOfTile = result.device.id.toString();
return Flexible(
child: Container(
child: Text(
textOfTile,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
)));
}
_buildConnectButton(BuildContext context) {
return OutlineButton(
borderSide: BorderSide(color: Theme.of(context).primaryColor),
shape: BeveledRectangleBorder(),
onPressed: (result.advertisementData.connectable) ? onTapCallback : null,
child: Text('CONNECT'),
); // Name of devices
}
@override
Widget build(BuildContext context) {
return Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_buildTitle(context),
_buildConnectButton(context),
],
),
),
);
}
}
+582
View File
@@ -0,0 +1,582 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/model/buttonData.dart';
import 'package:touch_demonstrator/model/touchData.dart';
import 'package:touch_demonstrator/model/vibrate.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart';
import 'package:flare_flutter/flare_actor.dart';
import 'dart:core';
import 'package:vibrate/vibrate.dart';
Duration durationSensorAnimation = Duration(milliseconds: 200);
class Battery extends StatelessWidget {
@override
Widget build(BuildContext context) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
return Positioned(
bottom: 0,
left: 0,
child: StreamBuilder<int>(
stream: bBloc.batteryValue$,
initialData: null,
builder: (BuildContext context, AsyncSnapshot<int> snapshotBattery) {
return Container(
padding: EdgeInsets.all(0.0),
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(3.0),
child: (snapshotBattery.data == null)
? Container()
: Container(
padding: EdgeInsets.all(4.0),
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: new BorderRadius.all(
const Radius.circular(3.0))),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Icon(
Icons.battery_full,
color: Colors.white,
),
Text(
'${snapshotBattery.data.toString()}%',
style: TextStyle(color: Colors.white),
),
],
),
),
),
);
}),
);
}
}
class Sensor extends StatefulWidget {
@override
SensorState createState() {
return SensorState();
}
}
class SensorState extends State<Sensor> {
static var animationDisabled = false;
static var firstTimeDidChangeDependencies = true;
static var canVibrate = false;
_drawButtons(
BuildContext context, double _heightSensor, double _widthSensor) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
final _buttonsPosY = [0.74 * _heightSensor, 0.57 * _heightSensor, 0.39 * _heightSensor];
final _buttonsPosX = 0.074 * _widthSensor;
final _buttonWidth = 0.08 * _widthSensor;
return StreamBuilder<List<bool>>(
stream: bBloc.buttonsState$,
initialData: [false, false, false],
builder:
(BuildContext context, AsyncSnapshot<List<bool>> snapshotButtons) {
if (snapshotButtons.hasData) {
return Stack(
children: <Widget>[
_singleButton(0, _buttonsPosX, _buttonsPosY[0], _buttonWidth,
snapshotButtons.data[0]),
_singleButton(1, _buttonsPosX, _buttonsPosY[1], _buttonWidth,
snapshotButtons.data[1]),
_singleButton(2, _buttonsPosX, _buttonsPosY[2], _buttonWidth,
snapshotButtons.data[2])
],
);
} else
return Container();
});
}
_singleButton(int buttonNumber, double _buttonPosX,
double _buttonPosY, double _widthButton, bool _isPressed) {
///draws a button of Type home(0), multitasking(1) or back(2).
assert(buttonNumber >= 0 && buttonNumber <= 2);
Color buttonColor;
_isPressed
? buttonColor = Theme.of(context).primaryColor
: buttonColor = Colors.white;
if (buttonNumber >= 3) return Container();
return AnimatedPositioned(
duration: durationSensorAnimation,
left: _buttonPosX,
bottom: _buttonPosY,
child: Image(
image: AssetImage('assets/buttons/button$buttonNumber.png'),
color: buttonColor,
width: _widthButton,
),
);
}
_notConnectedText() {
return Positioned.fill(
child: Stack(
children: <Widget>[
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 0.3,
sigmaY: 0.3,
),
child: Container(color: Colors.black.withOpacity(0)),
),
Center(
child: Container(
padding: EdgeInsets.all(15.0),
decoration: BoxDecoration(
color: Colors.black45,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(8.0),
),
child: Text(
'Not connected to a device',
style: TextStyle(color: Colors.white),
))),
],
));
}
_successAnimation() {
if (animationDisabled) {
print('no animation');
return Container();
}
print('animation');
return Container(
padding: EdgeInsets.all(80.0),
child: Center(
child: FlareActor(
'assets/Animations/success_hide.flr',
animation: 'success',
fit: BoxFit.contain,
alignment: Alignment.center,
isPaused: false,
),
),
);
}
_middleSensor(
BuildContext context, double _heightSensor, double _widthSensor) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
final _marginRight = _widthSensor * 0.168;
final _marginLeft = _widthSensor * 0.18;
return StreamBuilder<List<TouchData>>(
stream: bBloc.getTouchesToVisualise$,
initialData: [TouchData(5, 0, 1200, 1200)],
builder: (BuildContext context,
AsyncSnapshot<List<TouchData>> snapshotTouches) {
if (snapshotTouches.hasData) {
return Container(
height: _heightSensor,
width: _widthSensor,
margin: EdgeInsets.only(left: _marginLeft, right: _marginRight),
child: MiddleOfSensor(
snapshotTouches.data, context, _heightSensor, _widthSensor),
// CustomPaint( foregroundPainter:TouchPainter(context, snapshotTouches.data), ),
);
} else {
return Container();
}
});
}
_sensorImage(){
return BoxDecoration(
image: DecorationImage(
image: ExactAssetImage('assets/sensor.png'),
fit: BoxFit.fitHeight),
);
}
@override
Widget build(BuildContext context) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
final widthOfScreen = MediaQuery.of(context).size.width;
final heightOfScreen = MediaQuery.of(context).size.height;
var _heightSensorConnected;
var _widthSensorConnected;
var _heightSensorDisconnected;
var _widthSensorDisconnected;
(heightOfScreen > widthOfScreen)
? _widthSensorConnected = _heightSensorConnected = widthOfScreen * 0.8
: _heightSensorConnected = _widthSensorConnected = heightOfScreen * 0.8;
_heightSensorDisconnected = _heightSensorConnected * 0.8;
_widthSensorDisconnected = _widthSensorConnected * 0.8;
return StreamBuilder(
stream: bBloc.isConnected$,
initialData: false,
builder:
(BuildContext context, AsyncSnapshot<bool> snapshotIsConnected) {
if (snapshotIsConnected.hasData) {
return Hero(
tag: 'touchpadHero',
child: AnimatedOpacity(
duration: durationSensorAnimation,
opacity: snapshotIsConnected.data ? 1.0 : 0.8,
child: Container(
// margin: EdgeInsets.only(left: 10.0, right: 10.0),
width: _widthSensorConnected,
height: _heightSensorConnected,
child: Center(
child: AnimatedContainer(
duration: durationSensorAnimation,
decoration: _sensorImage(),
height: snapshotIsConnected.data
? _heightSensorConnected
: _heightSensorDisconnected,
width: snapshotIsConnected.data
? _widthSensorConnected
: _widthSensorDisconnected,
child: Stack(
children: <Widget>[
_middleSensor(context, _heightSensorConnected, _widthSensorConnected),
snapshotIsConnected.data
? _drawButtons(
context, _heightSensorConnected, _widthSensorConnected)
: _drawButtons(context, _heightSensorDisconnected,
_widthSensorDisconnected),
DrawSlider(context, _widthSensorConnected, _heightSensorConnected),
snapshotIsConnected.data
? _successAnimation()
: _notConnectedText(),
Battery(),
],
),
),
),
),
),
);
} else
return Container();
});
}
void _disableSuccessAnimation(bool isConnected) {
// If already are connected to a device -> prevent success animation from showing again.
// This would happens if you change orientation of device.
if (isConnected) {
vibrateDelayed(FeedbackType.success,Duration(milliseconds: 50));
Future.delayed(Duration(seconds: 1), () {
animationDisabled = true;
});
} else {
// Activate success animation for next connection.
animationDisabled = false;
}
}
void _vibrateDependingOnButtonEvent(buttonsState buttonState) {
(buttonState == buttonsState.pressed)
? vibrate(FeedbackType.selection)
: vibrate(FeedbackType.impact);
}
@override
void didChangeDependencies() {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
print('didchangedependencies');
if (firstTimeDidChangeDependencies) {
bBloc.isConnected$.listen((data) => _disableSuccessAnimation(data));
bBloc.vibrationButton$
.listen((data) => _vibrateDependingOnButtonEvent(data));
firstTimeDidChangeDependencies = false;
}
super.didChangeDependencies();
}
}
class DrawSlider extends StatefulWidget {
final BuildContext context;
final double _heightSensor, _widthSensor;
DrawSlider(this.context, this._heightSensor, this._widthSensor);
@override
_DrawSliderState createState() => _DrawSliderState();
}
class _DrawSliderState extends State<DrawSlider>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
static const Duration sliderAnimationMoveDuration = Duration(milliseconds: 1);
static int lastPercent = 101;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
);
_animation = Tween(
begin: 1.0,
end: 0.0,
).animate(_controller);
_controller.reverse();
}
@override
void dispose() {
_controller.dispose();
_controller = null;
super.dispose();
}
_sliderFadeAwayAnimation() {
if (_controller != null) {
_controller.reset();
}
Future.delayed(Duration(seconds: 1), () {
if (_controller != null) {
_controller.forward();
}
});
}
_drawSlider(double percent) {
var _sliderPosY = 0.12 * widget._heightSensor +
percent * widget._heightSensor * 0.01 * 0.78;
var _sliderPosX = widget._widthSensor * 0.83;
return Positioned(
left: _sliderPosX,
bottom: _sliderPosY,
child: Image(
color: Theme.of(context).primaryColor,
image: AssetImage('assets/slider_4.png'),
width: widget._widthSensor * 0.15,
),
);
}
_drawSliderText(int percent) {
var _sliderTextPosX = widget._widthSensor * 0.73;
var _sliderTextPosY = 0.13 * widget._heightSensor +
percent * widget._heightSensor * 0.01 * 0.78;
// return AnimatedPositioned(
return Positioned(
// duration: sliderAnimationMoveDuration,
left: _sliderTextPosX,
bottom: _sliderTextPosY,
child: Container(
width: 25.0,
child: Text(
'$percent',
textAlign: TextAlign.end,
style: TextStyle(color: Colors.white),
),
),
);
}
@override
Widget build(BuildContext context) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
// print('slider build');
return StreamBuilder<int>(
stream: bBloc.slider$,
builder: (BuildContext context, AsyncSnapshot<int> sliderSnapshot) {
if (sliderSnapshot.hasData) {
if (sliderSnapshot.data <= 100 && sliderSnapshot.data >= 0) {
if (lastPercent != sliderSnapshot.data)
_sliderFadeAwayAnimation();
lastPercent = sliderSnapshot.data;
print('Slider: ${sliderSnapshot.data}');
print('');
return FadeTransition(
opacity: _animation,
child: Stack(
children: <Widget>[
_drawSlider(sliderSnapshot.data.toDouble()),
_drawSliderText(lastPercent),
],
),
);
} else
return Container();
} else
return Container();
});
}
}
class MiddleOfSensor extends StatefulWidget {
final List<TouchData> touchCollection;
final BuildContext context;
final double _heightSensor;
final double _widthSensor;
MiddleOfSensor(this.touchCollection, this.context, this._heightSensor,
this._widthSensor);
@override
_MiddleOfSensorState createState() => _MiddleOfSensorState();
}
class _MiddleOfSensorState extends State<MiddleOfSensor> {
Duration durationTouchPointChange = Duration(microseconds: 200);
static const leftLine = 100.0;
static const rightLine = 900.0;
static const maxSize = rightLine - leftLine;
static const yMaxSize = 1023;
static const fingerSize = 25.0;
static const List<Color> _fingerColors = [
Colors.lightBlue,
Colors.green,
Colors.deepPurple,
Colors.deepOrangeAccent,
Colors.pink,
];
Widget _buildMiddleOfSensor(List<TouchData> _lastFingerTouchDataPoints) {
Widget _touchPoint(TouchData item, BoxConstraints constraints) {
var _height = constraints.constrainHeight();
var _width = constraints.constrainWidth();
var _xCircle = _width * (item.x.toDouble() - leftLine) / maxSize;
var _yCircle = 0.04 * _height + 0.84 * _height * item.y.toDouble() / yMaxSize;
Text _buildTouchPointText = Text(
'${item.fingerNumber.toString()}',
style: TextStyle(fontSize: 20, color: Colors.white),
);
return AnimatedPositioned(
// return Positioned(
duration: durationTouchPointChange,
left: _xCircle - fingerSize / 2,
top: _yCircle - fingerSize / 2,
child: Container(
width: fingerSize,
height: fingerSize,
decoration: BoxDecoration(
color: _fingerColors[item.fingerNumber],
shape: BoxShape.circle,
),
child: Center(
child: _buildTouchPointText))
);
}
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Stack(
children: _lastFingerTouchDataPoints
.where((TouchData item) => item.event == 4 || item.event == 1)
.where((TouchData item) => item.x < 1200 && item.y < 1200)
.where((TouchData item) => item.fingerNumber < 5)
.map((TouchData item) => _touchPoint(item, constraints))
.toList());
});
}
@override
Widget build(BuildContext context) {
return _buildMiddleOfSensor(widget.touchCollection);
}
}
/*class TouchPainter extends CustomPainter {
/// Draws Slider or touches in the middle of the touch pad.
List<TouchData> touchCollection;
BuildContext context;
double width;
TouchPainter(this.context, this.touchCollection);
static List<Color> _colorOfTouches = [
Colors.lightBlue,
Colors.green,
Colors.deepPurple,
Colors.deepOrangeAccent,
Colors.pink,
];
@override
void paint(Canvas canvas, Size size) {
double leftLine = 80.0;
double rightLine = 900.0;
double maxSize = rightLine - leftLine;
double xCircle, xText;
double yCircle, yText;
Offset offsetCircle, offsetText;
void _drawCircle(int fingerNumber, Offset offset, Color v) {
// double xCircle;
// double yCircle;
// xCircle = x.toDouble() / maxSize * size.width;
// yCircle = y.toDouble() / 1100 * size.height;
Paint line = Paint()
..color = v
..strokeCap = StrokeCap.round
..style = PaintingStyle.fill
..strokeWidth = 15.0;
// print("C: $xCircle / ${size.width}");
canvas.drawCircle(offset, 16.0, line);
}
void _drawPercent(int fingerNumber, Offset offset){
TextSpan span = TextSpan(style: TextStyle(color: Colors.white, fontSize: 20), text: '$fingerNumber');
TextPainter tp = TextPainter(text: span, textAlign: TextAlign.right, textDirection: TextDirection.ltr);
tp.layout();
tp.paint(canvas, offset);
}
touchCollection.forEach((k) {
// print("${k.f} ${k.e} ${k.x} ${k.y}");
if (k.x >= leftLine && k.x <= rightLine && k.fingerNumber <=4) {
switch (k.event) {
case 5:
break;
case 4:
continue move;
move:
case 1:
{
xCircle = (k.x.toDouble() - leftLine) / maxSize * size.width;
yCircle = k.y.toDouble() / 1100 * size.height;
print("Incoming (${k.x.round().toString()}|${k.y.round().toString()})");
xText = (k.x.toDouble() - leftLine - 27.0) / maxSize * size.width;
yText = (k.y.toDouble() - 44.0) / 1100 * size.height;
offsetCircle = Offset(xCircle, yCircle );
offsetText = Offset(xText, yText);
_drawCircle(k.fingerNumber, offsetCircle, _colorOfTouches[k.fingerNumber.toInt()]);
_drawPercent(k.fingerNumber, offsetText);
break;
}
default:
{
print("Data is Corrupt");
break;
}
}
}
});
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}*/
+54
View File
@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:touch_demonstrator/pages/SideBarContent.dart';
class LeftDrawer extends StatelessWidget {
static const htmlPrivacyNotes = 'assets/html/privacy_notice.html';
static const htmlImprint = 'assets/html/imprint.html';
static const htmlTermsOfUse = 'assets/html/terms_of_use.html';
static const htmlDisclaimer = 'assets/html/disclaimer.html';
static const headerText = Text(
'Touch Demonstrator App',
textScaleFactor: 1.5,
style: TextStyle(color: Colors.white),
);
DrawerHeader _buildHeader(BuildContext context) {
return DrawerHeader(
key: Key('DrawerHeader'),
child: headerText,
decoration: BoxDecoration(color: Theme.of(context).primaryColor),
);
}
ListTile _buildTile(BuildContext context, String title, String html) {
return ListTile(
key: Key(title),
title: Text(title),
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) =>
SideBarContent(title: title, html: html)));
},
);
}
@override
Widget build(BuildContext context) {
return Drawer(
key: Key('Drawer'),
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
_buildHeader(context),
_buildTile(context, 'Privacy Notes', htmlPrivacyNotes),
_buildTile(context, 'Imprint', htmlImprint),
_buildTile(context, 'Terms of Use', htmlTermsOfUse),
_buildTile(context, 'Disclaimer', htmlDisclaimer),
],
),
);
}
}
+101
View File
@@ -0,0 +1,101 @@
import 'dart:ui';
import 'package:flutter/material.dart';
//import 'package:flutter_svg/flutter_svg.dart';
import 'package:touch_demonstrator/pages/PageTouchPoints.dart';
import 'package:touch_demonstrator/src/blocs/BlocProvider.dart'; //bloc:
import 'package:flutter/animation.dart';
class Button extends StatelessWidget {
/// Button that navigates to the page 'PageTouchPoints'
@override
Widget build(BuildContext context) {
return Hero(
tag: 'SearchHero',
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
color: Theme.of(context).accentColor,
shape: StadiumBorder(),
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => PageTouchPoints()));
},
child: Text(
'Search and Connect Touchpad',
style: TextStyle(color: Colors.white),
),
),
),
);
}
}
class TouchPad extends StatelessWidget {
/// Image of the touch pad
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset('assets/trackpad.png'),
);
}
}
class ReusableWidgets {
getAppBar(String title) {
return AppBar(
title: Text(title),
);
}
getAppBar2(BuildContext context) {
final bBloc = BlocProvider.of(context).bluetoothBlocGetter;
String appBarText;
return AppBar(
title: StreamBuilder<bool>(
stream: bBloc.isConnected$,
initialData: false,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
if (snapshot.data) {
appBarText = 'Connected';
} else {
// _showSnackbar('Disconnected');
// return Text('Disconnected');
appBarText = 'Disconnected';
}
return AnimatedSwitcher(
duration: const Duration(milliseconds: 350),
transitionBuilder: (Widget child, Animation<double> opacity) {
return FadeTransition(child: child, opacity: opacity);
},
child: new Text(
'$appBarText',
// Must have this key to build a unique widget when _count changes.
key: new ValueKey<String>(appBarText),
textScaleFactor: 1.0,
textDirection: TextDirection.ltr,
),
);
} else
return Text('Disconnected');
}),
// actions: <Widget>[
// enableDebug ?IconButton(
// icon: Icon(Icons.build),
// onPressed: () {
// print('pressed');
// Navigator.push(
// context, MaterialPageRoute(builder: (context) => DebugView()));
// },
// ):Container(),
// ],
);
}
}
+259
View File
@@ -0,0 +1,259 @@
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
/// scan Results
class ScanResultTile extends StatelessWidget {
final ScanResult result;
final VoidCallback onTap;
final String onlyDeviceToShow;
const ScanResultTile(
{Key key, this.result, this.onlyDeviceToShow, this.onTap})
: super(key: key);
Widget _buildTitle(BuildContext context) {
if (result.device.name.length > 0) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(result.device.name),
Text(
result.device.id.toString(),
style: Theme.of(context).textTheme.caption,
)
],
);
} else {
return Text(result.device.id.toString());
}
}
String getNiceHexArray(List<int> bytes) {
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
.toUpperCase();
}
String getNiceManufacturerData(Map<int, List<int>> data) {
if (data.isEmpty) {
return null;
}
List<String> res = [];
data.forEach((id, bytes) {
res.add(
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
String getNiceServiceData(Map<String, List<int>> data) {
if (data.isEmpty) {
return null;
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
@override
Widget build(BuildContext context) {
// Test if device = TouchDemonstrator
if (result.advertisementData.localName == onlyDeviceToShow) {
return Container(
// decoration: FlutterLogoDecoration(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_buildTitle(context), // Name of devices
RaisedButton(
child: Text('CONNECT'),
color: Colors.black,
textColor: Colors.white,
onPressed:
(result.advertisementData.connectable) ? onTap : null,
),
],
),
),
);
} else {
return Container();
}
}
}
/// Service tiles
class ServiceTile extends StatelessWidget {
final BluetoothService service;
final List<CharacteristicTile> characteristicTiles;
final String onlyServiceToShow;
const ServiceTile(
{Key key, this.service, this.characteristicTiles, this.onlyServiceToShow})
: super(key: key);
@override
Widget build(BuildContext context) {
if (characteristicTiles.length > 0) {
if (service.uuid.toString().toUpperCase().substring(4, 8) ==
onlyServiceToShow) {
return new ExpansionTile(
initiallyExpanded: true,
title: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Service (BLEUart)'),
new Text(
'UUID: 0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.body1.copyWith(
color: Theme.of(context).textTheme.caption.color))
],
),
/// characteristic tiles
children: characteristicTiles,
);
} else {
return Container(); //Text("Service not BLEUart");
}
} else {
return new ListTile(
title: const Text('Service'),
subtitle: new Text(
'0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
);
}
}
}
/// characteristic tile
class CharacteristicTile extends StatelessWidget {
final BluetoothCharacteristic characteristic;
final List<DescriptorTile> descriptorTiles;
//final VoidCallback onReadPressed;
// final VoidCallback onWritePressed;
final VoidCallback onNotificationPressed;
final String onlyCharacteristicToListen;
const CharacteristicTile(
{Key key,
this.characteristic,
this.descriptorTiles,
//this.onReadPressed,
// this.onWritePressed,
this.onNotificationPressed,
@required this.onlyCharacteristicToListen})
: super(key: key);
@override
Widget build(BuildContext context) {
if(onlyCharacteristicToListen == characteristic.uuid.toString().toUpperCase().substring(4,8)){
print("found ${characteristic.uuid.toString().toUpperCase().substring(4,8)} /// $onlyCharacteristicToListen");
}
/// title
var title = new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('CharacteristicTile title'),
new Text(
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.body1
.copyWith(color: Theme.of(context).textTheme.caption.color))
],
);
/// subscribe
var actions = new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new IconButton(
icon: new Icon(
characteristic.isNotifying ? Icons.sync_disabled : Icons.sync,
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
onPressed: onNotificationPressed,
)
],
);
return new ListTile(
title: title,
subtitle: new Text(characteristic.value.toString()),
trailing: actions,
);
//onNotificationPressed();
/*if (descriptorTiles.length > 0) {
return new ExpansionTile(
title: new ListTile(
title: title,
subtitle: new Text(characteristic.value.toString()),
contentPadding: EdgeInsets.all(0.0),
),
trailing: actions,
children: descriptorTiles,
);
} else {*/
}
}
class DescriptorTile extends StatelessWidget {
final BluetoothDescriptor descriptor;
/* final VoidCallback onReadPressed;
final VoidCallback onWritePressed;*/
const DescriptorTile(
{Key key, this.descriptor}) //, this.onReadPressed, this.onWritePressed})
: super(key: key);
@override
Widget build(BuildContext context) {
var title = new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text('Descriptor'),
new Text(
'0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.body1
.copyWith(color: Theme.of(context).textTheme.caption.color))
],
);
return new ListTile(
title: title,
subtitle: new Text(descriptor.value.toString()),
/* trailing: new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new IconButton(
icon: new Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
),
onPressed: null,//onReadPressed,
),
new IconButton(
icon: new Icon(
Icons.file_upload,
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
),
onPressed: null,//onWritePressed,
)
],
),*/
);
}
}