AI2 Component  (Version nb184)
Pedometer.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 
19 
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.hardware.Sensor;
23 import android.hardware.SensorEvent;
24 import android.hardware.SensorEventListener;
25 import android.hardware.SensorManager;
26 import android.util.Log;
27 
32 @DesignerComponent(version = YaVersion.PEDOMETER_COMPONENT_VERSION,
33  description = "A Component that acts like a Pedometer. It senses motion via the " +
34  "Accerleromter and attempts to determine if a step has been " +
35  "taken. Using a configurable stride length, it can estimate the " +
36  "distance traveled as well. ",
37  category = ComponentCategory.SENSORS,
38  nonVisible = true,
39  iconName = "images/pedometer.png")
40 @SimpleObject
42  implements Component, SensorEventListener, Deleteable {
43  private static final String TAG = "Pedometer";
44  private static final String PREFS_NAME = "PedometerPrefs";
45 
46  private static final int INTERVAL_VARIATION = 250;
47  private static final int NUM_INTERVALS = 2;
48  private static final int WIN_SIZE = 100;
49  private static final float STRIDE_LENGTH = (float) 0.73;
50  private static final float PEAK_VALLEY_RANGE = (float) 40.0;
51 
52  private final Context context;
53  private final SensorManager sensorManager;
54 
55  private int stopDetectionTimeout = 2000;
56  private int winPos = 0, intervalPos = 0;
57  private int numStepsWithFilter = 0, numStepsRaw = 0;
58  private float lastValley = 0;
59  private float[] lastValues = new float[WIN_SIZE];
60  private float strideLength = STRIDE_LENGTH;
61  private float totalDistance = 0;
62  private long[] stepInterval = new long[NUM_INTERVALS];
63  private long stepTimestamp = 0;
64  private long startTime = 0, prevStopClockTime = 0;
65  private boolean foundValley = false;
66  private boolean startPeaking = false;
67  private boolean foundNonStep = true;
68  private boolean pedometerPaused = true;
69 
70  private float[] avgWindow = new float[10];
71  private int avgPos = 0;
72 
74  public Pedometer(ComponentContainer container) {
75  super(container.$form());
76  context = container.$context();
77  // some initialization
78  winPos = 0;
79  startPeaking = false;
80  numStepsWithFilter = 0;
81  numStepsRaw = 0;
82 
83  foundValley = true;
84  lastValley = 0;
85 
86  sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
87 
88  // Restore preferences
89  SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
90  strideLength = settings.getFloat("Pedometer.stridelength", STRIDE_LENGTH);
91  totalDistance = settings.getFloat("Pedometer.distance", 0);
92  numStepsRaw = settings.getInt("Pedometer.prevStepCount", 0);
93  prevStopClockTime = settings.getLong("Pedometer.clockTime", 0);
94  numStepsWithFilter = numStepsRaw;
95  startTime = System.currentTimeMillis();
96  Log.d(TAG, "Pedometer Created");
97  }
98 
99  // Simple functions
100 
104  @SimpleFunction(description = "Start counting steps")
105  public void Start() {
106  if (pedometerPaused) {
107  pedometerPaused = false;
108  sensorManager.registerListener(this,
109  sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0),
110  SensorManager.SENSOR_DELAY_FASTEST);
111  startTime = System.currentTimeMillis();
112  }
113  }
114 
118  @SimpleFunction(description = "Stop counting steps")
119  public void Stop() {
120  if (!pedometerPaused) {
121  pedometerPaused = true;
122  sensorManager.unregisterListener(this);
123  Log.d(TAG, "Unregistered listener on pause");
124  prevStopClockTime += (System.currentTimeMillis() - startTime);
125  }
126  }
127 
132  @SimpleFunction(description = "Resets the step counter, distance measure and time running.")
133  public void Reset() {
134  numStepsWithFilter = 0;
135  numStepsRaw = 0;
136  totalDistance = 0;
137  prevStopClockTime = 0;
138  startTime = System.currentTimeMillis();
139  }
140 
144  @Deprecated
145  @SimpleFunction(description = "Resumes counting, synonym of Start.")
146  public void Resume() {
147  Start();
148  }
149 
153  @Deprecated
154  @SimpleFunction(description = "Pause counting of steps and distance.")
155  public void Pause() {
156  Stop();
157  }
158 
163  @SimpleFunction(description = "Saves the pedometer state to the phone. Permits " +
164  "permits accumulation of steps and distance between invocations of an App that uses " +
165  "the pedometer. Different Apps will have their own saved state.")
166  public void Save() {
167  // Store preferences
168  SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
169  SharedPreferences.Editor editor = settings.edit();
170  editor.putFloat("Pedometer.stridelength", strideLength);
171  editor.putFloat("Pedometer.distance", totalDistance);
172  editor.putInt("Pedometer.prevStepCount", numStepsRaw);
173  if (pedometerPaused) {
174  editor.putLong("Pedometer.clockTime", prevStopClockTime);
175  } else {
176  editor.putLong("Pedometer.clockTime", prevStopClockTime +
177  (System.currentTimeMillis() - startTime));
178  }
179  editor.putLong("Pedometer.closeTime", System.currentTimeMillis());
180  editor.commit();
181  Log.d(TAG, "Pedometer state saved.");
182  }
183 
184  // Events
185 
193  @SimpleEvent(description = "This event is run when a raw step is detected.")
194  public void SimpleStep(int simpleSteps, float distance) {
195  EventDispatcher.dispatchEvent(this, "SimpleStep", simpleSteps, distance);
196  }
197 
206  @SimpleEvent(description = "This event is run when a walking step is detected. " +
207  "A walking step is a step that appears to be involved in forward motion.")
208  public void WalkStep(int walkSteps, float distance) {
209  EventDispatcher.dispatchEvent(this, "WalkStep", walkSteps, distance);
210  }
211 
212  // Properties
213 
222  defaultValue = "0.73")
224  description = "Set the average stride length in meters.",
225  category = PropertyCategory.BEHAVIOR)
226  public void StrideLength(float length) {
227  strideLength = length;
228  }
229 
237  public float StrideLength() {
238  return strideLength;
239  }
240 
247  defaultValue = "2000")
249  category = PropertyCategory.BEHAVIOR,
250  description = "The duration in milliseconds of idleness (no steps detected) " +
251  "after which to go into a \"stopped\" state")
252  public void StopDetectionTimeout(int timeout) {
253  stopDetectionTimeout = timeout;
254  }
255 
262  public int StopDetectionTimeout() {
263  return stopDetectionTimeout;
264  }
265 
272  category = PropertyCategory.BEHAVIOR, description = "The approximate distance traveled in meters.")
273  public float Distance() {
274  return totalDistance;
275  }
276 
283  category = PropertyCategory.BEHAVIOR, description = "Time elapsed in milliseconds since the pedometer was started.")
284  public long ElapsedTime() {
285  if (pedometerPaused) {
286  return prevStopClockTime;
287  } else {
288  return prevStopClockTime + (System.currentTimeMillis() - startTime);
289  }
290  }
291 
298  description = "The number of simple steps taken since the pedometer has started.")
299  public int SimpleSteps() {
300  return numStepsRaw;
301  }
302 
309  description = "the number of walk steps taken since the pedometer has started.")
310  public int WalkSteps() {
311  return numStepsWithFilter;
312  }
313 
318  private boolean areStepsEquallySpaced() {
319  float avg = 0;
320  int num = 0;
321  for (long interval : stepInterval) {
322  if (interval > 0) {
323  num++;
324  avg += interval;
325  }
326  }
327  avg = avg / num;
328  for (long interval : stepInterval) {
329  if (Math.abs(interval - avg) > INTERVAL_VARIATION) {
330  return false;
331  }
332  }
333  return true;
334  }
335 
339  private boolean isPeak() {
340  int mid = (winPos + WIN_SIZE / 2) % WIN_SIZE;
341  for (int i = 0; i < WIN_SIZE; i++) {
342  if (i != mid && lastValues[i] > lastValues[mid]) {
343  return false;
344  }
345  }
346  return true;
347  }
348 
352  private boolean isValley() {
353  int mid = (winPos + WIN_SIZE / 2) % WIN_SIZE;
354  for (int i = 0; i < WIN_SIZE; i++) {
355  if (i != mid && lastValues[i] < lastValues[mid]) {
356  return false;
357  }
358  }
359  return true;
360  }
361 
362  // SensorEventListener implementation
363 
364  @Override
365  public void onAccuracyChanged(Sensor sensor, int accuracy) {
366  Log.d(TAG, "Accelerometer accuracy changed.");
367  }
368 
369  @Override
370  public void onSensorChanged(SensorEvent event) {
371  if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
372  return;
373  }
374  float[] values = event.values;
375  float magnitude = 0;
376  for (float v : values) magnitude += v * v;
377  // Check if the middle reading within the current window represents
378  // a peak/valley.
379  int mid = (winPos + WIN_SIZE / 2) % WIN_SIZE;
380 
381  // Peak is detected
382  if (startPeaking && isPeak()) {
383  if (foundValley && lastValues[mid] - lastValley > PEAK_VALLEY_RANGE) {
384  // Step detected on axis k with maximum peak-valley range.
385  long timestamp = System.currentTimeMillis();
386  stepInterval[intervalPos] = timestamp - stepTimestamp;
387  intervalPos = (intervalPos + 1) % NUM_INTERVALS;
388  stepTimestamp = timestamp;
389  if (areStepsEquallySpaced()) {
390  if (foundNonStep) {
391  numStepsWithFilter += NUM_INTERVALS;
392  totalDistance += strideLength * NUM_INTERVALS;
393  foundNonStep = false;
394  }
395  numStepsWithFilter++;
396  WalkStep(numStepsWithFilter, totalDistance);
397  totalDistance += strideLength;
398  } else {
399  foundNonStep = true;
400  }
401  numStepsRaw++;
402  SimpleStep(numStepsRaw, totalDistance);
403  foundValley = false;
404  }
405  }
406  // Valley is detected
407  if (startPeaking && isValley()) {
408  foundValley = true;
409  lastValley = lastValues[mid];
410  }
411  // Store latest accelerometer reading in the window.
412  avgWindow[avgPos] = magnitude;
413  avgPos = (avgPos + 1) % avgWindow.length;
414  lastValues[winPos] = 0;
415  for (float m : avgWindow) lastValues[winPos] += m;
416  lastValues[winPos] /= avgWindow.length;
417  if (startPeaking || winPos > 1) {
418  int i = winPos;
419  if (--i < 0) i += WIN_SIZE;
420  lastValues[winPos] += 2 * lastValues[i];
421  if (--i < 0) i += WIN_SIZE;
422  lastValues[winPos] += lastValues[i];
423  lastValues[winPos] /= 4;
424  } else if (!startPeaking && winPos == 1) {
425  lastValues[1] = (lastValues[1] + lastValues[0]) / 2f;
426  }
427 
428  long elapsedTimestamp = System.currentTimeMillis();
429  if (elapsedTimestamp - stepTimestamp > stopDetectionTimeout) {
430  stepTimestamp = elapsedTimestamp;
431  }
432  // Once the buffer is full, start peak/valley detection.
433  if (winPos == WIN_SIZE - 1 && !startPeaking) {
434  startPeaking = true;
435  }
436  // Increment position within the window.
437  winPos = (winPos + 1) % WIN_SIZE;
438  }
439 
440  // Deleteable implementation
441  @Override
442  public void onDelete() {
443  sensorManager.unregisterListener(this);
444  }
445 
446  // DEPRECATED:
447  // Everything below here is deprecated. We cannot completely remove them
448  // because older projects loaded into the system that use them would break.
449  // Instead we leave stub routines which are annotated as @Deprecated. This
450  // will result in blocks for these routines to be marked bad when a project
451  // that has them is loaded.
452 
453  @Deprecated
454  @SimpleEvent(description = "This event has been deprecated.")
455  public void StartedMoving() {
456  }
457 
458  @Deprecated
459  @SimpleEvent(description = "This event has been deprecated.")
460  public void StoppedMoving() {
461 
462  }
463 
464  @Deprecated
466  description = "This property has been deprecated.")
467  public void UseGPS(boolean gps) {
468  }
469 
470  @Deprecated
471  @SimpleEvent(description = "This event has been deprecated.")
472  public void CalibrationFailed() {
473  }
474 
475  @Deprecated
476  @SimpleEvent(description = "This event has been deprecated.")
477  public void GPSAvailable() {
478  }
479 
480  @Deprecated
481  @SimpleEvent(description = "This event has been deprecated.")
482  public void GPSLost() {
483  }
484 
485  // Properties
486 
487  @Deprecated
489  description = "This property has been deprecated.")
490  public void CalibrateStrideLength(boolean cal) {
491  }
492 
493  @Deprecated
494  @SimpleProperty(description = "This property has been deprecated.")
495  public boolean CalibrateStrideLength() {
496  return false;
497  }
498 
499  @Deprecated
500  @SimpleProperty(description = "This property has been deprecated.")
501  public boolean Moving() {
502  return false;
503  }
504 
505 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.Pedometer.onAccuracyChanged
void onAccuracyChanged(Sensor sensor, int accuracy)
Definition: Pedometer.java:365
com.google.appinventor.components.runtime.Pedometer.StartedMoving
void StartedMoving()
Definition: Pedometer.java:455
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.Pedometer.Moving
boolean Moving()
Definition: Pedometer.java:501
com.google.appinventor.components.runtime.Pedometer.Pedometer
Pedometer(ComponentContainer container)
Definition: Pedometer.java:74
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.Pedometer.Distance
float Distance()
Definition: Pedometer.java:273
com.google.appinventor.components.runtime.Pedometer.onDelete
void onDelete()
Definition: Pedometer.java:442
com.google.appinventor.components
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_INTEGER
static final String PROPERTY_TYPE_NON_NEGATIVE_INTEGER
Definition: PropertyTypeConstants.java:206
com.google.appinventor.components.runtime.Pedometer.WalkSteps
int WalkSteps()
Definition: Pedometer.java:310
com.google.appinventor.components.runtime.Pedometer.onSensorChanged
void onSensorChanged(SensorEvent event)
Definition: Pedometer.java:370
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.runtime.Pedometer.SimpleStep
void SimpleStep(int simpleSteps, float distance)
Definition: Pedometer.java:194
com.google.appinventor.components.runtime.Pedometer.GPSAvailable
void GPSAvailable()
Definition: Pedometer.java:477
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.Pedometer.Start
void Start()
Definition: Pedometer.java:105
com.google.appinventor.components.runtime.Pedometer.StrideLength
float StrideLength()
Definition: Pedometer.java:237
com.google.appinventor.components.runtime.Pedometer.CalibrationFailed
void CalibrationFailed()
Definition: Pedometer.java:472
com.google.appinventor.components.runtime.Pedometer
Definition: Pedometer.java:41
com.google.appinventor.components.runtime.Pedometer.CalibrateStrideLength
boolean CalibrateStrideLength()
Definition: Pedometer.java:495
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.Pedometer.WalkStep
void WalkStep(int walkSteps, float distance)
Definition: Pedometer.java:208
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.Pedometer.Resume
void Resume()
Definition: Pedometer.java:146
com.google.appinventor.components.runtime.Pedometer.SimpleSteps
int SimpleSteps()
Definition: Pedometer.java:299
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.Pedometer.Save
void Save()
Definition: Pedometer.java:166
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.Deleteable
Definition: Deleteable.java:15
com.google.appinventor.components.runtime.Pedometer.StoppedMoving
void StoppedMoving()
Definition: Pedometer.java:460
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.Pedometer.StopDetectionTimeout
int StopDetectionTimeout()
Definition: Pedometer.java:262
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_NON_NEGATIVE_FLOAT
static final String PROPERTY_TYPE_NON_NEGATIVE_FLOAT
Definition: PropertyTypeConstants.java:200
com
com.google.appinventor.components.runtime.Pedometer.UseGPS
void UseGPS(boolean gps)
Definition: Pedometer.java:467
com.google.appinventor.components.runtime.Pedometer.Pause
void Pause()
Definition: Pedometer.java:155
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.Pedometer.Reset
void Reset()
Definition: Pedometer.java:133
com.google.appinventor.components.runtime.Pedometer.ElapsedTime
long ElapsedTime()
Definition: Pedometer.java:284
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.runtime.Pedometer.Stop
void Stop()
Definition: Pedometer.java:119
com.google.appinventor.components.annotations
com.google.appinventor
com.google.appinventor.components.runtime.Pedometer.GPSLost
void GPSLost()
Definition: Pedometer.java:482