save sensor data as json file

This commit is contained in:
2024-12-04 14:57:43 +08:00
parent 20711ea05c
commit 5d1099086b
9 changed files with 144 additions and 31 deletions

View File

@@ -8,15 +8,14 @@ plugins {
android { android {
namespace = "tw.moonjuice.light_sesnor_capture" namespace = "tw.moonjuice.light_sesnor_capture"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion "25.1.8937393"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8 jvmTarget = 17
} }
defaultConfig { defaultConfig {

View File

@@ -1,4 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:label="light_sesnor_capture" android:label="light_sesnor_capture"
android:name="${applicationName}" android:name="${applicationName}"

View File

@@ -1,20 +1,29 @@
package tw.moonjuice.light_sesnor_capture; package tw.moonjuice.light_sesnor_capture;
import android.Manifest;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor; import android.hardware.Sensor;
import android.hardware.SensorEvent; import android.hardware.SensorEvent;
import android.hardware.SensorEventListener; import android.hardware.SensorEventListener;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.EventChannel; import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity implements EventChannel.StreamHandler, SensorEventListener { public class MainActivity extends FlutterActivity implements EventChannel.StreamHandler, SensorEventListener, MethodChannel.MethodCallHandler {
private Sensor mLightSensor; private Sensor mLightSensor;
private SensorManager mSensorManager; private SensorManager mSensorManager;
private EventChannel.EventSink mEventSink; private EventChannel.EventSink mEventSink;
@@ -30,24 +39,39 @@ public class MainActivity extends FlutterActivity implements EventChannel.Stream
super.configureFlutterEngine(flutterEngine); super.configureFlutterEngine(flutterEngine);
EventChannel eventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "tw.moonjuice.light_sensor.stream"); EventChannel eventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "tw.moonjuice.light_sensor.stream");
eventChannel.setStreamHandler(this); eventChannel.setStreamHandler(this);
MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor(), "tw.moonjuice.light_sensor.method");
methodChannel.setMethodCallHandler(this);
}
@Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_FASTEST);
}
@Override
protected void onStop() {
mSensorManager.unregisterListener(this);
super.onStop();
} }
@Override @Override
public void onListen(Object arguments, EventChannel.EventSink events) { public void onListen(Object arguments, EventChannel.EventSink events) {
mEventSink = events; mEventSink = events;
mSensorManager.registerListener(this, mLightSensor, SensorManager.SENSOR_DELAY_FASTEST);
} }
@Override @Override
public void onCancel(Object arguments) { public void onCancel(Object arguments) {
mEventSink = null; mEventSink = null;
mSensorManager.unregisterListener(this);
} }
@Override @Override
public void onSensorChanged(SensorEvent sensorEvent) { public void onSensorChanged(SensorEvent sensorEvent) {
if (mEventSink != null) { if (mEventSink != null) {
mEventSink.success(sensorEvent.values[0]); final Map data = new HashMap();
data.put("lux", sensorEvent.values[0]);
data.put("timestamp", sensorEvent.timestamp);
mEventSink.success(data);
} }
} }
@@ -55,4 +79,22 @@ public class MainActivity extends FlutterActivity implements EventChannel.Stream
public void onAccuracyChanged(Sensor sensor, int i) { public void onAccuracyChanged(Sensor sensor, int i) {
} }
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
case "getExternalDownloadsPath":
result.success(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
break;
case "start":
Toast.makeText(getContext(), "start", Toast.LENGTH_SHORT).show();
result.success("success");
break;
default:
Toast.makeText(getContext(), "stop", Toast.LENGTH_SHORT).show();
result.success("success");
break;
}
}
} }

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip

View File

@@ -18,8 +18,8 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.1.0" apply false id "com.android.application" version "8.3.2" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false id "org.jetbrains.kotlin.android" version "2.0.20" apply false
} }
include ":app" include ":app"

View File

@@ -0,0 +1,11 @@
import 'dart:ffi';
class LightSensorEvent {
final double lux;
final int timestamp;
LightSensorEvent(this.lux, this.timestamp);
Map<String, dynamic> toJson() => {
'lux': lux,
'timestamp': timestamp,
};
}

View File

@@ -1,5 +1,10 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:light_sesnor_capture/light_sensor_event.dart';
import 'package:intl/intl.dart';
void main() { void main() {
runApp(const MyApp()); runApp(const MyApp());
@@ -56,18 +61,10 @@ class MyHomePage extends StatefulWidget {
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
int _counter = 0; int mState = 0;
static const MethodChannel methodChannel =
void _incrementCounter() { MethodChannel('tw.moonjuice.light_sensor.method');
setState(() { List mLightSensorEvents = [];
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -107,12 +104,15 @@ class _MyHomePageState extends State<MyHomePage> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Spacer(), Spacer(),
StreamBuilder<String>( StreamBuilder<LightSensorEvent>(
stream: streamSensorEventFromNative(), stream: streamSensorEventFromNative(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (snapshot.hasData) {
if (mState == 1) {
mLightSensorEvents.add(snapshot.data);
}
return Text( return Text(
'Sensor Data: ${snapshot.data}', 'Sensor Data: ${jsonEncode(snapshot.data)}',
style: Theme.of(context).textTheme.headlineLarge, style: Theme.of(context).textTheme.headlineLarge,
); );
} else { } else {
@@ -125,10 +125,13 @@ class _MyHomePageState extends State<MyHomePage> {
children: [ children: [
Spacer(), Spacer(),
ElevatedButton( ElevatedButton(
onPressed: onPressed, child: Text('Start Record')), onPressed: (mState == 0) ? onPressed : null,
child: Text('Start Record'),
),
Spacer(), Spacer(),
ElevatedButton( ElevatedButton(
onPressed: onPressed, child: Text('Stop Record')), onPressed: (mState == 1) ? onPressed : null,
child: Text('Stop Record')),
Spacer(), Spacer(),
], ],
), ),
@@ -139,12 +142,28 @@ class _MyHomePageState extends State<MyHomePage> {
); );
} }
void onPressed() {} Future<void> onPressed() async {
setState(() {
mState = ((mState == 0) ? 1 : 0);
});
if (mState == 0) {
final String downloadsPath =
await methodChannel.invokeMethod('getExternalDownloadsPath');
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd_HH-mm-ss').format(now);
File file = File('$downloadsPath/$formattedDate.txt');
var sink = file.openWrite();
sink.write(jsonEncode(mLightSensorEvents));
await sink.flush();
await sink.close();
mLightSensorEvents.clear();
}
}
Stream<String> streamSensorEventFromNative() { Stream<LightSensorEvent> streamSensorEventFromNative() {
const eventChannel = EventChannel('tw.moonjuice.light_sensor.stream'); const eventChannel = EventChannel('tw.moonjuice.light_sensor.stream');
return eventChannel return eventChannel
.receiveBroadcastStream() .receiveBroadcastStream()
.map((event) => event.toString()); .map((event) => LightSensorEvent(event['lux'], event['timestamp']));
} }
} }

View File

@@ -75,6 +75,30 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
intl:
dependency: "direct main"
description:
name: intl
sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50"
url: "https://pub.dev"
source: hosted
version: "0.20.1"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@@ -192,6 +216,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.2" version: "0.7.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -208,6 +240,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.2.5" version: "14.2.5"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
sdks: sdks:
dart: ">=3.5.4 <4.0.0" dart: ">=3.5.4 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54" flutter: ">=3.18.0-18.0.pre.54"

View File

@@ -35,6 +35,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
intl: ^0.20.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: