AI2 Component  (Version nb184)
OrientationSensor.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2012 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
21 
22 import android.content.Context;
23 import android.hardware.Sensor;
24 import android.hardware.SensorEvent;
25 import android.hardware.SensorEventListener;
26 import android.hardware.SensorManager;
27 import android.util.Log;
28 import android.view.Display;
29 import android.view.Surface;
30 import android.view.WindowManager;
31 
53 @DesignerComponent(version = YaVersion.ORIENTATIONSENSOR_COMPONENT_VERSION,
54  description = "<p>Non-visible component providing information about the " +
55  "device's physical orientation in three dimensions: <ul> " +
56  "<li> <strong>Roll</strong>: 0 degrees when the device is level, increases to " +
57  " 90 degrees as the device is tilted up on its left side, and " +
58  " decreases to -90 degrees when the device is tilted up on its right side. " +
59  " </li> " +
60  "<li> <strong>Pitch</strong>: 0 degrees when the device is level, up to " +
61  " 90 degrees as the device is tilted so its top is pointing down, " +
62  " up to 180 degrees as it gets turned over. Similarly, as the device " +
63  " is tilted so its bottom points down, pitch decreases to -90 " +
64  " degrees, then further decreases to -180 degrees as it gets turned all the way " +
65  " over.</li> " +
66  "<li> <strong>Azimuth</strong>: 0 degrees when the top of the device is " +
67  " pointing north, 90 degrees when it is pointing east, 180 degrees " +
68  " when it is pointing south, 270 degrees when it is pointing west, " +
69  " etc.</li></ul>" +
70  " These measurements assume that the device itself is not moving.</p>",
71  category = ComponentCategory.SENSORS,
72  nonVisible = true,
73  iconName = "images/orientationsensor.png")
74 
75 @SimpleObject
77  implements SensorEventListener, Deleteable, OnPauseListener, OnResumeListener {
78  // Constants
79  private static final String LOG_TAG = "OrientationSensor";
80  // offsets in array returned by SensorManager.getOrientation()
81  private static final int AZIMUTH = 0;
82  private static final int PITCH = 1;
83  private static final int ROLL = 2;
84  private static final int DIMENSIONS = 3; // Warning: specific to our universe
85 
86  // Properties
87  private boolean enabled;
88  private float azimuth; // degrees
89  private float pitch; // degrees
90  private float roll; // degrees
91  private int accuracy;
92 
93  // Sensor information
94  private final SensorManager sensorManager;
95  private final Sensor accelerometerSensor;
96  private final Sensor magneticFieldSensor;
97  private boolean listening;
98 
99  // Pre-allocated arrays to hold sensor data so that we don't cause so many garbage collections
100  // while processing sensor events. All are used only in onSensorChanged.
101  private final float[] accels = new float[DIMENSIONS]; // acceleration vector
102  private final float[] mags = new float[DIMENSIONS]; // magnetic field vector
103 
104  // Flags to tell whether the above arrays are filled. They are set in onSensorChanged and cleared
105  // in stopListening.
106  private boolean accelsFilled;
107  private boolean magsFilled;
108 
109  // Pre-allocated matrixes used to compute orientation values from acceleration and magnetic
110  // field data.
111  private final float[] rotationMatrix = new float[DIMENSIONS * DIMENSIONS];
112  private final float[] inclinationMatrix = new float[DIMENSIONS * DIMENSIONS];
113  private final float[] values = new float[DIMENSIONS];
114 
121  super(container.$form());
122 
123  // Get sensors, and start listening.
124  sensorManager =
125  (SensorManager) container.$context().getSystemService(Context.SENSOR_SERVICE);
126  accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
127  magneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
128 
129  // Begin listening in onResume() and stop listening in onPause().
131  form.registerForOnPause(this);
132 
133  // Set default property values.
134  Enabled(true);
135  }
136 
137  private void startListening() {
138  if (!listening) {
139  sensorManager.registerListener(this, accelerometerSensor,
140  SensorManager.SENSOR_DELAY_NORMAL);
141  sensorManager.registerListener(this, magneticFieldSensor,
142  SensorManager.SENSOR_DELAY_NORMAL);
143  listening = true;
144  }
145  }
146 
147  private void stopListening() {
148  if (listening) {
149  sensorManager.unregisterListener(this);
150  listening = false;
151 
152  // Throw out sensor information that will go stale.
153  accelsFilled = false;
154  magsFilled = false;
155  }
156  }
157 
158  // Events
159 
173  @SimpleEvent(description = "Called when the orientation has changed.")
174  public void OrientationChanged(float azimuth, float pitch, float roll) {
175  EventDispatcher.dispatchEvent(this, "OrientationChanged", azimuth, pitch, roll);
176  }
177 
178  // Properties
179 
187  public boolean Available() {
188  return sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() > 0
189  && sensorManager.getSensorList(Sensor.TYPE_MAGNETIC_FIELD).size() > 0;
190  }
191 
199  public boolean Enabled() {
200  return enabled;
201  }
202 
211  defaultValue = "True")
213  public void Enabled(boolean enabled) {
214  if (this.enabled != enabled) {
215  this.enabled = enabled;
216  if (enabled) {
217  startListening();
218  } else {
219  stopListening();
220  }
221  }
222  }
223 
231  public float Pitch() {
232  return pitch;
233  }
234 
242  public float Roll() {
243  return roll;
244  }
245 
253  public float Azimuth() {
254  return azimuth;
255  }
256 
272  public float Angle() {
273  return OrientationSensor.computeAngle(pitch, roll);
274  }
275 
292  static float computeAngle(float pitch, float roll) {
293  return (float) Math.toDegrees(Math.atan2(Math.toRadians(pitch),
294  // invert roll to correct sign
295  -Math.toRadians(roll)));
296  }
297 
309  @SimpleProperty(category = PropertyCategory.BEHAVIOR)
310  public float Magnitude() {
311  // Limit pitch and roll to 90; otherwise, the phone is upside down.
312  // The official documentation falsely claims that the range of pitch and
313  // roll is [-90, 90]. If the device is upside-down, it can range from
314  // -180 to 180. We restrict it to the range [-90, 90].
315  // With that restriction, if the pitch and roll angles are P and R, then
316  // the force is given by 1 - cos(P)cos(R). I have found a truly wonderful
317  // proof of this theorem, but the margin enforced by Lint is too small to
318  // contain it.
319  final int MAX_VALUE = 90;
320  double npitch = Math.toRadians(Math.min(MAX_VALUE, Math.abs(pitch)));
321  double nroll = Math.toRadians(Math.min(MAX_VALUE, Math.abs(roll)));
322  return (float) (1.0 - Math.cos(npitch) * Math.cos(nroll));
323  }
324 
325  // SensorListener implementation
326 
327  /*
328  * Returns the rotation of the screen from its "natural" orientation.
329  * Note that this is the angle of rotation of the drawn graphics on the
330  * screen, which is the opposite direction of the physical rotation of the
331  * device. For example, if the device is rotated 90 degrees counter-clockwise,
332  * to compensate rendering will be rotated by 90 degrees clockwise and thus
333  * the returned value here will be Surface.ROTATION_90. Return values will
334  * be in the set Surface.ROTATION_{0,90,180,270}.
335  */
336  private int getScreenRotation() {
337  Display display =
338  ((WindowManager) form.getSystemService(Context.WINDOW_SERVICE)).
339  getDefaultDisplay();
341  return FroyoUtil.getRotation(display);
342  } else {
343  return display.getOrientation();
344  }
345  }
346 
355  @Override
356  public void onSensorChanged(SensorEvent sensorEvent) {
357  if (enabled) {
358  int eventType = sensorEvent.sensor.getType();
359 
360  // Save the new sensor information about acceleration or the magnetic field.
361  switch (eventType) {
362  case Sensor.TYPE_ACCELEROMETER:
363  // Update acceleration array.
364  System.arraycopy(sensorEvent.values, 0, accels, 0, DIMENSIONS);
365  accelsFilled = true;
366  // Only update the accuracy property for the accelerometer.
367  accuracy = sensorEvent.accuracy;
368  break;
369 
370  case Sensor.TYPE_MAGNETIC_FIELD:
371  // Update magnetic field array.
372  System.arraycopy(sensorEvent.values, 0, mags, 0, DIMENSIONS);
373  magsFilled = true;
374  break;
375 
376  default:
377  Log.e(LOG_TAG, "Unexpected sensor type: " + eventType);
378  return;
379  }
380 
381  // If we have both acceleration and magnetic information, recompute values.
382  if (accelsFilled && magsFilled) {
383  SensorManager.getRotationMatrix(rotationMatrix, // output
384  inclinationMatrix, // output
385  accels,
386  mags);
387  SensorManager.getOrientation(rotationMatrix, values);
388 
389  // Make sure values are in expected range.
391  (float) Math.toDegrees(values[AZIMUTH]));
393  (float) Math.toDegrees(values[PITCH]));
394  // Sign change for roll is for compatibility with earlier versions
395  // of App Inventor that got orientation sensor information differently.
397  (float) -Math.toDegrees(values[ROLL]));
398 
399  // Adjust pitch and roll for phone rotation (e.g., landscape)
400  int rotation = getScreenRotation();
401  switch(rotation) {
402  case Surface.ROTATION_0: // normal rotation
403  break;
404  case Surface.ROTATION_90: // phone is turned 90 degrees counter-clockwise
405  float temp = -pitch;
406  pitch = -roll;
407  roll = temp;
408  break;
409  case Surface.ROTATION_180: // phone is rotated 180 degrees
410  roll = -roll;
411  break;
412  case Surface.ROTATION_270: // phone is turned 90 degrees clockwise
413  temp = pitch;
414  pitch = roll;
415  roll = temp;
416  break;
417  default:
418  Log.e(LOG_TAG, "Illegal value for getScreenRotation(): " +
419  rotation);
420  break;
421  }
422 
423  // Raise event.
424  OrientationChanged(azimuth, pitch, roll);
425  }
426  }
427  }
428 
429  @Override
430  public void onAccuracyChanged(Sensor sensor, int accuracy) {
431  // TODO(markf): Figure out if we actually need to do something here.
432  }
433 
434  // Deleteable implementation
435 
436  @Override
437  public void onDelete() {
438  stopListening();
439  }
440 
441  // OnPauseListener implementation
442 
443  public void onPause() {
444  stopListening();
445  }
446 
447  // OnResumeListener implementation
448 
449  public void onResume() {
450  if (enabled) {
451  startListening();
452  }
453  }
454 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.OrientationSensor.onSensorChanged
void onSensorChanged(SensorEvent sensorEvent)
Definition: OrientationSensor.java:356
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.runtime.Form.registerForOnResume
void registerForOnResume(OnResumeListener component)
Definition: Form.java:740
com.google.appinventor.components.runtime.OrientationSensor.Roll
float Roll()
Definition: OrientationSensor.java:242
com.google.appinventor.components
com.google.appinventor.components.runtime.OrientationSensor.Angle
float Angle()
Definition: OrientationSensor.java:272
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN
static final String PROPERTY_TYPE_BOOLEAN
Definition: PropertyTypeConstants.java:35
com.google.appinventor.components.runtime.OrientationSensor.Enabled
boolean Enabled()
Definition: OrientationSensor.java:199
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.util.OrientationSensorUtil.normalizeAzimuth
static float normalizeAzimuth(float azimuth)
Definition: OrientationSensorUtil.java:47
com.google.appinventor.components.runtime.util.FroyoUtil.getRotation
static int getRotation(Display display)
Definition: FroyoUtil.java:41
com.google.appinventor.components.runtime.OrientationSensor.Available
boolean Available()
Definition: OrientationSensor.java:187
com.google.appinventor.components.runtime.OrientationSensor.Pitch
float Pitch()
Definition: OrientationSensor.java:231
com.google.appinventor.components.runtime.util.FroyoUtil
Definition: FroyoUtil.java:28
com.google.appinventor.components.runtime.OnResumeListener
Definition: OnResumeListener.java:14
com.google.appinventor.components.runtime.OrientationSensor.Azimuth
float Azimuth()
Definition: OrientationSensor.java:253
com.google.appinventor.components.runtime.OrientationSensor
Definition: OrientationSensor.java:76
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.runtime.util.SdkLevel
Definition: SdkLevel.java:19
com.google.appinventor.components.runtime.util.OrientationSensorUtil.normalizePitch
static float normalizePitch(float pitch)
Definition: OrientationSensorUtil.java:57
com.google.appinventor.components.runtime.OrientationSensor.Magnitude
float Magnitude()
Definition: OrientationSensor.java:310
com.google.appinventor.components.runtime.OnPauseListener
Definition: OnPauseListener.java:14
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.OrientationSensor.onAccuracyChanged
void onAccuracyChanged(Sensor sensor, int accuracy)
Definition: OrientationSensor.java:430
com.google.appinventor.components.runtime.OrientationSensor.OrientationChanged
void OrientationChanged(float azimuth, float pitch, float roll)
Definition: OrientationSensor.java:174
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.util.SdkLevel.getLevel
static int getLevel()
Definition: SdkLevel.java:45
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Deleteable
Definition: Deleteable.java:15
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_FROYO
static final int LEVEL_FROYO
Definition: SdkLevel.java:25
com.google.appinventor.components.runtime.OrientationSensor.OrientationSensor
OrientationSensor(ComponentContainer container)
Definition: OrientationSensor.java:120
com.google.appinventor.components.runtime.util.OrientationSensorUtil
Definition: OrientationSensorUtil.java:15
com.google.appinventor.components.runtime.OrientationSensor.onPause
void onPause()
Definition: OrientationSensor.java:443
com.google.appinventor.components.runtime.Form.registerForOnPause
void registerForOnPause(OnPauseListener component)
Definition: Form.java:780
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google
com
com.google.appinventor.components.runtime.OrientationSensor.onDelete
void onDelete()
Definition: OrientationSensor.java:437
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.OrientationSensor.onResume
void onResume()
Definition: OrientationSensor.java:449
com.google.appinventor.components.runtime.util.OrientationSensorUtil.normalizeRoll
static float normalizeRoll(float roll)
Definition: OrientationSensorUtil.java:79
com.google.appinventor.components.runtime.OrientationSensor.Enabled
void Enabled(boolean enabled)
Definition: OrientationSensor.java:213
com.google.appinventor.components.runtime.AndroidNonvisibleComponent.form
final Form form
Definition: AndroidNonvisibleComponent.java:19
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.annotations
com.google.appinventor