diff options
author | Jan-Felix Schmakeit <jfschmakeit@google.com> | 2014-01-02 11:41:04 +1100 |
---|---|---|
committer | Alexander Lucas <alexlucas@google.com> | 2014-02-06 17:23:07 +0000 |
commit | 56216a861a0910fb7d4317be4a6c1b43697ebe09 (patch) | |
tree | 2ee6139c3ea29732421956546f4e3f316dabd7fe /sensors | |
parent | 2fa6e8d98412b6a91420ca217ef99d527d720417 (diff) | |
download | android-56216a861a0910fb7d4317be4a6c1b43697ebe09.tar.gz |
Add a sample showing the use of the step sensors
(STEP_DETECTOR and STEP_COUNTER) and sensor event batching.
Change-Id: Ied3a2601b09b12a53ddbe709b959776ca9e87c3c
(cherry picked from commit 501ae9a1d88ba4b5c877f4218b75630271c71e3c)
Diffstat (limited to 'sensors')
16 files changed, 1145 insertions, 0 deletions
diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/README-CardStream.txt b/sensors/BatchStepSensor/BatchStepSensorSample/README-CardStream.txt new file mode 100644 index 00000000..957ccd65 --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/README-CardStream.txt @@ -0,0 +1,82 @@ +<#-- + Copyright 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +Steps to implement CardStream template: +-in template-params.xml.ftl: + -add the following templates + <template src="base"/> + <template src="CardStream"/> + -add the following line to common imports + <common src="activities"/> + <common src="logger"/> + +-Add a Fragment to handle behavior. In your MainActivity.java class, it will reference a Fragment + called (yourProjectName)Fragment.java. Create that file in your project, using the "main" source + folder instead of "common" or "templates". + For instance, if your package name is com.example.foo, create the file + src/main/java/com/example/foo/FooFragment.java + +-Now it's time to deal with cards. Implement a method like this in your Fragment to access the CardStream: +private CardStreamFragment getCardStream() { + if (mCards == null) { + mCards = ((CardStream) getActivity()).getCardStream(); + } + return mCards; +} + + +-Create a instance of Card.Builder with a tag String that *must* be unique among all cards. + + Card.Builder builder = new Card.Builder(UNIQUE_TAG_STRING); + +-Set the properties for your card in the builder. Some properties (title, description, progress type) can also +be changed later. + + builder.setTitle(String title) + +-Cards can also have more than one action that is shown as a button at the bottom of the card. +All actions *must* be defined through the builder. They can be hidden or shown later again, but they must be defined +in the builder before .build() is called. + +-To implement an action, use Builder.addAction with a label, id, type (Neutral/Positive/Negative) and + a CardActionCallback. + Actions can be distinguished by their id to avoid the use of a large number of unnamed callback instances. + For convenience, the tag of the card the action belongs to is also returned in the callback. + + builder.addAction(actionLabel1, 0, Card.ACTION_NEUTRAL, new Card.CardActionCallback() { + @Override + public void onClick(int cardActionId, String tag) { + ... + } + }); + + +-After finishing setup process, call Buidler.build() to return a new instance of a Card. + + final Card card = builder.build(activity); + +-Inside your MainActivity.java class, call getCardStream() to get the instance of the CardStreamFragment through +which cards are shown on screen. +A card needs to be added to the CardStreamFragment first before it can be shown. +Cards are identified by their unique tag. + + Card myCard = ... + getCardStreamFragment().addCard(myCard); + getCardStreamFragment().show(myCard.getTag()); + getCardStreamFragment().hide("MyCardTag"); + getCardStreamFragment().show("MyCardTag",false); // can't be dismissed by user + + diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/AndroidManifest.xml b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/AndroidManifest.xml new file mode 100644 index 00000000..2c4e4f29 --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + + + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.batchstepsensor" + android:versionCode="1" + android:versionName="1.0"> + + <!-- This sample requires at least Android KitKat for sensor batching support --> + <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> + + <!-- Require the step counter and step detector sensors. + See the method BatchStepSensorFragment#isKitkatWithStepSensor() for a programmatic check if + support is optional and the application supports a case where these sensors are not available. + --> + <uses-feature android:name="android.hardware.sensor.stepcounter" /> + <uses-feature android:name="android.hardware.sensor.stepdetector" /> + + + <application android:allowBackup="true" + android:label="@string/app_name" + android:icon="@drawable/ic_launcher" + android:theme="@style/AppTheme"> + + <activity android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + + +</manifest> diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/java/com/example/android/batchstepsensor/BatchStepSensorFragment.java b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/java/com/example/android/batchstepsensor/BatchStepSensorFragment.java new file mode 100644 index 00000000..aab0fc38 --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/java/com/example/android/batchstepsensor/BatchStepSensorFragment.java @@ -0,0 +1,588 @@ +/* +* Copyright 2014 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.example.android.batchstepsensor; + +import android.app.Activity; +import android.content.pm.PackageManager; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Bundle; +import android.support.v4.app.Fragment; + +import com.example.android.common.logger.Log; + +public class BatchStepSensorFragment extends Fragment implements OnCardClickListener { + + public static final String TAG = "StepSensorSample"; + // Cards + private CardStreamFragment mCards = null; + + // Card tags + public static final String CARD_INTRO = "intro"; + public static final String CARD_REGISTER_DETECTOR = "register_detector"; + public static final String CARD_REGISTER_COUNTER = "register_counter"; + public static final String CARD_BATCHING_DESCRIPTION = "register_batching_description"; + public static final String CARD_COUNTING = "counting"; + public static final String CARD_EXPLANATION = "explanation"; + public static final String CARD_NOBATCHSUPPORT = "error"; + + // Actions from REGISTER cards + public static final int ACTION_REGISTER_DETECT_NOBATCHING = 10; + public static final int ACTION_REGISTER_DETECT_BATCHING_5s = 11; + public static final int ACTION_REGISTER_DETECT_BATCHING_10s = 12; + public static final int ACTION_REGISTER_COUNT_NOBATCHING = 21; + public static final int ACTION_REGISTER_COUNT_BATCHING_5s = 22; + public static final int ACTION_REGISTER_COUNT_BATCHING_10s = 23; + // Action from COUNTING card + public static final int ACTION_UNREGISTER = 1; + // Actions from description cards + private static final int ACTION_BATCHING_DESCRIPTION_DISMISS = 2; + private static final int ACTION_EXPLANATION_DISMISS = 3; + + // State of application, used to register for sensors when app is restored + public static final int STATE_OTHER = 0; + public static final int STATE_COUNTER = 1; + public static final int STATE_DETECTOR = 2; + + // Bundle tags used to store data when restoring application state + private static final String BUNDLE_STATE = "state"; + private static final String BUNDLE_LATENCY = "latency"; + private static final String BUNDLE_STEPS = "steps"; + + // max batch latency is specified in microseconds + private static final int BATCH_LATENCY_0 = 0; // no batching + private static final int BATCH_LATENCY_10s = 10000000; + private static final int BATCH_LATENCY_5s = 5000000; + + /* + For illustration we keep track of the last few events and show their delay from when the + event occurred until it was received by the event listener. + These variables keep track of the list of timestamps and the number of events. + */ + // Number of events to keep in queue and display on card + private static final int EVENT_QUEUE_LENGTH = 10; + // List of timestamps when sensor events occurred + private float[] mEventDelays = new float[EVENT_QUEUE_LENGTH]; + + // number of events in event list + private int mEventLength = 0; + // pointer to next entry in sensor event list + private int mEventData = 0; + + // Steps counted in current session + private int mSteps = 0; + // Value of the step counter sensor when the listener was registered. + // (Total steps are calculated from this value.) + private int mCounterSteps = 0; + // Steps counted by the step counter previously. Used to keep counter consistent across rotation + // changes + private int mPreviousCounterSteps = 0; + // State of the app (STATE_OTHER, STATE_COUNTER or STATE_DETECTOR) + private int mState = STATE_OTHER; + // When a listener is registered, the batch sensor delay in microseconds + private int mMaxDelay = 0; + + @Override + public void onResume() { + super.onResume(); + + CardStreamFragment stream = getCardStream(); + if (stream.getVisibleCardCount() < 1) { + // No cards are visible, started for the first time + // Prepare all cards and show the intro card. + initialiseCards(); + showIntroCard(); + // Show the registration card if the hardware is supported, show an error otherwise + if (isKitkatWithStepSensor()) { + showRegisterCard(); + } else { + showErrorCard(); + } + } + } + + @Override + public void onPause() { + super.onPause(); + // BEGIN_INCLUDE(onpause) + // Unregister the listener when the application is paused + unregisterListeners(); + // END_INCLUDE(onpause) + } + + /** + * Returns true if this device is supported. It needs to be running Android KitKat (4.4) or + * higher and has a step counter and step detector sensor. + * This check is useful when an app provides an alternative implementation or different + * functionality if the step sensors are not available or this code runs on a platform version + * below Android KitKat. If this functionality is required, then the minSDK parameter should + * be specified appropriately in the AndroidManifest. + * + * @return True iff the device can run this sample + */ + private boolean isKitkatWithStepSensor() { + // BEGIN_INCLUDE(iskitkatsensor) + // Require at least Android KitKat + int currentApiVersion = android.os.Build.VERSION.SDK_INT; + // Check that the device supports the step counter and detector sensors + PackageManager packageManager = getActivity().getPackageManager(); + return currentApiVersion >= android.os.Build.VERSION_CODES.KITKAT + && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER) + && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR); + // END_INCLUDE(iskitkatsensor) + } + + /** + * Handles a click on a card action. + * Registers a SensorEventListener (see {@link #registerEventListener(int, int)}) with the + * selected delay, dismisses cards and unregisters the listener + * (see {@link #unregisterListeners()}). + * Actions are defined when a card is created. + * + * @param cardActionId + * @param cardTag + */ + @Override + public void onCardClick(int cardActionId, String cardTag) { + + switch (cardActionId) { + // BEGIN_INCLUDE(onclick) + // Register Step Counter card + case ACTION_REGISTER_COUNT_NOBATCHING: + registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_COUNTER); + break; + case ACTION_REGISTER_COUNT_BATCHING_5s: + registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_COUNTER); + break; + case ACTION_REGISTER_COUNT_BATCHING_10s: + registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_COUNTER); + break; + + // Register Step Detector card + case ACTION_REGISTER_DETECT_NOBATCHING: + registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_DETECTOR); + break; + case ACTION_REGISTER_DETECT_BATCHING_5s: + registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_DETECTOR); + break; + case ACTION_REGISTER_DETECT_BATCHING_10s: + registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_DETECTOR); + break; + + // Unregister card + case ACTION_UNREGISTER: + showRegisterCard(); + unregisterListeners(); + // reset the application state when explicitly unregistered + mState = STATE_OTHER; + break; + // END_INCLUDE(onclick) + // Explanation cards + case ACTION_BATCHING_DESCRIPTION_DISMISS: + // permanently remove the batch description card, it will not be shown again + getCardStream().removeCard(CARD_BATCHING_DESCRIPTION); + break; + case ACTION_EXPLANATION_DISMISS: + // permanently remove the explanation card, it will not be shown again + getCardStream().removeCard(CARD_EXPLANATION); + } + + // For register cards, display the counting card + if (cardTag.equals(CARD_REGISTER_COUNTER) || cardTag.equals(CARD_REGISTER_DETECTOR)) { + showCountingCards(); + } + } + + /** + * Register a {@link android.hardware.SensorEventListener} for the sensor and max batch delay. + * The maximum batch delay specifies the maximum duration in microseconds for which subsequent + * sensor events can be temporarily stored by the sensor before they are delivered to the + * registered SensorEventListener. A larger delay allows the system to handle sensor events more + * efficiently, allowing the system to switch to a lower power state while the sensor is + * capturing events. Once the max delay is reached, all stored events are delivered to the + * registered listener. Note that this value only specifies the maximum delay, the listener may + * receive events quicker. A delay of 0 disables batch mode and registers the listener in + * continuous mode. + * The optimium batch delay depends on the application. For example, a delay of 5 seconds or + * higher may be appropriate for an application that does not update the UI in real time. + * + * @param maxdelay + * @param sensorType + */ + private void registerEventListener(int maxdelay, int sensorType) { + // BEGIN_INCLUDE(register) + + // Keep track of state so that the correct sensor type and batch delay can be set up when + // the app is restored (for example on screen rotation). + mMaxDelay = maxdelay; + if (sensorType == Sensor.TYPE_STEP_COUNTER) { + mState = STATE_COUNTER; + /* + Reset the initial step counter value, the first event received by the event listener is + stored in mCounterSteps and used to calculate the total number of steps taken. + */ + mCounterSteps = 0; + Log.i(TAG, "Event listener for step counter sensor registered with a max delay of " + + mMaxDelay); + } else { + mState = STATE_DETECTOR; + Log.i(TAG, "Event listener for step detector sensor registered with a max delay of " + + mMaxDelay); + } + + // Get the default sensor for the sensor type from the SenorManager + SensorManager sensorManager = + (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE); + // sensorType is either Sensor.TYPE_STEP_COUNTER or Sensor.TYPE_STEP_DETECTOR + Sensor sensor = sensorManager.getDefaultSensor(sensorType); + + // Register the listener for this sensor in batch mode. + // If the max delay is 0, events will be delivered in continuous mode without batching. + final boolean batchMode = sensorManager.registerListener( + mListener, sensor, SensorManager.SENSOR_DELAY_NORMAL, maxdelay); + + if (!batchMode) { + // Batch mode could not be enabled, show a warning message and switch to continuous mode + getCardStream().getCard(CARD_NOBATCHSUPPORT) + .setDescription(getString(R.string.warning_nobatching)); + getCardStream().showCard(CARD_NOBATCHSUPPORT); + Log.w(TAG, "Could not register sensor listener in batch mode, " + + "falling back to continuous mode."); + } + + if (maxdelay > 0 && batchMode) { + // Batch mode was enabled successfully, show a description card + getCardStream().showCard(CARD_BATCHING_DESCRIPTION); + } + + // Show the explanation card + getCardStream().showCard(CARD_EXPLANATION); + + // END_INCLUDE(register) + + } + + /** + * Unregisters the sensor listener if it is registered. + */ + private void unregisterListeners() { + // BEGIN_INCLUDE(unregister) + SensorManager sensorManager = + (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE); + sensorManager.unregisterListener(mListener); + Log.i(TAG, "Sensor listener unregistered."); + + // END_INCLUDE(unregister) + } + + /** + * Resets the step counter by clearing all counting variables and lists. + */ + private void resetCounter() { + // BEGIN_INCLUDE(reset) + mSteps = 0; + mCounterSteps = 0; + mEventLength = 0; + mEventDelays = new float[EVENT_QUEUE_LENGTH]; + mPreviousCounterSteps = 0; + // END_INCLUDE(reset) + } + + + /** + * Listener that handles step sensor events for step detector and step counter sensors. + */ + private final SensorEventListener mListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + // BEGIN_INCLUDE(sensorevent) + // store the delay of this event + recordDelay(event); + final String delayString = getDelayString(); + + if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) { + // A step detector event is received for each step. + // This means we need to count steps ourselves + + mSteps += event.values.length; + + // Update the card with the latest step count + getCardStream().getCard(CARD_COUNTING) + .setTitle(getString(R.string.counting_title, mSteps)) + .setDescription(getString(R.string.counting_description, + getString(R.string.sensor_detector), mMaxDelay, delayString)); + + Log.i(TAG, + "New step detected by STEP_DETECTOR sensor. Total step count: " + mSteps); + + } else if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) { + + /* + A step counter event contains the total number of steps since the listener + was first registered. We need to keep track of this initial value to calculate the + number of steps taken, as the first value a listener receives is undefined. + */ + if (mCounterSteps < 1) { + // initial value + mCounterSteps = (int) event.values[0]; + } + + // Calculate steps taken based on first counter value received. + mSteps = (int) event.values[0] - mCounterSteps; + + // Add the number of steps previously taken, otherwise the counter would start at 0. + // This is needed to keep the counter consistent across rotation changes. + mSteps = mSteps + mPreviousCounterSteps; + + // Update the card with the latest step count + getCardStream().getCard(CARD_COUNTING) + .setTitle(getString(R.string.counting_title, mSteps)) + .setDescription(getString(R.string.counting_description, + getString(R.string.sensor_counter), mMaxDelay, delayString)); + Log.i(TAG, "New step detected by STEP_COUNTER sensor. Total step count: " + mSteps); + // END_INCLUDE(sensorevent) + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + }; + + /** + * Records the delay for the event. + * + * @param event + */ + private void recordDelay(SensorEvent event) { + // Calculate the delay from when event was recorded until it was received here in ms + // Event timestamp is recorded in us accuracy, but ms accuracy is sufficient here + mEventDelays[mEventData] = System.currentTimeMillis() - (event.timestamp / 1000000L); + + // Increment length counter + mEventLength = Math.min(EVENT_QUEUE_LENGTH, mEventLength + 1); + // Move pointer to the next (oldest) location + mEventData = (mEventData + 1) % EVENT_QUEUE_LENGTH; + } + + private final StringBuffer mDelayStringBuffer = new StringBuffer(); + + /** + * Returns a string describing the sensor delays recorded in + * {@link #recordDelay(android.hardware.SensorEvent)}. + * + * @return + */ + private String getDelayString() { + // Empty the StringBuffer + mDelayStringBuffer.setLength(0); + + // Loop over all recorded delays and append them to the buffer as a decimal + for (int i = 0; i < mEventLength; i++) { + if (i > 0) { + mDelayStringBuffer.append(", "); + } + final int index = (mEventData + i) % EVENT_QUEUE_LENGTH; + final float delay = mEventDelays[index] / 1000f; // convert delay from ms into s + mDelayStringBuffer.append(String.format("%1.1f", delay)); + } + + return mDelayStringBuffer.toString(); + } + + /** + * Records the state of the application into the {@link android.os.Bundle}. + * + * @param outState + */ + @Override + public void onSaveInstanceState(Bundle outState) { + // BEGIN_INCLUDE(saveinstance) + super.onSaveInstanceState(outState); + // Store all variables required to restore the state of the application + outState.putInt(BUNDLE_LATENCY, mMaxDelay); + outState.putInt(BUNDLE_STATE, mState); + outState.putInt(BUNDLE_STEPS, mSteps); + // END_INCLUDE(saveinstance) + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // BEGIN_INCLUDE(restore) + // Fragment is being restored, reinitialise its state with data from the bundle + if (savedInstanceState != null) { + resetCounter(); + mSteps = savedInstanceState.getInt(BUNDLE_STEPS); + mState = savedInstanceState.getInt(BUNDLE_STATE); + mMaxDelay = savedInstanceState.getInt(BUNDLE_LATENCY); + + // Register listeners again if in detector or counter states with restored delay + if (mState == STATE_DETECTOR) { + registerEventListener(mMaxDelay, Sensor.TYPE_STEP_DETECTOR); + } else if (mState == STATE_COUNTER) { + // store the previous number of steps to keep step counter count consistent + mPreviousCounterSteps = mSteps; + registerEventListener(mMaxDelay, Sensor.TYPE_STEP_COUNTER); + } + } + // END_INCLUDE(restore) + } + + /** + * Hides the registration cards, reset the counter and show the step counting card. + */ + private void showCountingCards() { + // Hide the registration cards + getCardStream().hideCard(CARD_REGISTER_DETECTOR); + getCardStream().hideCard(CARD_REGISTER_COUNTER); + + // Show the explanation card if it has not been dismissed + getCardStream().showCard(CARD_EXPLANATION); + + // Reset the step counter, then show the step counting card + resetCounter(); + + // Set the inital text for the step counting card before a step is recorded + String sensor = "-"; + if (mState == STATE_COUNTER) { + sensor = getString(R.string.sensor_counter); + } else if (mState == STATE_DETECTOR) { + sensor = getString(R.string.sensor_detector); + } + // Set initial text + getCardStream().getCard(CARD_COUNTING) + .setTitle(getString(R.string.counting_title, 0)) + .setDescription(getString(R.string.counting_description, sensor, mMaxDelay, "-")); + + // Show the counting card and make it undismissable + getCardStream().showCard(CARD_COUNTING, false); + + } + + /** + * Show the introduction card + */ + private void showIntroCard() { + Card c = new Card.Builder(this, CARD_INTRO) + .setTitle(getString(R.string.intro_title)) + .setDescription(getString(R.string.intro_message)) + .build(getActivity()); + getCardStream().addCard(c, true); + } + + /** + * Show two registration cards, one for the step detector and counter sensors. + */ + private void showRegisterCard() { + // Hide the counting and explanation cards + getCardStream().hideCard(CARD_BATCHING_DESCRIPTION); + getCardStream().hideCard(CARD_EXPLANATION); + getCardStream().hideCard(CARD_COUNTING); + + // Show two undismissable registration cards, one for each step sensor + getCardStream().showCard(CARD_REGISTER_DETECTOR, false); + getCardStream().showCard(CARD_REGISTER_COUNTER, false); + } + + /** + * Show the error card. + */ + private void showErrorCard() { + getCardStream().showCard(CARD_NOBATCHSUPPORT, false); + } + + /** + * Initialise Cards. + */ + private void initialiseCards() { + // Step counting + Card c = new Card.Builder(this, CARD_COUNTING) + .setTitle("Steps") + .setDescription("") + .addAction("Unregister Listener", ACTION_UNREGISTER, Card.ACTION_NEGATIVE) + .build(getActivity()); + getCardStream().addCard(c); + + // Register step detector listener + c = new Card.Builder(this, CARD_REGISTER_DETECTOR) + .setTitle(getString(R.string.register_detector_title)) + .setDescription(getString(R.string.register_detector_description)) + .addAction(getString(R.string.register_0), + ACTION_REGISTER_DETECT_NOBATCHING, Card.ACTION_NEUTRAL) + .addAction(getString(R.string.register_5), + ACTION_REGISTER_DETECT_BATCHING_5s, Card.ACTION_NEUTRAL) + .addAction(getString(R.string.register_10), + ACTION_REGISTER_DETECT_BATCHING_10s, Card.ACTION_NEUTRAL) + .build(getActivity()); + getCardStream().addCard(c); + + // Register step counter listener + c = new Card.Builder(this, CARD_REGISTER_COUNTER) + .setTitle(getString(R.string.register_counter_title)) + .setDescription(getString(R.string.register_counter_description)) + .addAction(getString(R.string.register_0), + ACTION_REGISTER_COUNT_NOBATCHING, Card.ACTION_NEUTRAL) + .addAction(getString(R.string.register_5), + ACTION_REGISTER_COUNT_BATCHING_5s, Card.ACTION_NEUTRAL) + .addAction(getString(R.string.register_10), + ACTION_REGISTER_COUNT_BATCHING_10s, Card.ACTION_NEUTRAL) + .build(getActivity()); + getCardStream().addCard(c); + + + // Batching description + c = new Card.Builder(this, CARD_BATCHING_DESCRIPTION) + .setTitle(getString(R.string.batching_queue_title)) + .setDescription(getString(R.string.batching_queue_description)) + .addAction(getString(R.string.action_notagain), + ACTION_BATCHING_DESCRIPTION_DISMISS, Card.ACTION_POSITIVE) + .build(getActivity()); + getCardStream().addCard(c); + + // Explanation + c = new Card.Builder(this, CARD_EXPLANATION) + .setDescription(getString(R.string.explanation_description)) + .addAction(getString(R.string.action_notagain), + ACTION_EXPLANATION_DISMISS, Card.ACTION_POSITIVE) + .build(getActivity()); + getCardStream().addCard(c); + + // Error + c = new Card.Builder(this, CARD_NOBATCHSUPPORT) + .setTitle(getString(R.string.error_title)) + .setDescription(getString(R.string.error_nosensor)) + .build(getActivity()); + getCardStream().addCard(c); + } + + /** + * Returns the cached CardStreamFragment used to show cards. + * + * @return + */ + private CardStreamFragment getCardStream() { + if (mCards == null) { + mCards = ((CardStream) getActivity()).getCardStream(); + } + return mCards; + } + +} diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-hdpi/ic_launcher.png b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..564742cc --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-hdpi/ic_launcher.png diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-mdpi/ic_launcher.png b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..08abe575 --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-mdpi/ic_launcher.png diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-xhdpi/ic_launcher.png b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..15bafae9 --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-xhdpi/ic_launcher.png diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-xxhdpi/ic_launcher.png b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..40bdd35d --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/drawable-xxhdpi/ic_launcher.png diff --git a/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/values/strings.xml b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/values/strings.xml new file mode 100644 index 00000000..9c0a8add --- /dev/null +++ b/sensors/BatchStepSensor/BatchStepSensorSample/src/main/res/values/strings.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string name="intro_title">Introduction</string> + + <string name="batching_queue_title">Background sensor batching</string> + <string name="batching_queue_description">Batching allows the sensor to report sensor events at + a specified frequency.\n\nThe system delays calls to the SensorEventListener and deliver + them in intervals, based on the maximum report latency specified when the listener is + registered. Note that this only means that the call to onSensorChanged() is delayed, the + total number of calls is identical as if no batching was used. Sensors only deliver events + while the CPU is awake. If the CPU is asleep and a batched sensor event listener is still + registered, the sensor will continue to collect events until it runs out of memory and + overwrites old values. This use case is not covered by this sample. (The sensor event queue + should be flushed using a scheduled background thread.) \n\nIn this sample app data is only + collected while the app is running and the CPU is awake. In this case the sensor will + deliver events before the queue fills up. + </string> + + <string name="explanation_description">The age of a sensor event describes the delay between + when it was recorded by the sensor until it was delivered to the SensorEventListener. + </string> + + <string name="register_detector_title">Register step detector sensor</string> + <string name="register_detector_description">Register a listener for the STEP DETECTOR + sensor.\n\nThis sensor delivers an event when the user takes a step. One event is received + per step. + </string> + + <string name="register_counter_title">Register step counter sensor</string> + <string name="register_counter_description">Register a listener for the STEP COUNTER + sensor.\n\nThis sensor triggers events when a step is detected, but applies algorithms to + filter out false positives. Events from this sensor have higher latency than the step + detector and contain the total number of steps taken since the sensor was first registered. + </string> + + <string name="register_0">No batching (delay=0)</string> + <string name="register_5">5s batching (delay=5000ms)</string> + <string name="register_10">10s batching (delay=10000ms)</string> + + <string name="counting_title">Total Steps: %1$d</string> + <string name="sensor_counter">Step Counter</string> + <string name="sensor_detector">Step Detector</string> + <string name="counting_description">Sensor: %1$s\nMax sensor event delay: %2$,d \u00B5s\nAge of + events in s:\n%3$s + </string> + + <string name="error_title">Error</string> + <string name="error_nosensor">This sample requires at least Android KitKat (4.4) and a device + with the step sensor.\n\nThis device does not appear to meet these requirements, as an + alternative you may want to consider using the gyro sensor and implement your own step + recognition as a fallback. + </string> + <string name="warning_nobatching">The listener has been registered, but batch mode could not be + enabled.\n\nIt is likely that it is not supported by this device.\n\nSensor events will be + delivered in continuous mode. + </string> + + <string name="action_notagain">Do not show again</string> +</resources>
\ No newline at end of file diff --git a/sensors/BatchStepSensor/build.gradle b/sensors/BatchStepSensor/build.gradle new file mode 100644 index 00000000..f9f6f652 --- /dev/null +++ b/sensors/BatchStepSensor/build.gradle @@ -0,0 +1,14 @@ + + + + +// BEGIN_EXCLUDE +import com.example.android.samples.build.SampleGenPlugin +apply plugin: SampleGenPlugin + +samplegen { + pathToBuild "../../../../build" + pathToSamplesCommon "../../common" +} +apply from: "../../../../build/build.gradle" +// END_EXCLUDE diff --git a/sensors/BatchStepSensor/buildSrc/build.gradle b/sensors/BatchStepSensor/buildSrc/build.gradle new file mode 100644 index 00000000..29282af4 --- /dev/null +++ b/sensors/BatchStepSensor/buildSrc/build.gradle @@ -0,0 +1,18 @@ + + + +repositories { + mavenCentral() +} +dependencies { + compile 'org.freemarker:freemarker:2.3.20' +} + +sourceSets { + main { + groovy { + srcDir new File(rootDir, "../../../../../build/buildSrc/src/main/groovy") + } + } +} + diff --git a/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.jar b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..8c0fb64a --- /dev/null +++ b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.jar diff --git a/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..56f685a4 --- /dev/null +++ b/sensors/BatchStepSensor/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip diff --git a/sensors/BatchStepSensor/gradlew b/sensors/BatchStepSensor/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/sensors/BatchStepSensor/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/sensors/BatchStepSensor/gradlew.bat b/sensors/BatchStepSensor/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/sensors/BatchStepSensor/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/sensors/BatchStepSensor/settings.gradle b/sensors/BatchStepSensor/settings.gradle new file mode 100644 index 00000000..18ebefc5 --- /dev/null +++ b/sensors/BatchStepSensor/settings.gradle @@ -0,0 +1,4 @@ + + + +include 'BatchStepSensorSample' diff --git a/sensors/BatchStepSensor/template-params.xml b/sensors/BatchStepSensor/template-params.xml new file mode 100644 index 00000000..a07579de --- /dev/null +++ b/sensors/BatchStepSensor/template-params.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + + + +<sample> + <name>BatchStepSensor</name> + <group>Sensors</group> + <package>com.example.android.batchstepsensor</package> + + + + <!-- change minSdk if needed--> + <minSdk>19</minSdk> + + + <strings> + <intro> + <![CDATA[ + This sample demonstrates the use of the two step sensors (step detector and counter) and + sensor batching.\n\n It shows how to register a SensorEventListener with and without + batching and shows how these events are received.\n\nThe Step Detector sensor fires an + event when a step is detected, while the step counter returns the total number of + steps since a listener was first registered for this sensor. + Both sensors only count steps while a listener is registered. This sample only covers the + basic case, where a listener is only registered while the app is running. Likewise, + batched sensors can be used in the background (when the CPU is suspended), which + requires manually flushing the sensor event queue before it overflows, which is not + covered in this sample. + ]]> + </intro> + </strings> + + <template src="base"/> + <template src="CardStream"/> + <common src="logger"/> + <common src="activities"/> + +</sample> |