加载中...
The Wayback Machine - https://sup1a9wrlpyh5li9ro.vcoronado.top/web/20140305055240/http://developer.android.com:80/samples/BatchStepSensor/src/com.example.android.batchstepsensor/BatchStepSensorFragment.html
to top
BatchStepSensor / src / com.example.android.batchstepsensor /

BatchStepSensorFragment.java

1
/*
2
* Copyright 2014 The Android Open Source Project
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
*     http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
 
17
package com.example.android.batchstepsensor;
18
 
19
import android.app.Activity;
20
import android.content.pm.PackageManager;
21
import android.hardware.Sensor;
22
import android.hardware.SensorEvent;
23
import android.hardware.SensorEventListener;
24
import android.hardware.SensorManager;
25
import android.os.Bundle;
26
import android.support.v4.app.Fragment;
27
 
28
import com.example.android.common.logger.Log;
29
 
30
public class BatchStepSensorFragment extends Fragment implements OnCardClickListener {
31
 
32
    public static final String TAG = "StepSensorSample";
33
    // Cards
34
    private CardStreamFragment mCards = null;
35
 
36
    // Card tags
37
    public static final String CARD_INTRO = "intro";
38
    public static final String CARD_REGISTER_DETECTOR = "register_detector";
39
    public static final String CARD_REGISTER_COUNTER = "register_counter";
40
    public static final String CARD_BATCHING_DESCRIPTION = "register_batching_description";
41
    public static final String CARD_COUNTING = "counting";
42
    public static final String CARD_EXPLANATION = "explanation";
43
    public static final String CARD_NOBATCHSUPPORT = "error";
44
 
45
    // Actions from REGISTER cards
46
    public static final int ACTION_REGISTER_DETECT_NOBATCHING = 10;
47
    public static final int ACTION_REGISTER_DETECT_BATCHING_5s = 11;
48
    public static final int ACTION_REGISTER_DETECT_BATCHING_10s = 12;
49
    public static final int ACTION_REGISTER_COUNT_NOBATCHING = 21;
50
    public static final int ACTION_REGISTER_COUNT_BATCHING_5s = 22;
51
    public static final int ACTION_REGISTER_COUNT_BATCHING_10s = 23;
52
    // Action from COUNTING card
53
    public static final int ACTION_UNREGISTER = 1;
54
    // Actions from description cards
55
    private static final int ACTION_BATCHING_DESCRIPTION_DISMISS = 2;
56
    private static final int ACTION_EXPLANATION_DISMISS = 3;
57
 
58
    // State of application, used to register for sensors when app is restored
59
    public static final int STATE_OTHER = 0;
60
    public static final int STATE_COUNTER = 1;
61
    public static final int STATE_DETECTOR = 2;
62
 
63
    // Bundle tags used to store data when restoring application state
64
    private static final String BUNDLE_STATE = "state";
65
    private static final String BUNDLE_LATENCY = "latency";
66
    private static final String BUNDLE_STEPS = "steps";
67
 
68
    // max batch latency is specified in microseconds
69
    private static final int BATCH_LATENCY_0 = 0; // no batching
70
    private static final int BATCH_LATENCY_10s = 10000000;
71
    private static final int BATCH_LATENCY_5s = 5000000;
72
 
73
    /*
74
    For illustration we keep track of the last few events and show their delay from when the
75
    event occurred until it was received by the event listener.
76
    These variables keep track of the list of timestamps and the number of events.
77
     */
78
    // Number of events to keep in queue and display on card
79
    private static final int EVENT_QUEUE_LENGTH = 10;
80
    // List of timestamps when sensor events occurred
81
    private float[] mEventDelays = new float[EVENT_QUEUE_LENGTH];
82
 
83
    // number of events in event list
84
    private int mEventLength = 0;
85
    // pointer to next entry in sensor event list
86
    private int mEventData = 0;
87
 
88
    // Steps counted in current session
89
    private int mSteps = 0;
90
    // Value of the step counter sensor when the listener was registered.
91
    // (Total steps are calculated from this value.)
92
    private int mCounterSteps = 0;
93
    // Steps counted by the step counter previously. Used to keep counter consistent across rotation
94
    // changes
95
    private int mPreviousCounterSteps = 0;
96
    // State of the app (STATE_OTHER, STATE_COUNTER or STATE_DETECTOR)
97
    private int mState = STATE_OTHER;
98
    // When a listener is registered, the batch sensor delay in microseconds
99
    private int mMaxDelay = 0;
100
 
101
    @Override
102
    public void onResume() {
103
        super.onResume();
104
 
105
        CardStreamFragment stream = getCardStream();
106
        if (stream.getVisibleCardCount() < 1) {
107
            // No cards are visible, started for the first time
108
            // Prepare all cards and show the intro card.
109
            initialiseCards();
110
            showIntroCard();
111
            // Show the registration card if the hardware is supported, show an error otherwise
112
            if (isKitkatWithStepSensor()) {
113
                showRegisterCard();
114
            } else {
115
                showErrorCard();
116
            }
117
        }
118
    }
119
 
120
    @Override
121
    public void onPause() {
122
        super.onPause();
124
        // Unregister the listener when the application is paused
125
        unregisterListeners();
127
    }
128
 
129
    /**
130
     * Returns true if this device is supported. It needs to be running Android KitKat (4.4) or
131
     * higher and has a step counter and step detector sensor.
132
     * This check is useful when an app provides an alternative implementation or different
133
     * functionality if the step sensors are not available or this code runs on a platform version
134
     * below Android KitKat. If this functionality is required, then the minSDK parameter should
135
     * be specified appropriately in the AndroidManifest.
136
     *
137
     * @return True iff the device can run this sample
138
     */
139
    private boolean isKitkatWithStepSensor() {
141
        // Require at least Android KitKat
142
        int currentApiVersion = android.os.Build.VERSION.SDK_INT;
143
        // Check that the device supports the step counter and detector sensors
144
        PackageManager packageManager = getActivity().getPackageManager();
145
        return currentApiVersion >= android.os.Build.VERSION_CODES.KITKAT
146
                && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER)
147
                && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
149
    }
150
 
151
    /**
152
     * Handles a click on a card action.
153
     * Registers a SensorEventListener (see {@link #registerEventListener(int, int)}) with the
154
     * selected delay, dismisses cards and unregisters the listener
155
     * (see {@link #unregisterListeners()}).
156
     * Actions are defined when a card is created.
157
     *
158
     * @param cardActionId
159
     * @param cardTag
160
     */
161
    @Override
162
    public void onCardClick(int cardActionId, String cardTag) {
163
 
164
        switch (cardActionId) {
166
            // Register Step Counter card
167
            case ACTION_REGISTER_COUNT_NOBATCHING:
168
                registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_COUNTER);
169
                break;
170
            case ACTION_REGISTER_COUNT_BATCHING_5s:
171
                registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_COUNTER);
172
                break;
173
            case ACTION_REGISTER_COUNT_BATCHING_10s:
174
                registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_COUNTER);
175
                break;
176
 
177
            // Register Step Detector card
178
            case ACTION_REGISTER_DETECT_NOBATCHING:
179
                registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_DETECTOR);
180
                break;
181
            case ACTION_REGISTER_DETECT_BATCHING_5s:
182
                registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_DETECTOR);
183
                break;
184
            case ACTION_REGISTER_DETECT_BATCHING_10s:
185
                registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_DETECTOR);
186
                break;
187
 
188
            // Unregister card
189
            case ACTION_UNREGISTER:
190
                showRegisterCard();
191
                unregisterListeners();
192
                // reset the application state when explicitly unregistered
193
                mState = STATE_OTHER;
194
                break;
196
            // Explanation cards
197
            case ACTION_BATCHING_DESCRIPTION_DISMISS:
198
                // permanently remove the batch description card, it will not be shown again
199
                getCardStream().removeCard(CARD_BATCHING_DESCRIPTION);
200
                break;
201
            case ACTION_EXPLANATION_DISMISS:
202
                // permanently remove the explanation card, it will not be shown again
203
                getCardStream().removeCard(CARD_EXPLANATION);
204
        }
205
 
206
        // For register cards, display the counting card
207
        if (cardTag.equals(CARD_REGISTER_COUNTER) || cardTag.equals(CARD_REGISTER_DETECTOR)) {
208
            showCountingCards();
209
        }
210
    }
211
 
212
    /**
213
     * Register a {@link android.hardware.SensorEventListener} for the sensor and max batch delay.
214
     * The maximum batch delay specifies the maximum duration in microseconds for which subsequent
215
     * sensor events can be temporarily stored by the sensor before they are delivered to the
216
     * registered SensorEventListener. A larger delay allows the system to handle sensor events more
217
     * efficiently, allowing the system to switch to a lower power state while the sensor is
218
     * capturing events. Once the max delay is reached, all stored events are delivered to the
219
     * registered listener. Note that this value only specifies the maximum delay, the listener may
220
     * receive events quicker. A delay of 0 disables batch mode and registers the listener in
221
     * continuous mode.
222
     * The optimium batch delay depends on the application. For example, a delay of 5 seconds or
223
     * higher may be appropriate for an  application that does not update the UI in real time.
224
     *
225
     * @param maxdelay
226
     * @param sensorType
227
     */
228
    private void registerEventListener(int maxdelay, int sensorType) {
230
 
231
        // Keep track of state so that the correct sensor type and batch delay can be set up when
232
        // the app is restored (for example on screen rotation).
233
        mMaxDelay = maxdelay;
234
        if (sensorType == Sensor.TYPE_STEP_COUNTER) {
235
            mState = STATE_COUNTER;
236
            /*
237
            Reset the initial step counter value, the first event received by the event listener is
238
            stored in mCounterSteps and used to calculate the total number of steps taken.
239
             */
240
            mCounterSteps = 0;
241
            Log.i(TAG, "Event listener for step counter sensor registered with a max delay of "
242
                    + mMaxDelay);
243
        } else {
244
            mState = STATE_DETECTOR;
245
            Log.i(TAG, "Event listener for step detector sensor registered with a max delay of "
246
                    + mMaxDelay);
247
        }
248
 
249
        // Get the default sensor for the sensor type from the SenorManager
250
        SensorManager sensorManager =
251
                (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE);
252
        // sensorType is either Sensor.TYPE_STEP_COUNTER or Sensor.TYPE_STEP_DETECTOR
253
        Sensor sensor = sensorManager.getDefaultSensor(sensorType);
254
 
255
        // Register the listener for this sensor in batch mode.
256
        // If the max delay is 0, events will be delivered in continuous mode without batching.
257
        final boolean batchMode = sensorManager.registerListener(
258
                mListener, sensor, SensorManager.SENSOR_DELAY_NORMAL, maxdelay);
259
 
260
        if (!batchMode) {
261
            // Batch mode could not be enabled, show a warning message and switch to continuous mode
262
            getCardStream().getCard(CARD_NOBATCHSUPPORT)
263
                    .setDescription(getString(R.string.warning_nobatching));
264
            getCardStream().showCard(CARD_NOBATCHSUPPORT);
265
            Log.w(TAG, "Could not register sensor listener in batch mode, " +
266
                    "falling back to continuous mode.");
267
        }
268
 
269
        if (maxdelay > 0 && batchMode) {
270
            // Batch mode was enabled successfully, show a description card
271
            getCardStream().showCard(CARD_BATCHING_DESCRIPTION);
272
        }
273
 
274
        // Show the explanation card
275
        getCardStream().showCard(CARD_EXPLANATION);
276
 
278
 
279
    }
280
 
281
    /**
282
     * Unregisters the sensor listener if it is registered.
283
     */
284
    private void unregisterListeners() {
286
        SensorManager sensorManager =
287
                (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE);
288
        sensorManager.unregisterListener(mListener);
289
        Log.i(TAG, "Sensor listener unregistered.");
290
 
292
    }
293
 
294
    /**
295
     * Resets the step counter by clearing all counting variables and lists.
296
     */
297
    private void resetCounter() {
299
        mSteps = 0;
300
        mCounterSteps = 0;
301
        mEventLength = 0;
302
        mEventDelays = new float[EVENT_QUEUE_LENGTH];
303
        mPreviousCounterSteps = 0;
305
    }
306
 
307
 
308
    /**
309
     * Listener that handles step sensor events for step detector and step counter sensors.
310
     */
311
    private final SensorEventListener mListener = new SensorEventListener() {
312
        @Override
313
        public void onSensorChanged(SensorEvent event) {
315
            // store the delay of this event
316
            recordDelay(event);
317
            final String delayString = getDelayString();
318
 
319
            if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) {
320
                // A step detector event is received for each step.
321
                // This means we need to count steps ourselves
322
 
323
                mSteps += event.values.length;
324
 
325
                // Update the card with the latest step count
326
                getCardStream().getCard(CARD_COUNTING)
327
                        .setTitle(getString(R.string.counting_title, mSteps))
328
                        .setDescription(getString(R.string.counting_description,
329
                                getString(R.string.sensor_detector), mMaxDelay, delayString));
330
 
331
                Log.i(TAG,
332
                        "New step detected by STEP_DETECTOR sensor. Total step count: " + mSteps);
333
 
334
            } else if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
335
 
336
                /*
337
                A step counter event contains the total number of steps since the listener
338
                was first registered. We need to keep track of this initial value to calculate the
339
                number of steps taken, as the first value a listener receives is undefined.
340
                 */
341
                if (mCounterSteps < 1) {
342
                    // initial value
343
                    mCounterSteps = (int) event.values[0];
344
                }
345
 
346
                // Calculate steps taken based on first counter value received.
347
                mSteps = (int) event.values[0] - mCounterSteps;
348
 
349
                // Add the number of steps previously taken, otherwise the counter would start at 0.
350
                // This is needed to keep the counter consistent across rotation changes.
351
                mSteps = mSteps + mPreviousCounterSteps;
352
 
353
                // Update the card with the latest step count
354
                getCardStream().getCard(CARD_COUNTING)
355
                        .setTitle(getString(R.string.counting_title, mSteps))
356
                        .setDescription(getString(R.string.counting_description,
357
                                getString(R.string.sensor_counter), mMaxDelay, delayString));
358
                Log.i(TAG, "New step detected by STEP_COUNTER sensor. Total step count: " + mSteps);
360
            }
361
        }
362
 
363
        @Override
364
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
365
 
366
        }
367
    };
368
 
369
    /**
370
     * Records the delay for the event.
371
     *
372
     * @param event
373
     */
374
    private void recordDelay(SensorEvent event) {
375
        // Calculate the delay from when event was recorded until it was received here in ms
376
        // Event timestamp is recorded in us accuracy, but ms accuracy is sufficient here
377
        mEventDelays[mEventData] = System.currentTimeMillis() - (event.timestamp / 1000000L);
378
 
379
        // Increment length counter
380
        mEventLength = Math.min(EVENT_QUEUE_LENGTH, mEventLength + 1);
381
        // Move pointer to the next (oldest) location
382
        mEventData = (mEventData + 1) % EVENT_QUEUE_LENGTH;
383
    }
384
 
385
    private final StringBuffer mDelayStringBuffer = new StringBuffer();
386
 
387
    /**
388
     * Returns a string describing the sensor delays recorded in
389
     * {@link #recordDelay(android.hardware.SensorEvent)}.
390
     *
391
     * @return
392
     */
393
    private String getDelayString() {
394
        // Empty the StringBuffer
395
        mDelayStringBuffer.setLength(0);
396
 
397
        // Loop over all recorded delays and append them to the buffer as a decimal
398
        for (int i = 0; i < mEventLength; i++) {
399
            if (i > 0) {
400
                mDelayStringBuffer.append(", ");
401
            }
402
            final int index = (mEventData + i) % EVENT_QUEUE_LENGTH;
403
            final float delay = mEventDelays[index] / 1000f; // convert delay from ms into s
404
            mDelayStringBuffer.append(String.format("%1.1f", delay));
405
        }
406
 
407
        return mDelayStringBuffer.toString();
408
    }
409
 
410
    /**
411
     * Records the state of the application into the {@link android.os.Bundle}.
412
     *
413
     * @param outState
414
     */
415
    @Override
416
    public void onSaveInstanceState(Bundle outState) {
418
        super.onSaveInstanceState(outState);
419
        // Store all variables required to restore the state of the application
420
        outState.putInt(BUNDLE_LATENCY, mMaxDelay);
421
        outState.putInt(BUNDLE_STATE, mState);
422
        outState.putInt(BUNDLE_STEPS, mSteps);
424
    }
425
 
426
    @Override
427
    public void onActivityCreated(Bundle savedInstanceState) {
428
        super.onActivityCreated(savedInstanceState);
430
        // Fragment is being restored, reinitialise its state with data from the bundle
431
        if (savedInstanceState != null) {
432
            resetCounter();
433
            mSteps = savedInstanceState.getInt(BUNDLE_STEPS);
434
            mState = savedInstanceState.getInt(BUNDLE_STATE);
435
            mMaxDelay = savedInstanceState.getInt(BUNDLE_LATENCY);
436
 
437
            // Register listeners again if in detector or counter states with restored delay
438
            if (mState == STATE_DETECTOR) {
439
                registerEventListener(mMaxDelay, Sensor.TYPE_STEP_DETECTOR);
440
            } else if (mState == STATE_COUNTER) {
441
                // store the previous number of steps to keep  step counter count consistent
442
                mPreviousCounterSteps = mSteps;
443
                registerEventListener(mMaxDelay, Sensor.TYPE_STEP_COUNTER);
444
            }
445
        }
447
    }
448
 
449
    /**
450
     * Hides the registration cards, reset the counter and show the step counting card.
451
     */
452
    private void showCountingCards() {
453
        // Hide the registration cards
454
        getCardStream().hideCard(CARD_REGISTER_DETECTOR);
455
        getCardStream().hideCard(CARD_REGISTER_COUNTER);
456
 
457
        // Show the explanation card if it has not been dismissed
458
        getCardStream().showCard(CARD_EXPLANATION);
459
 
460
        // Reset the step counter, then show the step counting card
461
        resetCounter();
462
 
463
        // Set the inital text for the step counting card before a step is recorded
464
        String sensor = "-";
465
        if (mState == STATE_COUNTER) {
466
            sensor = getString(R.string.sensor_counter);
467
        } else if (mState == STATE_DETECTOR) {
468
            sensor = getString(R.string.sensor_detector);
469
        }
470
        // Set initial text
471
        getCardStream().getCard(CARD_COUNTING)
472
                .setTitle(getString(R.string.counting_title, 0))
473
                .setDescription(getString(R.string.counting_description, sensor, mMaxDelay, "-"));
474
 
475
        // Show the counting card and make it undismissable
476
        getCardStream().showCard(CARD_COUNTING, false);
477
 
478
    }
479
 
480
    /**
481
     * Show the introduction card
482
     */
483
    private void showIntroCard() {
484
        Card c = new Card.Builder(this, CARD_INTRO)
485
                .setTitle(getString(R.string.intro_title))
486
                .setDescription(getString(R.string.intro_message))
487
                .build(getActivity());
488
        getCardStream().addCard(c, true);
489
    }
490
 
491
    /**
492
     * Show two registration cards, one for the step detector and counter sensors.
493
     */
494
    private void showRegisterCard() {
495
        // Hide the counting and explanation cards
496
        getCardStream().hideCard(CARD_BATCHING_DESCRIPTION);
497
        getCardStream().hideCard(CARD_EXPLANATION);
498
        getCardStream().hideCard(CARD_COUNTING);
499
 
500
        // Show two undismissable registration cards, one for each step sensor
501
        getCardStream().showCard(CARD_REGISTER_DETECTOR, false);
502
        getCardStream().showCard(CARD_REGISTER_COUNTER, false);
503
    }
504
 
505
    /**
506
     * Show the error card.
507
     */
508
    private void showErrorCard() {
509
        getCardStream().showCard(CARD_NOBATCHSUPPORT, false);
510
    }
511
 
512
    /**
513
     * Initialise Cards.
514
     */
515
    private void initialiseCards() {
516
        // Step counting
517
        Card c = new Card.Builder(this, CARD_COUNTING)
518
                .setTitle("Steps")
519
                .setDescription("")
520
                .addAction("Unregister Listener", ACTION_UNREGISTER, Card.ACTION_NEGATIVE)
521
                .build(getActivity());
522
        getCardStream().addCard(c);
523
 
524
        // Register step detector listener
525
        c = new Card.Builder(this, CARD_REGISTER_DETECTOR)
526
                .setTitle(getString(R.string.register_detector_title))
527
                .setDescription(getString(R.string.register_detector_description))
528
                .addAction(getString(R.string.register_0),
529
                        ACTION_REGISTER_DETECT_NOBATCHING, Card.ACTION_NEUTRAL)
530
                .addAction(getString(R.string.register_5),
531
                        ACTION_REGISTER_DETECT_BATCHING_5s, Card.ACTION_NEUTRAL)
532
                .addAction(getString(R.string.register_10),
533
                        ACTION_REGISTER_DETECT_BATCHING_10s, Card.ACTION_NEUTRAL)
534
                .build(getActivity());
535
        getCardStream().addCard(c);
536
 
537
        // Register step counter listener
538
        c = new Card.Builder(this, CARD_REGISTER_COUNTER)
539
                .setTitle(getString(R.string.register_counter_title))
540
                .setDescription(getString(R.string.register_counter_description))
541
                .addAction(getString(R.string.register_0),
542
                        ACTION_REGISTER_COUNT_NOBATCHING, Card.ACTION_NEUTRAL)
543
                .addAction(getString(R.string.register_5),
544
                        ACTION_REGISTER_COUNT_BATCHING_5s, Card.ACTION_NEUTRAL)
545
                .addAction(getString(R.string.register_10),
546
                        ACTION_REGISTER_COUNT_BATCHING_10s, Card.ACTION_NEUTRAL)
547
                .build(getActivity());
548
        getCardStream().addCard(c);
549
 
550
 
551
        // Batching description
552
        c = new Card.Builder(this, CARD_BATCHING_DESCRIPTION)
553
                .setTitle(getString(R.string.batching_queue_title))
554
                .setDescription(getString(R.string.batching_queue_description))
555
                .addAction(getString(R.string.action_notagain),
556
                        ACTION_BATCHING_DESCRIPTION_DISMISS, Card.ACTION_POSITIVE)
557
                .build(getActivity());
558
        getCardStream().addCard(c);
559
 
560
        // Explanation
561
        c = new Card.Builder(this, CARD_EXPLANATION)
562
                .setDescription(getString(R.string.explanation_description))
563
                .addAction(getString(R.string.action_notagain),
564
                        ACTION_EXPLANATION_DISMISS, Card.ACTION_POSITIVE)
565
                .build(getActivity());
566
        getCardStream().addCard(c);
567
 
568
        // Error
569
        c = new Card.Builder(this, CARD_NOBATCHSUPPORT)
570
                .setTitle(getString(R.string.error_title))
571
                .setDescription(getString(R.string.error_nosensor))
572
                .build(getActivity());
573
        getCardStream().addCard(c);
574
    }
575
 
576
    /**
577
     * Returns the cached CardStreamFragment used to show cards.
578
     *
579
     * @return
580
     */
581
    private CardStreamFragment getCardStream() {
582
        if (mCards == null) {
583
            mCards = ((CardStream) getActivity()).getCardStream();
584
        }
585
        return mCards;
586
    }
587
 
588
}