Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update the Android sensor code. #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
241 changes: 143 additions & 98 deletions android/src/main/java/com/aeyrium/sensor/AeyriumSensorPlugin.java
Original file line number Diff line number Diff line change
@@ -1,120 +1,165 @@
package com.aeyrium.sensor;

import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Surface;
import android.view.WindowManager;
import android.app.Activity;
import android.content.Context;

/** AeyriumSensorPlugin */
public class AeyriumSensorPlugin implements EventChannel.StreamHandler {

private static final String SENSOR_CHANNEL_NAME =
"plugins.aeyrium.com/sensor";
private static final int SENSOR_DELAY_MICROS = 1000 * 1000;//16 * 1000;
private WindowManager mWindowManager;
private SensorEventListener sensorEventListener;
private SensorManager sensorManager;
private Sensor sensor;
private int mLastAccuracy;

/** Plugin registration. */
public static void registerWith(Registrar registrar) {
final EventChannel sensorChannel =
new EventChannel(registrar.messenger(), SENSOR_CHANNEL_NAME);
sensorChannel.setStreamHandler(
new AeyriumSensorPlugin(registrar.context(), Sensor.TYPE_ROTATION_VECTOR, registrar));

}

private AeyriumSensorPlugin(Context context, int sensorType, Registrar registrar) {
mWindowManager = registrar.activity().getWindow().getWindowManager();
sensorManager = (SensorManager) context.getSystemService(context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(sensorType);
}

@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
sensorEventListener = createSensorEventListener(events);
sensorManager.registerListener(sensorEventListener, sensor, sensorManager.SENSOR_DELAY_UI);
}

@Override
public void onCancel(Object arguments) {
if (sensorManager != null && sensorEventListener != null){
sensorManager.unregisterListener(sensorEventListener);
import java.util.Arrays;

import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.PluginRegistry.Registrar;


/**
* AeyriumSensorPlugin
*/
public class AeyriumSensorPlugin implements EventChannel.StreamHandler, SensorEventListener {

private static final String SENSOR_CHANNEL_NAME = "plugins.aeyrium.com/sensor";
private static final int SENSOR_DELAY_uS = SensorManager.SENSOR_DELAY_UI;

private final float[] mVec4Rotation = new float[4];
private final float[] mMat4Rotation = new float[16];
private final float[] mMat4RotDisplay = new float[16];
private final float[] mMat4RotRemappedXZ = new float[16];
private final double[] mVec3Orientation = new double[3];

private WindowManager mWindowManager;
private SensorManager mSensorManager;
private Sensor mSensor;
private int mLastAccuracy;
private EventChannel.EventSink mEventSink;


/**
* Plugin registration.
*/
public static void registerWith(Registrar registrar) {
final EventChannel sensorChannel = new EventChannel(registrar.messenger(), SENSOR_CHANNEL_NAME);
sensorChannel.setStreamHandler(new AeyriumSensorPlugin(registrar.context(), registrar));

}
}

SensorEventListener createSensorEventListener(final EventChannel.EventSink events) {
return new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
private AeyriumSensorPlugin(Context context, Registrar registrar) {
mWindowManager = registrar.activity().getWindow().getWindowManager();
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager == null) {
throw new IllegalStateException("Sensor Manager not found");
}
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
}

@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
Arrays.fill(mVec4Rotation, 0);
mEventSink = events;
mSensorManager.registerListener(this, mSensor, SENSOR_DELAY_uS);
}

@Override
public void onCancel(Object arguments) {
mSensorManager.unregisterListener(this, mSensor);
mEventSink = null;
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
if (mLastAccuracy != accuracy) {
mLastAccuracy = accuracy;
mLastAccuracy = accuracy;
}
}
}


@Override
public void onSensorChanged(SensorEvent event) {
if (mLastAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
return;
@Override
public void onSensorChanged(SensorEvent event) {
if (mLastAccuracy == SensorManager.SENSOR_STATUS_UNRELIABLE || mEventSink == null) {
return;
}

updateOrientation(event.values, events);
}
};
}

private void updateOrientation(float[] rotationVector, EventChannel.EventSink events) {
float[] rotationMatrix = new float[9];
SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVector);
// Low-pass filter the incoming event data to smooth it out.
lowPassFilter(event.values, mVec4Rotation, 0.3f);

// Get the rotation matrix from the smoothed event rotation vector.
SensorManager.getRotationMatrixFromVector(mMat4Rotation, mVec4Rotation);

// Adjust the matrix to take into account the device orientation.
remapRotationMatrixByDisplay(mWindowManager, mMat4RotDisplay, mMat4Rotation);

// Remap to adjust for display orientation.
SensorManager.remapCoordinateSystem(mMat4RotDisplay, SensorManager.AXIS_X,
SensorManager.AXIS_Z, mMat4RotRemappedXZ);

// Capture the orientation vector from the rotation matrix.
//SensorManager.getOrientation(mMat4RotRemappedXZ, mVec3Orientation);
mVec3Orientation[0] = Math.atan2(mMat4RotRemappedXZ[1], mMat4RotRemappedXZ[5]);
mVec3Orientation[1] = Math.asin(-mMat4RotRemappedXZ[9]);
mVec3Orientation[2] = Math.atan2(-mMat4RotRemappedXZ[8], mMat4RotRemappedXZ[10]);

final int worldAxisForDeviceAxisX;
final int worldAxisForDeviceAxisY;
mEventSink.success(mVec3Orientation);
}

private static void lowPassFilter(float[] input, float[] prev, float alpha) {
if (input == null || prev == null) {
throw new NullPointerException("input and prev float arrays must be non-NULL");
}
int length = Math.min(input.length, prev.length);
for (int i = 0; i < length; i++) {
prev[i] = prev[i] + alpha * (input[i] - prev[i]);
}
}

/**
* https://android-developers.blogspot.co.uk/2010/09/one-screen-turn-deserves-another.html
*/
@SuppressWarnings("SuspiciousNameCombination")
private static void remapRotationMatrixByDisplay(WindowManager windowManager, float[] outMatrix, float[] inMatrix) {
int x = SensorManager.AXIS_X, y = SensorManager.AXIS_Y;
switch (windowManager.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
x = SensorManager.AXIS_X;
y = SensorManager.AXIS_Y;
break;
case Surface.ROTATION_90:
x = SensorManager.AXIS_Y;
y = SensorManager.AXIS_MINUS_X;
break;
case Surface.ROTATION_180:
x = SensorManager.AXIS_MINUS_X;
y = SensorManager.AXIS_MINUS_Y;
break;
case Surface.ROTATION_270:
x = SensorManager.AXIS_MINUS_Y;
y = SensorManager.AXIS_X;
break;
}
SensorManager.remapCoordinateSystem(inMatrix, x, y, outMatrix);
}

/*
// Remap the axes as if the device screen was the instrument panel,
// and adjust the rotation matrix for the device orientation.
switch (mWindowManager.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
default:
worldAxisForDeviceAxisX = SensorManager.AXIS_X;
worldAxisForDeviceAxisY = SensorManager.AXIS_Z;
break;
case Surface.ROTATION_90:
worldAxisForDeviceAxisX = SensorManager.AXIS_Z;
worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X;
break;
case Surface.ROTATION_180:
worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X;
worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z;
break;
case Surface.ROTATION_270:
worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z;
worldAxisForDeviceAxisY = SensorManager.AXIS_X;
break;
case Surface.ROTATION_0:
default:
worldAxisForDeviceAxisX = SensorManager.AXIS_X;
worldAxisForDeviceAxisY = SensorManager.AXIS_Z;
break;
case Surface.ROTATION_90:
worldAxisForDeviceAxisX = SensorManager.AXIS_Z;
worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X;
break;
case Surface.ROTATION_180:
worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X;
worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z;
break;
case Surface.ROTATION_270:
worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z;
worldAxisForDeviceAxisY = SensorManager.AXIS_X;
break;
}


float[] adjustedRotationMatrix = new float[9];
SensorManager.remapCoordinateSystem(rotationMatrix, worldAxisForDeviceAxisX,
worldAxisForDeviceAxisY, adjustedRotationMatrix);

// Transform rotation matrix into azimuth/pitch/roll
float[] orientation = new float[3];
SensorManager.getOrientation(adjustedRotationMatrix, orientation);

double pitch = - orientation[1];
double roll = - orientation[2];
double[] sensorValues = new double[2];
sensorValues[0] = pitch;
sensorValues[1] = roll;
events.success(sensorValues);
}
}
*/
}
85 changes: 57 additions & 28 deletions lib/aeyrium_sensor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,69 @@ import 'dart:async';

import 'package:flutter/services.dart';

const EventChannel _sensorEventChannel =
EventChannel('plugins.aeyrium.com/sensor');
const EventChannel _sensorEventChannel = EventChannel('plugins.aeyrium.com/sensor');

class SensorEvent {
/// Pitch from the device in radians
/// A pitch is a rotation around a lateral (X) axis that passes through the device from side to side
final double pitch;

///Roll value from the device in radians
///A roll is a rotation around a longitudinal (Y) axis that passes through the device from its top to bottom
final double roll;
/// Azimuth, angle of rotation about the -z axis in radians.
///
/// This value represents the angle between the device's y axis and the magnetic
/// north pole. When facing north, this angle is 0, when facing south, this
/// angle is π. Likewise, when facing east, this angle is π/2, and when facing
/// west, this angle is -π/2.
///
/// The range of values is -π to π.
final double azimuth;

SensorEvent(this.pitch, this.roll);
/// Pitch, angle of rotation about the x axis in radians.
///
/// A pitch is a rotation around a lateral (X) axis that passes through the device
/// from side to side.
///
/// This value represents the angle between a plane parallel to the device's screen
/// and a plane parallel to the ground. Assuming that the bottom edge of the device
/// faces the user and that the screen is face-up, tilting the top edge of the
/// device toward the ground creates a positive pitch angle.
///
/// The range of values is -π to π.
final double pitch;

@override
String toString() => '[Event: (pitch: $pitch, roll: $roll)]';
///Roll value from the device in radians
/// Roll, angle of rotation about the y axis in radians.
///
/// A roll is a rotation around a longitudinal (Y) axis that passes through the device
/// from top to bottom.
///
/// This value represents the angle between a plane perpendicular to the device's
/// screen and a plane perpendicular to the ground. Assuming that the bottom edge of
/// the device faces the user and that the screen is face-up, tilting the left edge
/// of the device toward the ground creates a positive roll angle.
///
/// The range of values is -π/2 to π/2.
final double roll;

SensorEvent(this.azimuth, this.pitch, this.roll);

@override
String toString() => '[SensorEvent: (azimuth: $azimuth, pitch: $pitch, roll: $roll)]';
}

class AeyriumSensor {
static Stream<SensorEvent> _sensorEvents;

AeyriumSensor._();

/// A broadcast stream of events from the device rotation sensor.
static Stream<SensorEvent> get sensorEvents {
if (_sensorEvents == null) {
_sensorEvents = _sensorEventChannel
.receiveBroadcastStream()
.map((dynamic event) => _listToSensorEvent(event.cast<double>()));
}
return _sensorEvents;
}

static SensorEvent _listToSensorEvent(List<double> list) {
return SensorEvent(list[0], list[1]);
}
static Stream<SensorEvent> _sensorEvents;

AeyriumSensor._();

/// A broadcast stream of events from the device rotation sensor.
static Stream<SensorEvent> get sensorEvents {
if (_sensorEvents == null) {
_sensorEvents = _sensorEventChannel
.receiveBroadcastStream()
.map((dynamic event) => _listToSensorEvent(event.cast<double>()));
}
return _sensorEvents;
}

static SensorEvent _listToSensorEvent(List<double> list) {
return SensorEvent(list[0], list[1], list[2]);
}
}