diff --git a/sensors_dashboard/.gitignore b/sensors_dashboard/.gitignore new file mode 100644 index 0000000..29a3a50 --- /dev/null +++ b/sensors_dashboard/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/sensors_dashboard/.metadata b/sensors_dashboard/.metadata new file mode 100644 index 0000000..6eb54a1 --- /dev/null +++ b/sensors_dashboard/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "761747bfc538b5af34aa0d3fac380f1bc331ec49" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: android + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: ios + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: linux + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: macos + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: web + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + - platform: windows + create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/sensors_dashboard/README.md b/sensors_dashboard/README.md new file mode 100644 index 0000000..ab77459 --- /dev/null +++ b/sensors_dashboard/README.md @@ -0,0 +1,16 @@ +# sensors_test + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/sensors_dashboard/analysis_options.yaml b/sensors_dashboard/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/sensors_dashboard/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/sensors_dashboard/devtools_options.yaml b/sensors_dashboard/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/sensors_dashboard/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/sensors_dashboard/http-server/server.dart b/sensors_dashboard/http-server/server.dart new file mode 100644 index 0000000..5d6298d --- /dev/null +++ b/sensors_dashboard/http-server/server.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; +import 'dart:io'; +import '../lib/model/sensor.dart'; + +const int port = 8080; +const sensorsList = [ + Sensor(name: "Accelerometer", type: "android.sensor.accelerometer"), + Sensor(name: "Gyroscope", type: "android.sensor.gyroscope"), + Sensor(name: "Light", type: "android.sensor.light"), + Sensor(name: "Gravity", type: "android.sensor.gravity"), + Sensor(name: "Linear Acceleration", type: "android.sensor.linear_acceleration"), + Sensor(name: "Rotation Vector", type: "android.sensor.rotation_vector"), + Sensor(name: "Orientation", type: "android.sensor.orientation"), +]; + +void main(List args) async { + + // Bind the server to the port + HttpServer server = await HttpServer.bind(InternetAddress.loopbackIPv4,port); + + print('Server started : ${server.address.address}:${server.port}'); + + // Listen for incoming requests + server.listen((HttpRequest request) async { + request.response.headers.set("Access-Control-Allow-Origin", "*"); + request.response.headers.set("Access-Control-Allow-Methods", "GET,PUT,PATCH,POST,DELETE"); + + print("request : ${request.requestedUri.path}"); + // Check if it's a GET request + + if (request.method == 'GET') { + // Get the requested path + final String path = request.uri.path; + switch(path){ + case "/sensors": + handleSensorsRequest(request); + break; + case "/wsport": + handlePortRequest(request); + break; + } + + } else { + // Handle other methods (optional) + request.response.statusCode = HttpStatus.methodNotAllowed; + await request.response.close(); + } + }); + + + +} + + void handleSensorsRequest(HttpRequest request) async { + + final List> jsonList = []; + for (var sensor in sensorsList) { + jsonList.add(sensor.toJson()); + } + + request.response + ..statusCode = HttpStatus.ok + ..write(jsonEncode(jsonList)); + + await request.response.close(); + + } + + void handlePortRequest(HttpRequest request) async { + final response = {"portNo": 8081}; + request.response + ..statusCode = HttpStatus.ok + ..write(jsonEncode(response)); + + await request.response.close(); + } diff --git a/sensors_dashboard/lib/main.dart b/sensors_dashboard/lib/main.dart new file mode 100644 index 0000000..6c74338 --- /dev/null +++ b/sensors_dashboard/lib/main.dart @@ -0,0 +1,37 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:sensors_dashboard/model/server_info.dart'; +import 'package:sensors_dashboard/view/screens/sensors_screen.dart'; +import 'package:sensors_dashboard/view_model/sensors_screen_viewmodel.dart'; +import 'package:get_it/get_it.dart'; + +void main() { + if(kDebugMode){ + GetIt.instance.registerSingleton(ServerInfo()); + } + else if(kReleaseMode){ + GetIt.instance.registerSingleton(ServerInfo(deployed: true)); + } + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'Sensors Dashboard', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: Colors.teal), + useMaterial3: true, + ), + home: ChangeNotifierProvider( + create: (context) => SensorsScreenViewmodel(), + child: SensorsScreen())); + } +} diff --git a/sensors_dashboard/lib/model/repository/info_repository.dart b/sensors_dashboard/lib/model/repository/info_repository.dart new file mode 100644 index 0000000..2139019 --- /dev/null +++ b/sensors_dashboard/lib/model/repository/info_repository.dart @@ -0,0 +1,21 @@ +import 'dart:convert'; + +import 'package:get_it/get_it.dart'; +import 'package:sensors_dashboard/model/server_info.dart'; +import "package:http/http.dart" as http; + +class InfoRepository { + Future getWebSocketPortNo() async { + try { + final serverInfo = GetIt.instance.get(); + final url = "http://${serverInfo.address}/wsport"; + + final response = await http.get(Uri.parse(url)); + final json = jsonDecode(response.body); + + return json["portNo"]; + } on Exception catch (_) { + return Future.error(Exception("Failed to get websocket portNo")); + } + } +} diff --git a/sensors_dashboard/lib/model/repository/sensors_repository.dart b/sensors_dashboard/lib/model/repository/sensors_repository.dart new file mode 100644 index 0000000..427395a --- /dev/null +++ b/sensors_dashboard/lib/model/repository/sensors_repository.dart @@ -0,0 +1,38 @@ +import 'dart:convert'; +import 'dart:developer'; + +import 'package:get_it/get_it.dart'; +import 'package:sensors_dashboard/model/sensor.dart'; +import 'package:http/http.dart' as http; +import 'package:sensors_dashboard/model/server_info.dart'; + +class SensorsRepository { + + + Future> getSensors() async { + try { + + final serverInfo = GetIt.instance.get(); + final url = "http://${serverInfo.address}/sensors"; + log("fetching sensors from $url"); + + + final response = await http.get(Uri.parse(url)); + final sensorsJson = jsonDecode(response.body); + + final sensors = []; + + for(dynamic sensor in sensorsJson){ + sensors.add(Sensor.fromJson(sensor)); + } + + return sensors; + + + } on Exception catch (_) { + log("Failed to load sensors"); + return Future.error(Exception("Unable to load sensors")); + + } + } +} diff --git a/sensors_dashboard/lib/model/sensor.dart b/sensors_dashboard/lib/model/sensor.dart new file mode 100644 index 0000000..0e99976 --- /dev/null +++ b/sensors_dashboard/lib/model/sensor.dart @@ -0,0 +1,20 @@ + +class Sensor { + + final String name; + final String type; + + const Sensor({required this.name, required this.type}); + + factory Sensor.fromJson(Map json){ + return Sensor(name: json["name"], type: json["type"]); + } + + Map toJson(){ + return { + "name" : name, + "type" : type + }; + } + +} \ No newline at end of file diff --git a/sensors_dashboard/lib/model/server_info.dart b/sensors_dashboard/lib/model/server_info.dart new file mode 100644 index 0000000..83b4439 --- /dev/null +++ b/sensors_dashboard/lib/model/server_info.dart @@ -0,0 +1,29 @@ +import 'package:web/web.dart' as web; +class ServerInfo { + + bool deployed; + + String testIp; + int testPortNo; + + + String get address{ + final location = web.window.location; + return switch(deployed){ + true => "${location.hostname}:${location.port}", + false => "$testIp:$testPortNo", + }; + } + + String get iP{ + return switch(deployed){ + true => web.window.location.hostname, + false => testIp, + }; + } + + + ServerInfo({this.deployed = false, this.testIp = "127.0.0.1", this.testPortNo = 8080}); + + +} \ No newline at end of file diff --git a/sensors_dashboard/lib/view/components/sensor_widget.dart b/sensors_dashboard/lib/view/components/sensor_widget.dart new file mode 100644 index 0000000..2ed784d --- /dev/null +++ b/sensors_dashboard/lib/view/components/sensor_widget.dart @@ -0,0 +1,127 @@ + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:sensors_dashboard/model/sensor.dart'; +import 'package:sensors_dashboard/view_model/sensor_viewmodel.dart'; + +/// A wiget which represents sensor of Android device +class SensorWidget extends StatelessWidget { + final Sensor sensor; + const SensorWidget(this.sensor,{super.key}); + + + @override + Widget build(BuildContext context) { + + final viewModel = Provider.of(context); + final colorScheme = Theme.of(context).colorScheme; + final textTheme = Theme.of(context).textTheme; + + + return Card( + elevation: 15, + shadowColor: colorScheme.secondary, + child: Container( + decoration: BoxDecoration( + color: colorScheme.secondaryContainer, + borderRadius: const BorderRadius.only(topLeft: Radius.circular(30),topRight: Radius.circular(30) )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 2, + child: Center( + child: Container( + width: double.infinity, + height: double.infinity, + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: colorScheme.inversePrimary), + child: FittedBox( + child: Text( + sensor.name, + style: TextStyle(fontSize: textTheme.headlineSmall?.fontSize), + ), + ), + ), + )), + + Expanded( + flex: 6, + child: Center( + child: switch(viewModel.isConnected){ + true => Text(viewModel.values), + false => viewModel.connecting ? const CircularProgressIndicator() : const Text("No Data") + } + )), + + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.all(5), + child: Row( + children: [ + Expanded( + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: _Button( + "Connect", + disabled: viewModel.isConnected, + onPressed: (){ + viewModel.connect(sensor); + }), + ), + ), + + const SizedBox(width: 10,), + + Expanded( + child: SizedBox( + width: double.infinity, + height: double.infinity, + child: _Button( + "Disconnect", + disabled: !viewModel.isConnected, + onPressed: (){ + viewModel.disconnect(); + }), + ), + ), + ], + ), + ), + ) + ], + ), + ), + ); + } +} + +class _Button extends StatelessWidget { + + final bool disabled; + final String text; + final VoidCallback onPressed; + const _Button(this.text, {super.key, this.disabled = false, required this.onPressed}); + + + @override + Widget build(BuildContext context) { + + final buttonStyle = ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + )); + + return ElevatedButton( + style: buttonStyle, + onPressed: disabled ? null : onPressed, // Button becomes disable with onPressed = null + child: FittedBox(child: Text(text))); + } +} + + diff --git a/sensors_dashboard/lib/view/screens/sensors_screen.dart b/sensors_dashboard/lib/view/screens/sensors_screen.dart new file mode 100644 index 0000000..b6e3799 --- /dev/null +++ b/sensors_dashboard/lib/view/screens/sensors_screen.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:sensors_dashboard/model/sensor.dart'; +import 'package:sensors_dashboard/view/components/sensor_widget.dart'; +import 'package:sensors_dashboard/view_model/sensor_viewmodel.dart'; +import 'package:sensors_dashboard/view_model/sensors_screen_viewmodel.dart'; + +class SensorsScreen extends StatelessWidget { + const SensorsScreen({super.key}); + + @override + Widget build(BuildContext context) { + + final viewModel = Provider.of(context); + + return Scaffold( + appBar: AppBar( + title: const Text("Sensors"), + centerTitle: true, + ), + body: SafeArea( + child: Center( + child: FutureBuilder>( + future: viewModel.getSensors(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const CircularProgressIndicator(); + } + if (snapshot.hasData) { + final sensors = snapshot.data; + + if (sensors != null) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: _SensorGrid(sensors: sensors,) + ); + } + } + + if(snapshot.hasError){ + return const Text("Failed to Load sensors"); + } + + return const Text("No data"); + }, + ), + ), + )); + } +} + +class _SensorGrid extends StatelessWidget { + final List sensors; + const _SensorGrid({super.key, required this.sensors}); + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + return GridView.builder( + // Normally, Gridview/Listview remove items that are no longer visible to save memory. + // We need to change this behavior because each SensorWidget have connection to a server. + // If SensorWiget gets removed while scrolling, it will be dispose and the connection is lost. + // Setting cacheExtent to a high value tells the view to keep more items in memory even if they're not visible. + // the widget will be dispose and connection will be lost + cacheExtent: double.maxFinite, + itemCount: sensors.length, + itemBuilder: (context, index) { + return ChangeNotifierProvider( + // Handle each SensorWidget's state in SensorViewModel + // Each SensorWidget should have separate websocket connection + create: (context) => SensorViewModel(), + child: SensorWidget(sensors[index])); + }, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: screenWidth <= 430 ? 1 : 3 , + crossAxisSpacing: 15, + mainAxisSpacing: 15), + ); + + } +} diff --git a/sensors_dashboard/lib/view_model/sensor_viewmodel.dart b/sensors_dashboard/lib/view_model/sensor_viewmodel.dart new file mode 100644 index 0000000..fb94a75 --- /dev/null +++ b/sensors_dashboard/lib/view_model/sensor_viewmodel.dart @@ -0,0 +1,153 @@ + +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; + +import 'package:flutter/foundation.dart'; +import 'package:get_it/get_it.dart'; + +import 'package:sensors_dashboard/model/repository/info_repository.dart'; +import 'package:sensors_dashboard/model/sensor.dart'; +import 'package:sensors_dashboard/model/server_info.dart'; +import 'package:web/web.dart' as web; + +/// A ViewModel for SensorWiget +class SensorViewModel with ChangeNotifier{ + + bool _connected = false; + bool get isConnected => _connected; + + bool _connecting = false; + bool get connecting => _connecting; + + String _values = ""; + String get values => _values; + + bool _connectionFailed = false; + bool get connectionFailed => _connectionFailed; + + String _closeMessage = ""; + String get closeMessage => _closeMessage; + + String _errorMessage = ""; + String get errorMessage => _errorMessage; + + bool _hasError = false; + bool get hasError => _hasError; + + static const connectionTimeOutSecs = 2; + + // A url for testing connections without repolying app to Android + final testUrl = "ws://192.168.0.103:8080/sensor/connect"; + + web.WebSocket? _websocket; + + void connect(Sensor sensor) async { + + _setConnecting(true); + + + Timer(const Duration(seconds: connectionTimeOutSecs), () { + if (!isConnected) { + log("connection timed out after $connectionTimeOutSecs"); + _websocket?.close(); + _setConnected(false); + _setErrorMessage("Connection Timmed Out"); + } + }); + + // Use hardcode or testUrl in debug mode. + if (kDebugMode) { + _websocket = web.WebSocket("$testUrl?type=${sensor.type}"); + } + // Get actual address when deployed to Android + if (kReleaseMode) { + final serverInfo = GetIt.instance.get(); + + log("getting websocket port from http://${serverInfo.address}/wsport"); + final webSocketPort = await InfoRepository().getWebSocketPortNo(); + final webSocketServerAddress = "ws://${serverInfo.iP}:$webSocketPort"; + + _websocket = web.WebSocket("$webSocketServerAddress/sensor/connect?type=${sensor.type}"); + } + + _websocket?.onOpen.listen((event){ + _setConnected(true); + }); + + _websocket?.onMessage.listen((messageEvent){ + + final data = messageEvent.data; + if (data != null) { + final json = jsonDecode(data.toString()); + _values = json["values"].toString(); + notifyListeners(); + } + + }); + + _websocket?.onClose.listen((closeEvent){ + _setConnected(false); + }); + + _websocket?.onError.listen((event){ + _setConnected(false); + _setConnectionFailed(true); + _setErrorMessage("Error Occurred on Connection"); + + }); + + + } + + void disconnect(){ + _websocket?.close(); + } + + + + void _setConnected(bool connected){ + + _setConnecting(false); + + if(_connected == connected){ + return; + } + + _connected = connected; + notifyListeners(); + + } + + void _setConnecting(bool connecting){ + + if(_connecting == connecting){ + return; + } + + _connecting = connecting; + notifyListeners(); + } + + void _setConnectionFailed(bool connectionFailed){ + if(_connectionFailed == connectionFailed){ + return; + } + + _connectionFailed = connectionFailed; + notifyListeners(); + } + +void _setErrorMessage(String message){ + _errorMessage = message; + _hasError = true; + notifyListeners(); +} + +@override + void dispose() { + super.dispose(); + _websocket?.close(); + } + +} \ No newline at end of file diff --git a/sensors_dashboard/lib/view_model/sensors_screen_viewmodel.dart b/sensors_dashboard/lib/view_model/sensors_screen_viewmodel.dart new file mode 100644 index 0000000..af1e0cf --- /dev/null +++ b/sensors_dashboard/lib/view_model/sensors_screen_viewmodel.dart @@ -0,0 +1,22 @@ + +import 'package:flutter/material.dart'; +import 'package:sensors_dashboard/model/repository/sensors_repository.dart'; +import 'package:sensors_dashboard/model/sensor.dart'; + +class SensorsScreenViewmodel with ChangeNotifier{ + + List _sensors = []; + List get sensors => _sensors; + + final repo = SensorsRepository(); + + void fetechSensors(){ + repo.getSensors().then((sensors){ + _sensors = sensors; + }); + } + + Future> getSensors() => repo.getSensors(); + + +} \ No newline at end of file diff --git a/sensors_dashboard/pubspec.lock b/sensors_dashboard/pubspec.lock new file mode 100644 index 0000000..6621bc5 --- /dev/null +++ b/sensors_dashboard/pubspec.lock @@ -0,0 +1,269 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: d85128a5dae4ea777324730dc65edd9c9f43155c109d5cc0a69cab74139fbac1 + url: "https://pub.dev" + source: hosted + version: "7.7.0" + http: + dependency: "direct main" + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + url: "https://pub.dev" + source: hosted + version: "1.12.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + url: "https://pub.dev" + source: hosted + version: "0.7.0" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.dev" + source: hosted + version: "14.2.1" + web: + dependency: "direct main" + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" +sdks: + dart: ">=3.4.3 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/sensors_dashboard/pubspec.yaml b/sensors_dashboard/pubspec.yaml new file mode 100644 index 0000000..b1bed20 --- /dev/null +++ b/sensors_dashboard/pubspec.yaml @@ -0,0 +1,89 @@ +name: sensors_dashboard +description: "A webapp to display Android's sensor data in web browser" +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=3.4.3 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.6 + provider: ^6.1.2 + web: ^0.5.1 + http: ^1.2.1 + get_it: ^7.7.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^3.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + #fonts: + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/sensors_dashboard/test/widget_test.dart b/sensors_dashboard/test/widget_test.dart new file mode 100644 index 0000000..1a13222 --- /dev/null +++ b/sensors_dashboard/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:sensors_dashboard/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/sensors_dashboard/web/favicon.png b/sensors_dashboard/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/sensors_dashboard/web/favicon.png differ diff --git a/sensors_dashboard/web/icons/Icon-192.png b/sensors_dashboard/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/sensors_dashboard/web/icons/Icon-192.png differ diff --git a/sensors_dashboard/web/icons/Icon-512.png b/sensors_dashboard/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/sensors_dashboard/web/icons/Icon-512.png differ diff --git a/sensors_dashboard/web/icons/Icon-maskable-192.png b/sensors_dashboard/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/sensors_dashboard/web/icons/Icon-maskable-192.png differ diff --git a/sensors_dashboard/web/icons/Icon-maskable-512.png b/sensors_dashboard/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/sensors_dashboard/web/icons/Icon-maskable-512.png differ diff --git a/sensors_dashboard/web/index.html b/sensors_dashboard/web/index.html new file mode 100644 index 0000000..566ff07 --- /dev/null +++ b/sensors_dashboard/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + sensors_test + + + + + + diff --git a/sensors_dashboard/web/manifest.json b/sensors_dashboard/web/manifest.json new file mode 100644 index 0000000..af774d6 --- /dev/null +++ b/sensors_dashboard/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "sensors_test", + "short_name": "sensors_test", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +}