Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-feature android:name="android.hardware.usb.host" />
<uses-feature
android:name="android.hardware.sensor.ambient_temperature"
android:required="false" />
<application
android:label="PSLab"
android:name="${applicationName}"
Expand Down
170 changes: 168 additions & 2 deletions android/app/src/main/java/io/pslab/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,172 @@
package io.pslab;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity implements SensorEventListener {
private static final String TEMPERATURE_CHANNEL = "io.pslab/temperature";
private static final String TEMPERATURE_STREAM = "io.pslab/temperature_stream";
private static final String TAG = "MainActivity";

private SensorManager sensorManager;
private Sensor temperatureSensor;
private MethodChannel temperatureChannel;
private EventChannel temperatureEventChannel;
private EventChannel.EventSink temperatureEventSink;
private boolean isListening = false;
private float currentTemperature = 0.0f;

@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager != null) {
temperatureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
}

temperatureChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), TEMPERATURE_CHANNEL);
temperatureChannel.setMethodCallHandler(this::handleMethodCall);

temperatureEventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), TEMPERATURE_STREAM);
temperatureEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
temperatureEventSink = events;
startTemperatureUpdates();
}

@Override
public void onCancel(Object arguments) {
temperatureEventSink = null;
stopTemperatureUpdates();
}
});
}

private void handleMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "isTemperatureSensorAvailable":
result.success(temperatureSensor != null);
break;
case "getCurrentTemperature":
result.success((double) currentTemperature);
break;
case "startTemperatureUpdates":
if (startTemperatureUpdates()) {
result.success(true);
} else {
result.error("SENSOR_ERROR", "Failed to start temperature updates", null);
}
break;
case "stopTemperatureUpdates":
stopTemperatureUpdates();
result.success(true);
break;
default:
result.notImplemented();
break;
}
}

private boolean startTemperatureUpdates() {
if (temperatureSensor == null || sensorManager == null) {
Log.e(TAG, "Temperature sensor not available");
return false;
}

if (!isListening) {
boolean registered = sensorManager.registerListener(this, temperatureSensor, SensorManager.SENSOR_DELAY_NORMAL);
if (registered) {
isListening = true;
Log.d(TAG, "Temperature sensor listener registered");

if (currentTemperature != 0.0f && temperatureEventSink != null) {
Log.d(TAG, "Sending initial temperature to Flutter: " + currentTemperature);
temperatureEventSink.success((double) currentTemperature);
}

return true;
} else {
Log.e(TAG, "Failed to register temperature sensor listener");
return false;
}
}
return true;
}

private void stopTemperatureUpdates() {
if (isListening && sensorManager != null) {
sensorManager.unregisterListener(this, temperatureSensor);
isListening = false;
Log.d(TAG, "Temperature sensor listener unregistered");
}
}

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE) {
float temperature = event.values[0];

if (isValidTemperature(temperature)) {
currentTemperature = temperature;
Log.d(TAG, "Temperature updated: " + currentTemperature + "°C");

if (temperatureEventSink != null) {
Log.d(TAG, "Sending temperature to Flutter: " + currentTemperature);
temperatureEventSink.success((double) currentTemperature);
}
} else {
Log.w(TAG, "Invalid temperature reading: " + temperature + " - ignoring");
}
}
}

private boolean isValidTemperature(float temperature) {
if (Float.isNaN(temperature) || Float.isInfinite(temperature)) return false;
if (temperature < -273.15f) return false;
if (temperature > 200f) return false;
if (Math.abs(temperature) > 1e10f) return false;
return true;
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.d(TAG, "Sensor accuracy changed: " + accuracy);
}

@Override
protected void onDestroy() {
super.onDestroy();
stopTemperatureUpdates();
}

@Override
protected void onPause() {
super.onPause();
if (isListening && sensorManager != null) {
sensorManager.unregisterListener(this);
}
}

public class MainActivity extends FlutterActivity {
}
@Override
protected void onResume() {
super.onResume();
if (isListening && temperatureSensor != null && sensorManager != null) {
sensorManager.registerListener(this, temperatureSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
}
9 changes: 9 additions & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,15 @@ String buyPsLabMenu = 'Buy PSLab';
String faqMenu = 'FAQ';
String shareAppMenu = 'Share App';
String privacyPolicyMenu = 'Privacy Policy';
String thermometerTitle = 'Thermometer';
String thermometerIntro =
'Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.';
String celsius = '°C';
String temperatureSensorError = 'Temperature sensor error:';
String temperatureSensorInitialError =
'Temperature sensor initialization error:';
String temperatureSensorUnavailableMessage =
'Ambient temperature sensor is not available on this device';
String shopLink = 'https://pslab.io/shop/';
String shopError = 'Could not open the shop link';
String baroMeterBulletPoint1 =
Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'package:pslab/view/about_us_screen.dart';
import 'package:pslab/view/software_licenses_screen.dart';
import 'package:pslab/theme/app_theme.dart';
import 'package:pslab/view/soundmeter_screen.dart';
import 'package:pslab/view/thermometer_screen.dart';
import 'constants.dart';

void main() {
Expand Down Expand Up @@ -62,6 +63,7 @@ class MyApp extends StatelessWidget {
'/luxmeter': (context) => const LuxMeterScreen(),
'/barometer': (context) => const BarometerScreen(),
'/soundmeter': (context) => const SoundMeterScreen(),
'/thermometer': (context) => const ThermometerScreen(),
},
);
}
Expand Down
92 changes: 92 additions & 0 deletions lib/others/temperature_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:pslab/others/logger_service.dart';

class TemperatureService {
static const MethodChannel _methodChannel =
MethodChannel('io.pslab/temperature');
static const EventChannel _eventChannel =
EventChannel('io.pslab/temperature_stream');

static StreamSubscription<dynamic>? _temperatureSubscription;
static final StreamController<double> _temperatureController =
StreamController<double>.broadcast();

static Stream<double> get temperatureStream => _temperatureController.stream;

static Future<bool> isTemperatureSensorAvailable() async {
try {
final bool isAvailable =
await _methodChannel.invokeMethod('isTemperatureSensorAvailable');
logger.d('Temperature sensor available: $isAvailable');
return isAvailable;
} on PlatformException catch (e) {
logger.e('Error checking temperature sensor availability: ${e.message}');
return false;
}
}

static Future<double> getCurrentTemperature() async {
try {
final double temperature =
await _methodChannel.invokeMethod('getCurrentTemperature');
logger.d('Current temperature: $temperature°C');
return temperature;
} on PlatformException catch (e) {
logger.e('Error getting current temperature: ${e.message}');
return 0.0;
}
}

static Future<bool> startTemperatureUpdates() async {
try {
final bool success =
await _methodChannel.invokeMethod('startTemperatureUpdates');
if (success) {
_startListening();
logger.d('Temperature updates started');
}
return success;
} on PlatformException catch (e) {
logger.e('Error starting temperature updates: ${e.message}');
return false;
}
}

static Future<void> stopTemperatureUpdates() async {
try {
await _methodChannel.invokeMethod('stopTemperatureUpdates');
_stopListening();
logger.d('Temperature updates stopped');
} on PlatformException catch (e) {
logger.e('Error stopping temperature updates: ${e.message}');
}
}

static void _startListening() {
_temperatureSubscription?.cancel();
_temperatureSubscription = _eventChannel.receiveBroadcastStream().listen(
(dynamic temperature) {
logger.d('Received temperature from stream: $temperature');
if (temperature is double) {
_temperatureController.add(temperature);
} else if (temperature is num) {
_temperatureController.add(temperature.toDouble());
}
},
onError: (error) {
logger.e('Temperature stream error: $error');
},
);
}

static void _stopListening() {
_temperatureSubscription?.cancel();
_temperatureSubscription = null;
}

static void dispose() {
_stopListening();
_temperatureController.close();
}
}
Loading