├── .google └── packaging.yaml ├── Application ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── android │ │ ├── batchstepsensor │ │ ├── BatchStepSensorFragment.java │ │ ├── MainActivity.java │ │ └── cardstream │ │ │ ├── Card.java │ │ │ ├── CardActionButton.java │ │ │ ├── CardLayout.java │ │ │ ├── CardStream.java │ │ │ ├── CardStreamAnimator.java │ │ │ ├── CardStreamFragment.java │ │ │ ├── CardStreamLinearLayout.java │ │ │ ├── CardStreamState.java │ │ │ ├── DefaultCardStreamAnimator.java │ │ │ ├── OnCardClickListener.java │ │ │ └── StreamRetentionFragment.java │ │ └── common │ │ ├── activities │ │ └── SampleActivityBase.java │ │ └── logger │ │ ├── Log.java │ │ ├── LogFragment.java │ │ ├── LogNode.java │ │ ├── LogView.java │ │ ├── LogWrapper.java │ │ └── MessageOnlyLogFilter.java │ └── res │ ├── drawable-hdpi │ ├── ic_action_cancel.png │ ├── ic_launcher.png │ └── tile.9.png │ ├── drawable-mdpi │ ├── ic_action_cancel.png │ └── ic_launcher.png │ ├── drawable-v21 │ ├── card_action_bg.xml │ ├── card_action_bg_negative.xml │ └── card_action_bg_positive.xml │ ├── drawable-xhdpi │ ├── card_bg.9.png │ ├── ic_cardaction_negative.png │ ├── ic_cardaction_negative_pressed.png │ ├── ic_cardaction_neutral.png │ ├── ic_cardaction_neutral_pressed.png │ ├── ic_cardaction_positive.png │ ├── ic_cardaction_positive_pressed.png │ └── ic_launcher.png │ ├── drawable-xxhdpi │ ├── ic_action_cancel.png │ └── ic_launcher.png │ ├── drawable │ ├── card_action_bg.xml │ ├── card_action_bg_negative.xml │ ├── card_action_bg_positive.xml │ ├── card_action_icon_negative.xml │ ├── card_action_icon_neutral.xml │ ├── card_action_icon_positive.xml │ ├── card_action_text.xml │ ├── card_action_text_negative.xml │ ├── card_action_text_positive.xml │ ├── card_overlay_focused.xml │ └── card_separator.xml │ ├── layout │ ├── activity_main.xml │ ├── card.xml │ ├── card_button_negative.xml │ ├── card_button_neutral.xml │ ├── card_button_positive.xml │ ├── card_button_seperator.xml │ ├── card_progress.xml │ └── cardstream.xml │ ├── values-sw600dp │ ├── template-dimens.xml │ └── template-styles.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-v11 │ ├── styles.xml │ └── template-styles.xml │ ├── values-v14 │ └── styles.xml │ ├── values-v16 │ └── styles.xml │ ├── values-v21 │ ├── base-colors.xml │ └── base-template-styles.xml │ └── values │ ├── attrs.xml │ ├── base-strings.xml │ ├── color.xml │ ├── dimens.xml │ ├── ids.xml │ ├── strings.xml │ ├── styles.xml │ ├── template-dimens.xml │ └── template-styles.xml ├── CONTRIB.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── packaging.yaml ├── screenshots ├── big_icon.png ├── screenshot1.png ├── screenshot2.png ├── screenshot3.png ├── screenshot4.png └── screenshot5.png └── settings.gradle /.google/packaging.yaml: -------------------------------------------------------------------------------- 1 | 2 | # GOOGLE SAMPLE PACKAGING DATA 3 | # 4 | # This file is used by Google as part of our samples packaging process. 5 | # End users may safely ignore this file. It has no relevance to other systems. 6 | --- 7 | status: PUBLISHED 8 | technologies: [Android] 9 | categories: [Sensors] 10 | languages: [Java] 11 | solutions: [Mobile] 12 | github: android-BatchStepSensor 13 | level: ADVANCED 14 | icon: screenshots/big_icon.png 15 | apiRefs: 16 | - android:android.hardware.Sensor 17 | - android:android.hardware.SensorEvent 18 | - android:android.hardware.SensorEventListener 19 | - android:android.hardware.SensorManager 20 | license: apache2 21 | -------------------------------------------------------------------------------- /Application/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | repositories { 4 | jcenter() 5 | google() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.0.1' 10 | } 11 | } 12 | 13 | apply plugin: 'com.android.application' 14 | 15 | repositories { 16 | jcenter() 17 | google() 18 | } 19 | 20 | dependencies { 21 | compile "com.android.support:support-v4:27.0.2" 22 | compile "com.android.support:support-v13:27.0.2" 23 | compile "com.android.support:cardview-v7:27.0.2" 24 | compile "com.android.support:appcompat-v7:27.0.2" 25 | } 26 | 27 | // The sample build uses multiple directories to 28 | // keep boilerplate and common code separate from 29 | // the main sample code. 30 | List dirs = [ 31 | 'main', // main sample code; look here for the interesting stuff. 32 | 'common', // components that are reused by multiple samples 33 | 'template'] // boilerplate code that is generated by the sample template process 34 | 35 | android { 36 | compileSdkVersion 27 37 | 38 | buildToolsVersion "27.0.2" 39 | 40 | defaultConfig { 41 | minSdkVersion 19 42 | targetSdkVersion 27 43 | } 44 | 45 | compileOptions { 46 | sourceCompatibility JavaVersion.VERSION_1_7 47 | targetCompatibility JavaVersion.VERSION_1_7 48 | } 49 | 50 | sourceSets { 51 | main { 52 | dirs.each { dir -> 53 | java.srcDirs "src/${dir}/java" 54 | res.srcDirs "src/${dir}/res" 55 | } 56 | } 57 | androidTest.setRoot('tests') 58 | androidTest.java.srcDirs = ['tests/src'] 59 | 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Application/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Application/src/main/java/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 | import com.example.android.batchstepsensor.cardstream.Card; 30 | import com.example.android.batchstepsensor.cardstream.CardStream; 31 | import com.example.android.batchstepsensor.cardstream.CardStreamFragment; 32 | import com.example.android.batchstepsensor.cardstream.OnCardClickListener; 33 | 34 | public class BatchStepSensorFragment extends Fragment implements OnCardClickListener { 35 | 36 | public static final String TAG = "StepSensorSample"; 37 | // Cards 38 | private CardStreamFragment mCards = null; 39 | 40 | // Card tags 41 | public static final String CARD_INTRO = "intro"; 42 | public static final String CARD_REGISTER_DETECTOR = "register_detector"; 43 | public static final String CARD_REGISTER_COUNTER = "register_counter"; 44 | public static final String CARD_BATCHING_DESCRIPTION = "register_batching_description"; 45 | public static final String CARD_COUNTING = "counting"; 46 | public static final String CARD_EXPLANATION = "explanation"; 47 | public static final String CARD_NOBATCHSUPPORT = "error"; 48 | 49 | // Actions from REGISTER cards 50 | public static final int ACTION_REGISTER_DETECT_NOBATCHING = 10; 51 | public static final int ACTION_REGISTER_DETECT_BATCHING_5s = 11; 52 | public static final int ACTION_REGISTER_DETECT_BATCHING_10s = 12; 53 | public static final int ACTION_REGISTER_COUNT_NOBATCHING = 21; 54 | public static final int ACTION_REGISTER_COUNT_BATCHING_5s = 22; 55 | public static final int ACTION_REGISTER_COUNT_BATCHING_10s = 23; 56 | // Action from COUNTING card 57 | public static final int ACTION_UNREGISTER = 1; 58 | // Actions from description cards 59 | private static final int ACTION_BATCHING_DESCRIPTION_DISMISS = 2; 60 | private static final int ACTION_EXPLANATION_DISMISS = 3; 61 | 62 | // State of application, used to register for sensors when app is restored 63 | public static final int STATE_OTHER = 0; 64 | public static final int STATE_COUNTER = 1; 65 | public static final int STATE_DETECTOR = 2; 66 | 67 | // Bundle tags used to store data when restoring application state 68 | private static final String BUNDLE_STATE = "state"; 69 | private static final String BUNDLE_LATENCY = "latency"; 70 | private static final String BUNDLE_STEPS = "steps"; 71 | 72 | // max batch latency is specified in microseconds 73 | private static final int BATCH_LATENCY_0 = 0; // no batching 74 | private static final int BATCH_LATENCY_10s = 10000000; 75 | private static final int BATCH_LATENCY_5s = 5000000; 76 | 77 | /* 78 | For illustration we keep track of the last few events and show their delay from when the 79 | event occurred until it was received by the event listener. 80 | These variables keep track of the list of timestamps and the number of events. 81 | */ 82 | // Number of events to keep in queue and display on card 83 | private static final int EVENT_QUEUE_LENGTH = 10; 84 | // List of timestamps when sensor events occurred 85 | private float[] mEventDelays = new float[EVENT_QUEUE_LENGTH]; 86 | 87 | // number of events in event list 88 | private int mEventLength = 0; 89 | // pointer to next entry in sensor event list 90 | private int mEventData = 0; 91 | 92 | // Steps counted in current session 93 | private int mSteps = 0; 94 | // Value of the step counter sensor when the listener was registered. 95 | // (Total steps are calculated from this value.) 96 | private int mCounterSteps = 0; 97 | // Steps counted by the step counter previously. Used to keep counter consistent across rotation 98 | // changes 99 | private int mPreviousCounterSteps = 0; 100 | // State of the app (STATE_OTHER, STATE_COUNTER or STATE_DETECTOR) 101 | private int mState = STATE_OTHER; 102 | // When a listener is registered, the batch sensor delay in microseconds 103 | private int mMaxDelay = 0; 104 | 105 | @Override 106 | public void onResume() { 107 | super.onResume(); 108 | 109 | CardStreamFragment stream = getCardStream(); 110 | if (stream.getVisibleCardCount() < 1) { 111 | // No cards are visible, started for the first time 112 | // Prepare all cards and show the intro card. 113 | initialiseCards(); 114 | showIntroCard(); 115 | // Show the registration card if the hardware is supported, show an error otherwise 116 | if (isKitkatWithStepSensor()) { 117 | showRegisterCard(); 118 | } else { 119 | showErrorCard(); 120 | } 121 | } 122 | } 123 | 124 | @Override 125 | public void onPause() { 126 | super.onPause(); 127 | // BEGIN_INCLUDE(onpause) 128 | // Unregister the listener when the application is paused 129 | unregisterListeners(); 130 | // END_INCLUDE(onpause) 131 | } 132 | 133 | /** 134 | * Returns true if this device is supported. It needs to be running Android KitKat (4.4) or 135 | * higher and has a step counter and step detector sensor. 136 | * This check is useful when an app provides an alternative implementation or different 137 | * functionality if the step sensors are not available or this code runs on a platform version 138 | * below Android KitKat. If this functionality is required, then the minSDK parameter should 139 | * be specified appropriately in the AndroidManifest. 140 | * 141 | * @return True iff the device can run this sample 142 | */ 143 | private boolean isKitkatWithStepSensor() { 144 | // BEGIN_INCLUDE(iskitkatsensor) 145 | // Require at least Android KitKat 146 | int currentApiVersion = android.os.Build.VERSION.SDK_INT; 147 | // Check that the device supports the step counter and detector sensors 148 | PackageManager packageManager = getActivity().getPackageManager(); 149 | return currentApiVersion >= android.os.Build.VERSION_CODES.KITKAT 150 | && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER) 151 | && packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR); 152 | // END_INCLUDE(iskitkatsensor) 153 | } 154 | 155 | /** 156 | * Handles a click on a card action. 157 | * Registers a SensorEventListener (see {@link #registerEventListener(int, int)}) with the 158 | * selected delay, dismisses cards and unregisters the listener 159 | * (see {@link #unregisterListeners()}). 160 | * Actions are defined when a card is created. 161 | * 162 | * @param cardActionId 163 | * @param cardTag 164 | */ 165 | @Override 166 | public void onCardClick(int cardActionId, String cardTag) { 167 | 168 | switch (cardActionId) { 169 | // BEGIN_INCLUDE(onclick) 170 | // Register Step Counter card 171 | case ACTION_REGISTER_COUNT_NOBATCHING: 172 | registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_COUNTER); 173 | break; 174 | case ACTION_REGISTER_COUNT_BATCHING_5s: 175 | registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_COUNTER); 176 | break; 177 | case ACTION_REGISTER_COUNT_BATCHING_10s: 178 | registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_COUNTER); 179 | break; 180 | 181 | // Register Step Detector card 182 | case ACTION_REGISTER_DETECT_NOBATCHING: 183 | registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_DETECTOR); 184 | break; 185 | case ACTION_REGISTER_DETECT_BATCHING_5s: 186 | registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_DETECTOR); 187 | break; 188 | case ACTION_REGISTER_DETECT_BATCHING_10s: 189 | registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_DETECTOR); 190 | break; 191 | 192 | // Unregister card 193 | case ACTION_UNREGISTER: 194 | showRegisterCard(); 195 | unregisterListeners(); 196 | // reset the application state when explicitly unregistered 197 | mState = STATE_OTHER; 198 | break; 199 | // END_INCLUDE(onclick) 200 | // Explanation cards 201 | case ACTION_BATCHING_DESCRIPTION_DISMISS: 202 | // permanently remove the batch description card, it will not be shown again 203 | getCardStream().removeCard(CARD_BATCHING_DESCRIPTION); 204 | break; 205 | case ACTION_EXPLANATION_DISMISS: 206 | // permanently remove the explanation card, it will not be shown again 207 | getCardStream().removeCard(CARD_EXPLANATION); 208 | } 209 | 210 | // For register cards, display the counting card 211 | if (cardTag.equals(CARD_REGISTER_COUNTER) || cardTag.equals(CARD_REGISTER_DETECTOR)) { 212 | showCountingCards(); 213 | } 214 | } 215 | 216 | /** 217 | * Register a {@link android.hardware.SensorEventListener} for the sensor and max batch delay. 218 | * The maximum batch delay specifies the maximum duration in microseconds for which subsequent 219 | * sensor events can be temporarily stored by the sensor before they are delivered to the 220 | * registered SensorEventListener. A larger delay allows the system to handle sensor events more 221 | * efficiently, allowing the system to switch to a lower power state while the sensor is 222 | * capturing events. Once the max delay is reached, all stored events are delivered to the 223 | * registered listener. Note that this value only specifies the maximum delay, the listener may 224 | * receive events quicker. A delay of 0 disables batch mode and registers the listener in 225 | * continuous mode. 226 | * The optimium batch delay depends on the application. For example, a delay of 5 seconds or 227 | * higher may be appropriate for an application that does not update the UI in real time. 228 | * 229 | * @param maxdelay 230 | * @param sensorType 231 | */ 232 | private void registerEventListener(int maxdelay, int sensorType) { 233 | // BEGIN_INCLUDE(register) 234 | 235 | // Keep track of state so that the correct sensor type and batch delay can be set up when 236 | // the app is restored (for example on screen rotation). 237 | mMaxDelay = maxdelay; 238 | if (sensorType == Sensor.TYPE_STEP_COUNTER) { 239 | mState = STATE_COUNTER; 240 | /* 241 | Reset the initial step counter value, the first event received by the event listener is 242 | stored in mCounterSteps and used to calculate the total number of steps taken. 243 | */ 244 | mCounterSteps = 0; 245 | Log.i(TAG, "Event listener for step counter sensor registered with a max delay of " 246 | + mMaxDelay); 247 | } else { 248 | mState = STATE_DETECTOR; 249 | Log.i(TAG, "Event listener for step detector sensor registered with a max delay of " 250 | + mMaxDelay); 251 | } 252 | 253 | // Get the default sensor for the sensor type from the SenorManager 254 | SensorManager sensorManager = 255 | (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE); 256 | // sensorType is either Sensor.TYPE_STEP_COUNTER or Sensor.TYPE_STEP_DETECTOR 257 | Sensor sensor = sensorManager.getDefaultSensor(sensorType); 258 | 259 | // Register the listener for this sensor in batch mode. 260 | // If the max delay is 0, events will be delivered in continuous mode without batching. 261 | final boolean batchMode = sensorManager.registerListener( 262 | mListener, sensor, SensorManager.SENSOR_DELAY_NORMAL, maxdelay); 263 | 264 | if (!batchMode) { 265 | // Batch mode could not be enabled, show a warning message and switch to continuous mode 266 | getCardStream().getCard(CARD_NOBATCHSUPPORT) 267 | .setDescription(getString(R.string.warning_nobatching)); 268 | getCardStream().showCard(CARD_NOBATCHSUPPORT); 269 | Log.w(TAG, "Could not register sensor listener in batch mode, " + 270 | "falling back to continuous mode."); 271 | } 272 | 273 | if (maxdelay > 0 && batchMode) { 274 | // Batch mode was enabled successfully, show a description card 275 | getCardStream().showCard(CARD_BATCHING_DESCRIPTION); 276 | } 277 | 278 | // Show the explanation card 279 | getCardStream().showCard(CARD_EXPLANATION); 280 | 281 | // END_INCLUDE(register) 282 | 283 | } 284 | 285 | /** 286 | * Unregisters the sensor listener if it is registered. 287 | */ 288 | private void unregisterListeners() { 289 | // BEGIN_INCLUDE(unregister) 290 | SensorManager sensorManager = 291 | (SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE); 292 | sensorManager.unregisterListener(mListener); 293 | Log.i(TAG, "Sensor listener unregistered."); 294 | 295 | // END_INCLUDE(unregister) 296 | } 297 | 298 | /** 299 | * Resets the step counter by clearing all counting variables and lists. 300 | */ 301 | private void resetCounter() { 302 | // BEGIN_INCLUDE(reset) 303 | mSteps = 0; 304 | mCounterSteps = 0; 305 | mEventLength = 0; 306 | mEventDelays = new float[EVENT_QUEUE_LENGTH]; 307 | mPreviousCounterSteps = 0; 308 | // END_INCLUDE(reset) 309 | } 310 | 311 | 312 | /** 313 | * Listener that handles step sensor events for step detector and step counter sensors. 314 | */ 315 | private final SensorEventListener mListener = new SensorEventListener() { 316 | @Override 317 | public void onSensorChanged(SensorEvent event) { 318 | // BEGIN_INCLUDE(sensorevent) 319 | // store the delay of this event 320 | recordDelay(event); 321 | final String delayString = getDelayString(); 322 | 323 | if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) { 324 | // A step detector event is received for each step. 325 | // This means we need to count steps ourselves 326 | 327 | mSteps += event.values.length; 328 | 329 | // Update the card with the latest step count 330 | getCardStream().getCard(CARD_COUNTING) 331 | .setTitle(getString(R.string.counting_title, mSteps)) 332 | .setDescription(getString(R.string.counting_description, 333 | getString(R.string.sensor_detector), mMaxDelay, delayString)); 334 | 335 | Log.i(TAG, 336 | "New step detected by STEP_DETECTOR sensor. Total step count: " + mSteps); 337 | 338 | } else if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) { 339 | 340 | /* 341 | A step counter event contains the total number of steps since the listener 342 | was first registered. We need to keep track of this initial value to calculate the 343 | number of steps taken, as the first value a listener receives is undefined. 344 | */ 345 | if (mCounterSteps < 1) { 346 | // initial value 347 | mCounterSteps = (int) event.values[0]; 348 | } 349 | 350 | // Calculate steps taken based on first counter value received. 351 | mSteps = (int) event.values[0] - mCounterSteps; 352 | 353 | // Add the number of steps previously taken, otherwise the counter would start at 0. 354 | // This is needed to keep the counter consistent across rotation changes. 355 | mSteps = mSteps + mPreviousCounterSteps; 356 | 357 | // Update the card with the latest step count 358 | getCardStream().getCard(CARD_COUNTING) 359 | .setTitle(getString(R.string.counting_title, mSteps)) 360 | .setDescription(getString(R.string.counting_description, 361 | getString(R.string.sensor_counter), mMaxDelay, delayString)); 362 | Log.i(TAG, "New step detected by STEP_COUNTER sensor. Total step count: " + mSteps); 363 | // END_INCLUDE(sensorevent) 364 | } 365 | } 366 | 367 | @Override 368 | public void onAccuracyChanged(Sensor sensor, int accuracy) { 369 | 370 | } 371 | }; 372 | 373 | /** 374 | * Records the delay for the event. 375 | * 376 | * @param event 377 | */ 378 | private void recordDelay(SensorEvent event) { 379 | // Calculate the delay from when event was recorded until it was received here in ms 380 | // Event timestamp is recorded in us accuracy, but ms accuracy is sufficient here 381 | mEventDelays[mEventData] = System.currentTimeMillis() - (event.timestamp / 1000000L); 382 | 383 | // Increment length counter 384 | mEventLength = Math.min(EVENT_QUEUE_LENGTH, mEventLength + 1); 385 | // Move pointer to the next (oldest) location 386 | mEventData = (mEventData + 1) % EVENT_QUEUE_LENGTH; 387 | } 388 | 389 | private final StringBuffer mDelayStringBuffer = new StringBuffer(); 390 | 391 | /** 392 | * Returns a string describing the sensor delays recorded in 393 | * {@link #recordDelay(android.hardware.SensorEvent)}. 394 | * 395 | * @return 396 | */ 397 | private String getDelayString() { 398 | // Empty the StringBuffer 399 | mDelayStringBuffer.setLength(0); 400 | 401 | // Loop over all recorded delays and append them to the buffer as a decimal 402 | for (int i = 0; i < mEventLength; i++) { 403 | if (i > 0) { 404 | mDelayStringBuffer.append(", "); 405 | } 406 | final int index = (mEventData + i) % EVENT_QUEUE_LENGTH; 407 | final float delay = mEventDelays[index] / 1000f; // convert delay from ms into s 408 | mDelayStringBuffer.append(String.format("%1.1f", delay)); 409 | } 410 | 411 | return mDelayStringBuffer.toString(); 412 | } 413 | 414 | /** 415 | * Records the state of the application into the {@link android.os.Bundle}. 416 | * 417 | * @param outState 418 | */ 419 | @Override 420 | public void onSaveInstanceState(Bundle outState) { 421 | // BEGIN_INCLUDE(saveinstance) 422 | super.onSaveInstanceState(outState); 423 | // Store all variables required to restore the state of the application 424 | outState.putInt(BUNDLE_LATENCY, mMaxDelay); 425 | outState.putInt(BUNDLE_STATE, mState); 426 | outState.putInt(BUNDLE_STEPS, mSteps); 427 | // END_INCLUDE(saveinstance) 428 | } 429 | 430 | @Override 431 | public void onActivityCreated(Bundle savedInstanceState) { 432 | super.onActivityCreated(savedInstanceState); 433 | // BEGIN_INCLUDE(restore) 434 | // Fragment is being restored, reinitialise its state with data from the bundle 435 | if (savedInstanceState != null) { 436 | resetCounter(); 437 | mSteps = savedInstanceState.getInt(BUNDLE_STEPS); 438 | mState = savedInstanceState.getInt(BUNDLE_STATE); 439 | mMaxDelay = savedInstanceState.getInt(BUNDLE_LATENCY); 440 | 441 | // Register listeners again if in detector or counter states with restored delay 442 | if (mState == STATE_DETECTOR) { 443 | registerEventListener(mMaxDelay, Sensor.TYPE_STEP_DETECTOR); 444 | } else if (mState == STATE_COUNTER) { 445 | // store the previous number of steps to keep step counter count consistent 446 | mPreviousCounterSteps = mSteps; 447 | registerEventListener(mMaxDelay, Sensor.TYPE_STEP_COUNTER); 448 | } 449 | } 450 | // END_INCLUDE(restore) 451 | } 452 | 453 | /** 454 | * Hides the registration cards, reset the counter and show the step counting card. 455 | */ 456 | private void showCountingCards() { 457 | // Hide the registration cards 458 | getCardStream().hideCard(CARD_REGISTER_DETECTOR); 459 | getCardStream().hideCard(CARD_REGISTER_COUNTER); 460 | 461 | // Show the explanation card if it has not been dismissed 462 | getCardStream().showCard(CARD_EXPLANATION); 463 | 464 | // Reset the step counter, then show the step counting card 465 | resetCounter(); 466 | 467 | // Set the inital text for the step counting card before a step is recorded 468 | String sensor = "-"; 469 | if (mState == STATE_COUNTER) { 470 | sensor = getString(R.string.sensor_counter); 471 | } else if (mState == STATE_DETECTOR) { 472 | sensor = getString(R.string.sensor_detector); 473 | } 474 | // Set initial text 475 | getCardStream().getCard(CARD_COUNTING) 476 | .setTitle(getString(R.string.counting_title, 0)) 477 | .setDescription(getString(R.string.counting_description, sensor, mMaxDelay, "-")); 478 | 479 | // Show the counting card and make it undismissable 480 | getCardStream().showCard(CARD_COUNTING, false); 481 | 482 | } 483 | 484 | /** 485 | * Show the introduction card 486 | */ 487 | private void showIntroCard() { 488 | Card c = new Card.Builder(this, CARD_INTRO) 489 | .setTitle(getString(R.string.intro_title)) 490 | .setDescription(getString(R.string.intro_message)) 491 | .build(getActivity()); 492 | getCardStream().addCard(c, true); 493 | } 494 | 495 | /** 496 | * Show two registration cards, one for the step detector and counter sensors. 497 | */ 498 | private void showRegisterCard() { 499 | // Hide the counting and explanation cards 500 | getCardStream().hideCard(CARD_BATCHING_DESCRIPTION); 501 | getCardStream().hideCard(CARD_EXPLANATION); 502 | getCardStream().hideCard(CARD_COUNTING); 503 | 504 | // Show two undismissable registration cards, one for each step sensor 505 | getCardStream().showCard(CARD_REGISTER_DETECTOR, false); 506 | getCardStream().showCard(CARD_REGISTER_COUNTER, false); 507 | } 508 | 509 | /** 510 | * Show the error card. 511 | */ 512 | private void showErrorCard() { 513 | getCardStream().showCard(CARD_NOBATCHSUPPORT, false); 514 | } 515 | 516 | /** 517 | * Initialise Cards. 518 | */ 519 | private void initialiseCards() { 520 | // Step counting 521 | Card c = new Card.Builder(this, CARD_COUNTING) 522 | .setTitle("Steps") 523 | .setDescription("") 524 | .addAction("Unregister Listener", ACTION_UNREGISTER, Card.ACTION_NEGATIVE) 525 | .build(getActivity()); 526 | getCardStream().addCard(c); 527 | 528 | // Register step detector listener 529 | c = new Card.Builder(this, CARD_REGISTER_DETECTOR) 530 | .setTitle(getString(R.string.register_detector_title)) 531 | .setDescription(getString(R.string.register_detector_description)) 532 | .addAction(getString(R.string.register_0), 533 | ACTION_REGISTER_DETECT_NOBATCHING, Card.ACTION_NEUTRAL) 534 | .addAction(getString(R.string.register_5), 535 | ACTION_REGISTER_DETECT_BATCHING_5s, Card.ACTION_NEUTRAL) 536 | .addAction(getString(R.string.register_10), 537 | ACTION_REGISTER_DETECT_BATCHING_10s, Card.ACTION_NEUTRAL) 538 | .build(getActivity()); 539 | getCardStream().addCard(c); 540 | 541 | // Register step counter listener 542 | c = new Card.Builder(this, CARD_REGISTER_COUNTER) 543 | .setTitle(getString(R.string.register_counter_title)) 544 | .setDescription(getString(R.string.register_counter_description)) 545 | .addAction(getString(R.string.register_0), 546 | ACTION_REGISTER_COUNT_NOBATCHING, Card.ACTION_NEUTRAL) 547 | .addAction(getString(R.string.register_5), 548 | ACTION_REGISTER_COUNT_BATCHING_5s, Card.ACTION_NEUTRAL) 549 | .addAction(getString(R.string.register_10), 550 | ACTION_REGISTER_COUNT_BATCHING_10s, Card.ACTION_NEUTRAL) 551 | .build(getActivity()); 552 | getCardStream().addCard(c); 553 | 554 | 555 | // Batching description 556 | c = new Card.Builder(this, CARD_BATCHING_DESCRIPTION) 557 | .setTitle(getString(R.string.batching_queue_title)) 558 | .setDescription(getString(R.string.batching_queue_description)) 559 | .addAction(getString(R.string.action_notagain), 560 | ACTION_BATCHING_DESCRIPTION_DISMISS, Card.ACTION_POSITIVE) 561 | .build(getActivity()); 562 | getCardStream().addCard(c); 563 | 564 | // Explanation 565 | c = new Card.Builder(this, CARD_EXPLANATION) 566 | .setDescription(getString(R.string.explanation_description)) 567 | .addAction(getString(R.string.action_notagain), 568 | ACTION_EXPLANATION_DISMISS, Card.ACTION_POSITIVE) 569 | .build(getActivity()); 570 | getCardStream().addCard(c); 571 | 572 | // Error 573 | c = new Card.Builder(this, CARD_NOBATCHSUPPORT) 574 | .setTitle(getString(R.string.error_title)) 575 | .setDescription(getString(R.string.error_nosensor)) 576 | .build(getActivity()); 577 | getCardStream().addCard(c); 578 | } 579 | 580 | /** 581 | * Returns the cached CardStreamFragment used to show cards. 582 | * 583 | * @return 584 | */ 585 | private CardStreamFragment getCardStream() { 586 | if (mCards == null) { 587 | mCards = ((CardStream) getActivity()).getCardStream(); 588 | } 589 | return mCards; 590 | } 591 | 592 | } 593 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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.os.Bundle; 20 | import android.support.v4.app.FragmentManager; 21 | import android.support.v4.app.FragmentTransaction; 22 | import android.view.Menu; 23 | 24 | import com.example.android.common.activities.SampleActivityBase; 25 | import com.example.android.common.logger.Log; 26 | 27 | import com.example.android.batchstepsensor.cardstream.CardStream; 28 | import com.example.android.batchstepsensor.cardstream.CardStreamFragment; 29 | import com.example.android.batchstepsensor.cardstream.CardStreamState; 30 | import com.example.android.batchstepsensor.cardstream.OnCardClickListener; 31 | import com.example.android.batchstepsensor.cardstream.StreamRetentionFragment; 32 | 33 | public class MainActivity extends SampleActivityBase implements CardStream { 34 | public static final String TAG = "MainActivity"; 35 | public static final String FRAGTAG = "BatchStepSensorFragment"; 36 | 37 | private CardStreamFragment mCardStreamFragment; 38 | 39 | private StreamRetentionFragment mRetentionFragment; 40 | private static final String RETENTION_TAG = "retention"; 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | setContentView(R.layout.activity_main); 46 | 47 | FragmentManager fm = getSupportFragmentManager(); 48 | BatchStepSensorFragment fragment = 49 | (BatchStepSensorFragment) fm.findFragmentByTag(FRAGTAG); 50 | 51 | if (fragment == null) { 52 | FragmentTransaction transaction = fm.beginTransaction(); 53 | fragment = new BatchStepSensorFragment(); 54 | transaction.add(fragment, FRAGTAG); 55 | transaction.commit(); 56 | } 57 | 58 | // Use fragment as click listener for cards, but must implement correct interface 59 | if (!(fragment instanceof OnCardClickListener)){ 60 | throw new ClassCastException("BatchStepSensorFragment must " + 61 | "implement OnCardClickListener interface."); 62 | } 63 | OnCardClickListener clickListener = (OnCardClickListener) fm.findFragmentByTag(FRAGTAG); 64 | 65 | mRetentionFragment = (StreamRetentionFragment) fm.findFragmentByTag(RETENTION_TAG); 66 | if (mRetentionFragment == null) { 67 | mRetentionFragment = new StreamRetentionFragment(); 68 | fm.beginTransaction().add(mRetentionFragment, RETENTION_TAG).commit(); 69 | } else { 70 | // If the retention fragment already existed, we need to pull some state. 71 | // pull state out 72 | CardStreamState state = mRetentionFragment.getCardStream(); 73 | 74 | // dump it in CardStreamFragment. 75 | mCardStreamFragment = 76 | (CardStreamFragment) fm.findFragmentById(R.id.fragment_cardstream); 77 | mCardStreamFragment.restoreState(state, clickListener); 78 | } 79 | } 80 | 81 | public CardStreamFragment getCardStream() { 82 | if (mCardStreamFragment == null) { 83 | mCardStreamFragment = (CardStreamFragment) 84 | getSupportFragmentManager().findFragmentById(R.id.fragment_cardstream); 85 | } 86 | return mCardStreamFragment; 87 | } 88 | 89 | @Override 90 | protected void onSaveInstanceState(Bundle outState) { 91 | super.onSaveInstanceState(outState); 92 | CardStreamState state = getCardStream().dumpState(); 93 | mRetentionFragment.storeCardStream(state); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/Card.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.animation.Animator; 21 | import android.animation.AnimatorListenerAdapter; 22 | import android.animation.ObjectAnimator; 23 | import android.app.Activity; 24 | import android.graphics.Color; 25 | import android.view.LayoutInflater; 26 | import android.view.View; 27 | import android.view.ViewGroup; 28 | import android.widget.Button; 29 | import android.widget.ProgressBar; 30 | import android.widget.TextView; 31 | 32 | import com.example.android.batchstepsensor.R; 33 | 34 | import java.util.ArrayList; 35 | 36 | /** 37 | * A Card contains a description and has a visual state. Optionally a card also contains a title, 38 | * progress indicator and zero or more actions. It is constructed through the {@link Builder}. 39 | */ 40 | public class Card { 41 | 42 | public static final int ACTION_POSITIVE = 1; 43 | public static final int ACTION_NEGATIVE = 2; 44 | public static final int ACTION_NEUTRAL = 3; 45 | 46 | public static final int PROGRESS_TYPE_NO_PROGRESS = 0; 47 | public static final int PROGRESS_TYPE_NORMAL = 1; 48 | public static final int PROGRESS_TYPE_INDETERMINATE = 2; 49 | public static final int PROGRESS_TYPE_LABEL = 3; 50 | 51 | private OnCardClickListener mClickListener; 52 | 53 | 54 | // The card model contains a reference to its desired layout (for extensibility), title, 55 | // description, zero to many action buttons, and zero or 1 progress indicators. 56 | private int mLayoutId = R.layout.card; 57 | 58 | /** 59 | * Tag that uniquely identifies this card. 60 | */ 61 | private String mTag = null; 62 | 63 | private String mTitle = null; 64 | private String mDescription = null; 65 | 66 | private View mCardView = null; 67 | private View mOverlayView = null; 68 | private TextView mTitleView = null; 69 | private TextView mDescView = null; 70 | private View mActionAreaView = null; 71 | 72 | private Animator mOngoingAnimator = null; 73 | 74 | /** 75 | * Visual state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or 76 | * {@link #CARD_STATE_INACTIVE}. 77 | */ 78 | private int mCardState = CARD_STATE_NORMAL; 79 | public static final int CARD_STATE_NORMAL = 1; 80 | public static final int CARD_STATE_FOCUSED = 2; 81 | public static final int CARD_STATE_INACTIVE = 3; 82 | 83 | /** 84 | * Represent actions that can be taken from the card. Stylistically the developer can 85 | * designate the action as positive, negative (ok/cancel, for instance), or neutral. 86 | * This "type" can be used as a UI hint. 87 | * @see com.example.android.sensors.batchstepsensor.Card.CardAction 88 | */ 89 | private ArrayList mCardActions = new ArrayList(); 90 | 91 | /** 92 | * Some cards will have a sense of "progress" which should be associated with, but separated 93 | * from its "parent" card. To push for simplicity in samples, Cards are designed to have 94 | * a maximum of one progress indicator per Card. 95 | */ 96 | private CardProgress mCardProgress = null; 97 | 98 | public Card() { 99 | } 100 | 101 | public String getTag() { 102 | return mTag; 103 | } 104 | 105 | public View getView() { 106 | return mCardView; 107 | } 108 | 109 | 110 | public Card setDescription(String desc) { 111 | if (mDescView != null) { 112 | mDescription = desc; 113 | mDescView.setText(desc); 114 | } 115 | return this; 116 | } 117 | 118 | public Card setTitle(String title) { 119 | if (mTitleView != null) { 120 | mTitle = title; 121 | mTitleView.setText(title); 122 | } 123 | return this; 124 | } 125 | 126 | 127 | /** 128 | * Return the UI state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} 129 | * or {@link #CARD_STATE_INACTIVE}. 130 | */ 131 | public int getState() { 132 | return mCardState; 133 | } 134 | 135 | /** 136 | * Set the UI state. The parameter describes the state and must be either 137 | * {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or {@link #CARD_STATE_INACTIVE}. 138 | * Note: This method must be called from the UI Thread. 139 | * @param state 140 | * @return The card itself, allows for chaining of calls 141 | */ 142 | public Card setState(int state) { 143 | mCardState = state; 144 | if (null != mOverlayView) { 145 | if (null != mOngoingAnimator) { 146 | mOngoingAnimator.end(); 147 | mOngoingAnimator = null; 148 | } 149 | switch (state) { 150 | case CARD_STATE_NORMAL: { 151 | mOverlayView.setVisibility(View.GONE); 152 | mOverlayView.setAlpha(1.f); 153 | break; 154 | } 155 | case CARD_STATE_FOCUSED: { 156 | mOverlayView.setVisibility(View.VISIBLE); 157 | mOverlayView.setBackgroundResource(R.drawable.card_overlay_focused); 158 | ObjectAnimator animator = ObjectAnimator.ofFloat(mOverlayView, "alpha", 0.f); 159 | animator.setRepeatMode(ObjectAnimator.REVERSE); 160 | animator.setRepeatCount(ObjectAnimator.INFINITE); 161 | animator.setDuration(1000); 162 | animator.start(); 163 | mOngoingAnimator = animator; 164 | break; 165 | } 166 | case CARD_STATE_INACTIVE: { 167 | mOverlayView.setVisibility(View.VISIBLE); 168 | mOverlayView.setAlpha(1.f); 169 | mOverlayView.setBackgroundColor(Color.argb(0xaa, 0xcc, 0xcc, 0xcc)); 170 | break; 171 | } 172 | } 173 | } 174 | return this; 175 | } 176 | 177 | /** 178 | * Set the type of progress indicator. 179 | * The progress type can only be changed if the Card was initially build with a progress 180 | * indicator. 181 | * See {@link Builder#setProgressType(int)}. 182 | * Must be a value of either {@link #PROGRESS_TYPE_NORMAL}, 183 | * {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL} or 184 | * {@link #PROGRESS_TYPE_NO_PROGRESS}. 185 | * @param progressType 186 | * @return The card itself, allows for chaining of calls 187 | */ 188 | public Card setProgressType(int progressType) { 189 | if (mCardProgress == null) { 190 | mCardProgress = new CardProgress(); 191 | } 192 | mCardProgress.setProgressType(progressType); 193 | return this; 194 | } 195 | 196 | /** 197 | * Return the progress indicator type. A value of either {@link #PROGRESS_TYPE_NORMAL}, 198 | * {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL}. Otherwise if no progress 199 | * indicator is enabled, {@link #PROGRESS_TYPE_NO_PROGRESS} is returned. 200 | * @return 201 | */ 202 | public int getProgressType() { 203 | if (mCardProgress == null) { 204 | return PROGRESS_TYPE_NO_PROGRESS; 205 | } 206 | return mCardProgress.progressType; 207 | } 208 | 209 | /** 210 | * Set the progress to the specified value. Only applicable if the card has a 211 | * {@link #PROGRESS_TYPE_NORMAL} progress type. 212 | * @param progress 213 | * @return 214 | * @see #setMaxProgress(int) 215 | */ 216 | public Card setProgress(int progress) { 217 | if (mCardProgress != null) { 218 | mCardProgress.setProgress(progress); 219 | } 220 | return this; 221 | } 222 | 223 | /** 224 | * Set the range of the progress to 0...max. Only applicable if the card has a 225 | * {@link #PROGRESS_TYPE_NORMAL} progress type. 226 | * @return 227 | */ 228 | public Card setMaxProgress(int max){ 229 | if (mCardProgress != null) { 230 | mCardProgress.setMax(max); 231 | } 232 | return this; 233 | } 234 | 235 | /** 236 | * Set the label text for the progress if the card has a progress type of 237 | * {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or 238 | * {@link #PROGRESS_TYPE_LABEL} 239 | * @param text 240 | * @return 241 | */ 242 | public Card setProgressLabel(String text) { 243 | if (mCardProgress != null) { 244 | mCardProgress.setProgressLabel(text); 245 | } 246 | return this; 247 | } 248 | 249 | /** 250 | * Toggle the visibility of the progress section of the card. Only applicable if 251 | * the card has a progress type of 252 | * {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or 253 | * {@link #PROGRESS_TYPE_LABEL}. 254 | * @param isVisible 255 | * @return 256 | */ 257 | public Card setProgressVisibility(boolean isVisible) { 258 | if (mCardProgress.progressView == null) { 259 | return this; // Card does not have progress 260 | } 261 | mCardProgress.progressView.setVisibility(isVisible ? View.VISIBLE : View.GONE); 262 | 263 | return this; 264 | } 265 | 266 | /** 267 | * Adds an action to this card during build time. 268 | * 269 | * @param label 270 | * @param id 271 | * @param type 272 | */ 273 | private void addAction(String label, int id, int type) { 274 | CardAction cardAction = new CardAction(); 275 | cardAction.label = label; 276 | cardAction.id = id; 277 | cardAction.type = type; 278 | mCardActions.add(cardAction); 279 | } 280 | 281 | /** 282 | * Toggles the visibility of a card action. 283 | * @param actionId 284 | * @param isVisible 285 | * @return 286 | */ 287 | public Card setActionVisibility(int actionId, boolean isVisible) { 288 | int visibilityFlag = isVisible ? View.VISIBLE : View.GONE; 289 | for (CardAction action : mCardActions) { 290 | if (action.id == actionId && action.actionView != null) { 291 | action.actionView.setVisibility(visibilityFlag); 292 | } 293 | } 294 | return this; 295 | } 296 | 297 | /** 298 | * Toggles visibility of the action area of this Card through an animation. 299 | * @param isVisible 300 | * @return 301 | */ 302 | public Card setActionAreaVisibility(boolean isVisible) { 303 | if (mActionAreaView == null) { 304 | return this; // Card does not have an action area 305 | } 306 | 307 | if (isVisible) { 308 | // Show the action area 309 | mActionAreaView.setVisibility(View.VISIBLE); 310 | mActionAreaView.setPivotY(0.f); 311 | mActionAreaView.setPivotX(mCardView.getWidth() / 2.f); 312 | mActionAreaView.setAlpha(0.5f); 313 | mActionAreaView.setRotationX(-90.f); 314 | mActionAreaView.animate().rotationX(0.f).alpha(1.f).setDuration(400); 315 | } else { 316 | // Hide the action area 317 | mActionAreaView.setPivotY(0.f); 318 | mActionAreaView.setPivotX(mCardView.getWidth() / 2.f); 319 | mActionAreaView.animate().rotationX(-90.f).alpha(0.f).setDuration(400).setListener( 320 | new AnimatorListenerAdapter() { 321 | @Override 322 | public void onAnimationEnd(Animator animation) { 323 | mActionAreaView.setVisibility(View.GONE); 324 | } 325 | }); 326 | } 327 | return this; 328 | } 329 | 330 | 331 | /** 332 | * Creates a shallow clone of the card. Shallow means all values are present, but no views. 333 | * This is useful for saving/restoring in the case of configuration changes, like screen 334 | * rotation. 335 | * 336 | * @return A shallow clone of the card instance 337 | */ 338 | public Card createShallowClone() { 339 | Card cloneCard = new Card(); 340 | 341 | // Outer card values 342 | cloneCard.mTitle = mTitle; 343 | cloneCard.mDescription = mDescription; 344 | cloneCard.mTag = mTag; 345 | cloneCard.mLayoutId = mLayoutId; 346 | cloneCard.mCardState = mCardState; 347 | 348 | // Progress 349 | if (mCardProgress != null) { 350 | cloneCard.mCardProgress = mCardProgress.createShallowClone(); 351 | } 352 | 353 | // Actions 354 | for (CardAction action : mCardActions) { 355 | cloneCard.mCardActions.add(action.createShallowClone()); 356 | } 357 | 358 | return cloneCard; 359 | } 360 | 361 | 362 | /** 363 | * Prepare the card to be stored for configuration change. 364 | */ 365 | public void prepareForConfigurationChange() { 366 | // Null out views. 367 | mCardView = null; 368 | for (CardAction action : mCardActions) { 369 | action.actionView = null; 370 | } 371 | mCardProgress.progressView = null; 372 | } 373 | 374 | /** 375 | * Creates a new {@link #Card}. 376 | */ 377 | public static class Builder { 378 | private Card mCard; 379 | 380 | /** 381 | * Instantiate the builder with data from a shallow clone. 382 | * @param listener 383 | * @param card 384 | * @see Card#createShallowClone() 385 | */ 386 | protected Builder(OnCardClickListener listener, Card card) { 387 | mCard = card; 388 | mCard.mClickListener = listener; 389 | } 390 | 391 | /** 392 | * Instantiate the builder with the tag of the card. 393 | * @param listener 394 | * @param tag 395 | */ 396 | public Builder(OnCardClickListener listener, String tag) { 397 | mCard = new Card(); 398 | mCard.mTag = tag; 399 | mCard.mClickListener = listener; 400 | } 401 | 402 | public Builder setTitle(String title) { 403 | mCard.mTitle = title; 404 | return this; 405 | } 406 | 407 | public Builder setDescription(String desc) { 408 | mCard.mDescription = desc; 409 | return this; 410 | } 411 | 412 | /** 413 | * Add an action. 414 | * The type describes how this action will be displayed. Accepted values are 415 | * {@link #ACTION_NEUTRAL}, {@link #ACTION_POSITIVE} or {@link #ACTION_NEGATIVE}. 416 | * 417 | * @param label The text to display for this action 418 | * @param id Identifier for this action, supplied in the click listener 419 | * @param type UI style of action 420 | * @return 421 | */ 422 | public Builder addAction(String label, int id, int type) { 423 | mCard.addAction(label, id, type); 424 | return this; 425 | } 426 | 427 | /** 428 | * Override the default layout. 429 | * The referenced layout file has to contain the same identifiers as defined in the default 430 | * layout configuration. 431 | * @param layout 432 | * @return 433 | * @see R.layout.card 434 | */ 435 | public Builder setLayout(int layout) { 436 | mCard.mLayoutId = layout; 437 | return this; 438 | } 439 | 440 | /** 441 | * Set the type of progress bar to display. 442 | * Accepted values are: 443 | *
    444 | *
  • {@link #PROGRESS_TYPE_NO_PROGRESS} disables the progress indicator
  • 445 | *
  • {@link #PROGRESS_TYPE_NORMAL} 446 | * displays a standard, linear progress indicator.
  • 447 | *
  • {@link #PROGRESS_TYPE_INDETERMINATE} displays an indeterminate (infite) progress 448 | * indicator.
  • 449 | *
  • {@link #PROGRESS_TYPE_LABEL} only displays a label text in the progress area 450 | * of the card.
  • 451 | *
452 | * 453 | * @param progressType 454 | * @return 455 | */ 456 | public Builder setProgressType(int progressType) { 457 | mCard.setProgressType(progressType); 458 | return this; 459 | } 460 | 461 | public Builder setProgressLabel(String label) { 462 | // ensure the progress layout has been initialized, use 'no progress' by default 463 | if (mCard.mCardProgress == null) { 464 | mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS); 465 | } 466 | mCard.mCardProgress.label = label; 467 | return this; 468 | } 469 | 470 | public Builder setProgressMaxValue(int maxValue) { 471 | // ensure the progress layout has been initialized, use 'no progress' by default 472 | if (mCard.mCardProgress == null) { 473 | mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS); 474 | } 475 | mCard.mCardProgress.maxValue = maxValue; 476 | return this; 477 | } 478 | 479 | public Builder setStatus(int status) { 480 | mCard.setState(status); 481 | return this; 482 | } 483 | 484 | public Card build(Activity activity) { 485 | LayoutInflater inflater = activity.getLayoutInflater(); 486 | // Inflating the card. 487 | ViewGroup cardView = (ViewGroup) inflater.inflate(mCard.mLayoutId, 488 | (ViewGroup) activity.findViewById(R.id.card_stream), false); 489 | 490 | // Check that the layout contains a TextView with the card_title id 491 | View viewTitle = cardView.findViewById(R.id.card_title); 492 | if (mCard.mTitle != null && viewTitle != null) { 493 | mCard.mTitleView = (TextView) viewTitle; 494 | mCard.mTitleView.setText(mCard.mTitle); 495 | } else if (viewTitle != null) { 496 | viewTitle.setVisibility(View.GONE); 497 | } 498 | 499 | // Check that the layout contains a TextView with the card_content id 500 | View viewDesc = cardView.findViewById(R.id.card_content); 501 | if (mCard.mDescription != null && viewDesc != null) { 502 | mCard.mDescView = (TextView) viewDesc; 503 | mCard.mDescView.setText(mCard.mDescription); 504 | } else if (viewDesc != null) { 505 | cardView.findViewById(R.id.card_content).setVisibility(View.GONE); 506 | } 507 | 508 | 509 | ViewGroup actionArea = (ViewGroup) cardView.findViewById(R.id.card_actionarea); 510 | 511 | // Inflate Progress 512 | initializeProgressView(inflater, actionArea); 513 | 514 | // Inflate all action views. 515 | initializeActionViews(inflater, cardView, actionArea); 516 | 517 | mCard.mCardView = cardView; 518 | mCard.mOverlayView = cardView.findViewById(R.id.card_overlay); 519 | 520 | return mCard; 521 | } 522 | 523 | /** 524 | * Initialize data from the given card. 525 | * @param card 526 | * @return 527 | * @see Card#createShallowClone() 528 | */ 529 | public Builder cloneFromCard(Card card) { 530 | mCard = card.createShallowClone(); 531 | return this; 532 | } 533 | 534 | /** 535 | * Build the action views by inflating the appropriate layouts and setting the text and 536 | * values. 537 | * @param inflater 538 | * @param cardView 539 | * @param actionArea 540 | */ 541 | private void initializeActionViews(LayoutInflater inflater, ViewGroup cardView, 542 | ViewGroup actionArea) { 543 | if (!mCard.mCardActions.isEmpty()) { 544 | // Set action area to visible only when actions are visible 545 | actionArea.setVisibility(View.VISIBLE); 546 | mCard.mActionAreaView = actionArea; 547 | } 548 | 549 | // Inflate all card actions 550 | for (final CardAction action : mCard.mCardActions) { 551 | 552 | int useActionLayout = 0; 553 | switch (action.type) { 554 | case Card.ACTION_POSITIVE: 555 | useActionLayout = R.layout.card_button_positive; 556 | break; 557 | case Card.ACTION_NEGATIVE: 558 | useActionLayout = R.layout.card_button_negative; 559 | break; 560 | case Card.ACTION_NEUTRAL: 561 | default: 562 | useActionLayout = R.layout.card_button_neutral; 563 | break; 564 | } 565 | 566 | action.actionView = inflater.inflate(useActionLayout, actionArea, false); 567 | Button actionButton = (Button) action.actionView.findViewById(R.id.card_button); 568 | 569 | actionButton.setText(action.label); 570 | actionButton.setOnClickListener(new View.OnClickListener() { 571 | @Override 572 | public void onClick(View v) { 573 | mCard.mClickListener.onCardClick(action.id, mCard.mTag); 574 | } 575 | }); 576 | actionArea.addView(action.actionView); 577 | } 578 | } 579 | 580 | /** 581 | * Build the progress view into the given ViewGroup. 582 | * 583 | * @param inflater 584 | * @param actionArea 585 | */ 586 | private void initializeProgressView(LayoutInflater inflater, ViewGroup actionArea) { 587 | 588 | // Only inflate progress layout if a progress type other than NO_PROGRESS was set. 589 | if (mCard.mCardProgress != null) { 590 | //Setup progress card. 591 | View progressView = inflater.inflate(R.layout.card_progress, actionArea, false); 592 | ProgressBar progressBar = 593 | (ProgressBar) progressView.findViewById(R.id.card_progress); 594 | ((TextView) progressView.findViewById(R.id.card_progress_text)) 595 | .setText(mCard.mCardProgress.label); 596 | progressBar.setMax(mCard.mCardProgress.maxValue); 597 | progressBar.setProgress(0); 598 | mCard.mCardProgress.progressView = progressView; 599 | mCard.mCardProgress.setProgressType(mCard.getProgressType()); 600 | actionArea.addView(progressView); 601 | } 602 | } 603 | } 604 | 605 | /** 606 | * Represents a clickable action, accessible from the bottom of the card. 607 | * Fields include the label, an ID to specify the action that was performed in the callback, 608 | * an action type (positive, negative, neutral), and the callback. 609 | */ 610 | public class CardAction { 611 | 612 | public String label; 613 | public int id; 614 | public int type; 615 | public View actionView; 616 | 617 | public CardAction createShallowClone() { 618 | CardAction actionClone = new CardAction(); 619 | actionClone.label = label; 620 | actionClone.id = id; 621 | actionClone.type = type; 622 | return actionClone; 623 | // Not the view. Never the view (don't want to hold view references for 624 | // onConfigurationChange. 625 | } 626 | 627 | } 628 | 629 | /** 630 | * Describes the progress of a {@link Card}. 631 | * Three types of progress are supported: 632 | *
  • {@link Card#PROGRESS_TYPE_NORMAL: Standard progress bar with label text
  • 633 | *
  • {@link Card#PROGRESS_TYPE_INDETERMINATE}: Indeterminate progress bar with label txt
  • 634 | *
  • {@link Card#PROGRESS_TYPE_LABEL}: Label only, no progresss bar
  • 635 | *
636 | */ 637 | public class CardProgress { 638 | private int progressType = Card.PROGRESS_TYPE_NO_PROGRESS; 639 | private String label = ""; 640 | private int currProgress = 0; 641 | private int maxValue = 100; 642 | 643 | public View progressView = null; 644 | private ProgressBar progressBar = null; 645 | private TextView progressLabel = null; 646 | 647 | public CardProgress createShallowClone() { 648 | CardProgress progressClone = new CardProgress(); 649 | progressClone.label = label; 650 | progressClone.currProgress = currProgress; 651 | progressClone.maxValue = maxValue; 652 | progressClone.progressType = progressType; 653 | return progressClone; 654 | } 655 | 656 | /** 657 | * Set the progress. Only useful for the type {@link #PROGRESS_TYPE_NORMAL}. 658 | * @param progress 659 | * @see android.widget.ProgressBar#setProgress(int) 660 | */ 661 | public void setProgress(int progress) { 662 | currProgress = progress; 663 | final ProgressBar bar = getProgressBar(); 664 | if (bar != null) { 665 | bar.setProgress(currProgress); 666 | bar.invalidate(); 667 | } 668 | } 669 | 670 | /** 671 | * Set the range of the progress to 0...max. 672 | * Only useful for the type {@link #PROGRESS_TYPE_NORMAL}. 673 | * @param max 674 | * @see android.widget.ProgressBar#setMax(int) 675 | */ 676 | public void setMax(int max) { 677 | maxValue = max; 678 | final ProgressBar bar = getProgressBar(); 679 | if (bar != null) { 680 | bar.setMax(maxValue); 681 | } 682 | } 683 | 684 | /** 685 | * Set the label text that appears near the progress indicator. 686 | * @param text 687 | */ 688 | public void setProgressLabel(String text) { 689 | label = text; 690 | final TextView labelView = getProgressLabel(); 691 | if (labelView != null) { 692 | labelView.setText(text); 693 | } 694 | } 695 | 696 | /** 697 | * Set how progress is displayed. The parameter must be one of three supported types: 698 | *
  • {@link Card#PROGRESS_TYPE_NORMAL: Standard progress bar with label text
  • 699 | *
  • {@link Card#PROGRESS_TYPE_INDETERMINATE}: 700 | * Indeterminate progress bar with label txt
  • 701 | *
  • {@link Card#PROGRESS_TYPE_LABEL}: Label only, no progresss bar
  • 702 | * @param type 703 | */ 704 | public void setProgressType(int type) { 705 | progressType = type; 706 | if (progressView != null) { 707 | switch (type) { 708 | case PROGRESS_TYPE_NO_PROGRESS: { 709 | progressView.setVisibility(View.GONE); 710 | break; 711 | } 712 | case PROGRESS_TYPE_NORMAL: { 713 | progressView.setVisibility(View.VISIBLE); 714 | getProgressBar().setIndeterminate(false); 715 | break; 716 | } 717 | case PROGRESS_TYPE_INDETERMINATE: { 718 | progressView.setVisibility(View.VISIBLE); 719 | getProgressBar().setIndeterminate(true); 720 | break; 721 | } 722 | } 723 | } 724 | } 725 | 726 | private TextView getProgressLabel() { 727 | if (progressLabel != null) { 728 | return progressLabel; 729 | } else if (progressView != null) { 730 | progressLabel = (TextView) progressView.findViewById(R.id.card_progress_text); 731 | return progressLabel; 732 | } else { 733 | return null; 734 | } 735 | } 736 | 737 | private ProgressBar getProgressBar() { 738 | if (progressBar != null) { 739 | return progressBar; 740 | } else if (progressView != null) { 741 | progressBar = (ProgressBar) progressView.findViewById(R.id.card_progress); 742 | return progressBar; 743 | } else { 744 | return null; 745 | } 746 | } 747 | 748 | } 749 | } 750 | 751 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardActionButton.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.content.Context; 21 | import android.os.Build; 22 | import android.support.v4.view.ViewCompat; 23 | import android.util.AttributeSet; 24 | import android.view.MotionEvent; 25 | import android.view.animation.BounceInterpolator; 26 | import android.view.animation.DecelerateInterpolator; 27 | import android.widget.Button; 28 | 29 | /** 30 | * Custom Button with a special 'pressed' effect for touch events. 31 | */ 32 | public class CardActionButton extends Button { 33 | 34 | public CardActionButton(Context context) { 35 | super(context); 36 | } 37 | 38 | public CardActionButton(Context context, AttributeSet attrs) { 39 | super(context, attrs); 40 | } 41 | 42 | public CardActionButton(Context context, AttributeSet attrs, int defStyle) { 43 | super(context, attrs, defStyle); 44 | } 45 | 46 | @Override 47 | public boolean onTouchEvent(MotionEvent event) { 48 | 49 | switch (event.getAction()) { 50 | case MotionEvent.ACTION_DOWN: { 51 | setPressed(true); 52 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { 53 | animate().scaleX(0.98f).scaleY(0.98f).setDuration(100) 54 | .setInterpolator(new DecelerateInterpolator()); 55 | } else { 56 | ViewCompat.setElevation(this, 8.f); 57 | } 58 | break; 59 | } 60 | case MotionEvent.ACTION_UP: 61 | case MotionEvent.ACTION_CANCEL: { 62 | setPressed(false); 63 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { 64 | animate().scaleX(1.f).scaleY(1.f).setDuration(50) 65 | .setInterpolator(new BounceInterpolator()); 66 | } else { 67 | ViewCompat.setElevation(this, 0.f); 68 | } 69 | break; 70 | } 71 | } 72 | 73 | return super.onTouchEvent(event); 74 | } 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.content.Context; 21 | import android.util.AttributeSet; 22 | import android.view.MotionEvent; 23 | import android.view.ViewConfiguration; 24 | import android.widget.RelativeLayout; 25 | 26 | /** 27 | * Custom Button with a special 'pressed' effect for touch events. 28 | */ 29 | public class CardLayout extends RelativeLayout { 30 | 31 | private boolean mSwiping = false; 32 | private float mDownX = 0.f; 33 | private float mDownY = 0.f; 34 | private float mTouchSlop = 0.f; 35 | 36 | public CardLayout(Context context) { 37 | super(context); 38 | init(); 39 | } 40 | 41 | public CardLayout(Context context, AttributeSet attrs) { 42 | super(context, attrs); 43 | init(); 44 | } 45 | 46 | public CardLayout(Context context, AttributeSet attrs, int defStyle) { 47 | super(context, attrs, defStyle); 48 | init(); 49 | } 50 | 51 | private void init(){ 52 | setFocusable(true); 53 | setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 54 | setWillNotDraw(false); 55 | setClickable(true); 56 | 57 | mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop() * 2.f; 58 | } 59 | 60 | @Override 61 | public boolean onTouchEvent(MotionEvent event) { 62 | switch(event.getAction()){ 63 | case MotionEvent.ACTION_CANCEL: 64 | case MotionEvent.ACTION_UP: 65 | mSwiping = false; 66 | break; 67 | } 68 | return super.onTouchEvent(event); 69 | } 70 | 71 | @Override 72 | public boolean onInterceptTouchEvent(MotionEvent event) { 73 | 74 | switch(event.getAction()){ 75 | case MotionEvent.ACTION_MOVE: 76 | if( !mSwiping ){ 77 | mSwiping = Math.abs(mDownX - event.getX()) > mTouchSlop; 78 | } 79 | break; 80 | case MotionEvent.ACTION_DOWN: 81 | mDownX = event.getX(); 82 | mDownY = event.getY(); 83 | mSwiping = false; 84 | break; 85 | case MotionEvent.ACTION_CANCEL: 86 | case MotionEvent.ACTION_UP: 87 | mSwiping = false; 88 | break; 89 | } 90 | return mSwiping; 91 | } 92 | } -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | public interface CardStream { 21 | public CardStreamFragment getCardStream(); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardStreamAnimator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.animation.ObjectAnimator; 21 | import android.content.Context; 22 | import android.view.View; 23 | 24 | /** 25 | * An abstract class which defines animators for CardStreamLinearLayout. 26 | */ 27 | abstract class CardStreamAnimator { 28 | 29 | protected float mSpeedFactor = 1.f; 30 | 31 | /** 32 | * Set speed factor of animations. Higher value means longer duration & slow animation. 33 | * 34 | * @param speedFactor speed type 1: SLOW, 2: NORMAL, 3:FAST 35 | */ 36 | public void setSpeedFactor(float speedFactor) { 37 | mSpeedFactor = speedFactor; 38 | } 39 | 40 | /** 41 | * Define initial animation of each child which fired when a user rotate a screen. 42 | * 43 | * @param context 44 | * @return ObjectAnimator for initial animation 45 | */ 46 | public abstract ObjectAnimator getInitalAnimator(Context context); 47 | 48 | /** 49 | * Define disappearing animation of a child which fired when a view is removed programmatically 50 | * 51 | * @param context 52 | * @return ObjectAnimator for disappearing animation 53 | */ 54 | public abstract ObjectAnimator getDisappearingAnimator(Context context); 55 | 56 | /** 57 | * Define appearing animation of a child which fired when a view is added programmatically 58 | * 59 | * @param context 60 | * @return ObjectAnimator for appearing animation 61 | */ 62 | public abstract ObjectAnimator getAppearingAnimator(Context context); 63 | 64 | /** 65 | * Define swipe-in (back to the origin position) animation of a child 66 | * which fired when a view is not moved enough to be removed. 67 | * 68 | * @param view target view 69 | * @param deltaX delta distance by x-axis 70 | * @param deltaY delta distance by y-axis 71 | * @return ObjectAnimator for swipe-in animation 72 | */ 73 | public abstract ObjectAnimator getSwipeInAnimator(View view, float deltaX, float deltaY); 74 | 75 | /** 76 | * Define swipe-out animation of a child 77 | * which fired when a view is removing by a user swipe action. 78 | * 79 | * @param view target view 80 | * @param deltaX delta distance by x-axis 81 | * @param deltaY delta distance by y-axis 82 | * @return ObjectAnimator for swipe-out animation 83 | */ 84 | public abstract ObjectAnimator getSwipeOutAnimator(View view, float deltaX, float deltaY); 85 | 86 | /** 87 | * A simple CardStreamAnimator implementation which is used to turn animations off. 88 | */ 89 | public static class EmptyAnimator extends CardStreamAnimator { 90 | 91 | @Override 92 | public ObjectAnimator getInitalAnimator(Context context) { 93 | return null; 94 | } 95 | 96 | @Override 97 | public ObjectAnimator getDisappearingAnimator(Context context) { 98 | return null; 99 | } 100 | 101 | @Override 102 | public ObjectAnimator getAppearingAnimator(Context context) { 103 | return null; 104 | } 105 | 106 | @Override 107 | public ObjectAnimator getSwipeInAnimator(View view, float deltaX, float deltaY) { 108 | return null; 109 | } 110 | 111 | @Override 112 | public ObjectAnimator getSwipeOutAnimator(View view, float deltaX, float deltaY) { 113 | return null; 114 | } 115 | } 116 | 117 | } 118 | 119 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardStreamFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.os.Bundle; 21 | import android.support.v4.app.Fragment; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | 26 | import java.util.Collection; 27 | import java.util.HashMap; 28 | import java.util.HashSet; 29 | import java.util.LinkedHashMap; 30 | 31 | import com.example.android.batchstepsensor.R; 32 | 33 | /** 34 | * A Fragment that handles a stream of cards. 35 | * Cards can be shown or hidden. When a card is shown it can also be marked as not-dismissible, see 36 | * {@link CardStreamLinearLayout#addCard(android.view.View, boolean)}. 37 | */ 38 | public class CardStreamFragment extends Fragment { 39 | 40 | private static final int INITIAL_SIZE = 15; 41 | private CardStreamLinearLayout mLayout = null; 42 | private LinkedHashMap mVisibleCards = new LinkedHashMap(INITIAL_SIZE); 43 | private HashMap mHiddenCards = new HashMap(INITIAL_SIZE); 44 | private HashSet mDismissibleCards = new HashSet(INITIAL_SIZE); 45 | 46 | // Set the listener to handle dismissed cards by moving them to the hidden cards map. 47 | private CardStreamLinearLayout.OnDissmissListener mCardDismissListener = 48 | new CardStreamLinearLayout.OnDissmissListener() { 49 | @Override 50 | public void onDismiss(String tag) { 51 | dismissCard(tag); 52 | } 53 | }; 54 | 55 | 56 | @Override 57 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 58 | Bundle savedInstanceState) { 59 | 60 | View view = inflater.inflate(R.layout.cardstream, container, false); 61 | mLayout = (CardStreamLinearLayout) view.findViewById(R.id.card_stream); 62 | mLayout.setOnDismissListener(mCardDismissListener); 63 | 64 | return view; 65 | } 66 | 67 | /** 68 | * Add a visible, dismissible card to the card stream. 69 | * 70 | * @param card 71 | */ 72 | public void addCard(Card card) { 73 | final String tag = card.getTag(); 74 | 75 | if (!mVisibleCards.containsKey(tag) && !mHiddenCards.containsKey(tag)) { 76 | final View view = card.getView(); 77 | view.setTag(tag); 78 | mHiddenCards.put(tag, card); 79 | } 80 | } 81 | 82 | /** 83 | * Add and show a card. 84 | * 85 | * @param card 86 | * @param show 87 | */ 88 | public void addCard(Card card, boolean show) { 89 | addCard(card); 90 | if (show) { 91 | showCard(card.getTag()); 92 | } 93 | } 94 | 95 | /** 96 | * Remove a card and return true if it has been successfully removed. 97 | * 98 | * @param tag 99 | * @return 100 | */ 101 | public boolean removeCard(String tag) { 102 | // Attempt to remove a visible card first 103 | Card card = mVisibleCards.get(tag); 104 | if (card != null) { 105 | // Card is visible, also remove from layout 106 | mVisibleCards.remove(tag); 107 | mLayout.removeView(card.getView()); 108 | return true; 109 | } else { 110 | // Card is hidden, no need to remove from layout 111 | card = mHiddenCards.remove(tag); 112 | return card != null; 113 | } 114 | } 115 | 116 | /** 117 | * Show a dismissible card, returns false if the card could not be shown. 118 | * 119 | * @param tag 120 | * @return 121 | */ 122 | public boolean showCard(String tag) { 123 | return showCard(tag, true); 124 | } 125 | 126 | /** 127 | * Show a card, returns false if the card could not be shown. 128 | * 129 | * @param tag 130 | * @param dismissible 131 | * @return 132 | */ 133 | public boolean showCard(String tag, boolean dismissible) { 134 | final Card card = mHiddenCards.get(tag); 135 | // ensure the card is hidden and not already visible 136 | if (card != null && !mVisibleCards.containsValue(tag)) { 137 | mHiddenCards.remove(tag); 138 | mVisibleCards.put(tag, card); 139 | mLayout.addCard(card.getView(), dismissible); 140 | if (dismissible) { 141 | mDismissibleCards.add(tag); 142 | } 143 | return true; 144 | } 145 | return false; 146 | } 147 | 148 | /** 149 | * Hides the card, returns false if the card could not be hidden. 150 | * 151 | * @param tag 152 | * @return 153 | */ 154 | public boolean hideCard(String tag) { 155 | final Card card = mVisibleCards.get(tag); 156 | if (card != null) { 157 | mVisibleCards.remove(tag); 158 | mDismissibleCards.remove(tag); 159 | mHiddenCards.put(tag, card); 160 | 161 | mLayout.removeView(card.getView()); 162 | return true; 163 | } 164 | return mHiddenCards.containsValue(tag); 165 | } 166 | 167 | 168 | private void dismissCard(String tag) { 169 | final Card card = mVisibleCards.get(tag); 170 | if (card != null) { 171 | mDismissibleCards.remove(tag); 172 | mVisibleCards.remove(tag); 173 | mHiddenCards.put(tag, card); 174 | } 175 | } 176 | 177 | 178 | public boolean isCardVisible(String tag) { 179 | return mVisibleCards.containsValue(tag); 180 | } 181 | 182 | /** 183 | * Returns true if the card is shown and is dismissible. 184 | * 185 | * @param tag 186 | * @return 187 | */ 188 | public boolean isCardDismissible(String tag) { 189 | return mDismissibleCards.contains(tag); 190 | } 191 | 192 | /** 193 | * Returns the Card for this tag. 194 | * 195 | * @param tag 196 | * @return 197 | */ 198 | public Card getCard(String tag) { 199 | final Card card = mVisibleCards.get(tag); 200 | if (card != null) { 201 | return card; 202 | } else { 203 | return mHiddenCards.get(tag); 204 | } 205 | } 206 | 207 | /** 208 | * Moves the view port to show the card with this tag. 209 | * 210 | * @param tag 211 | * @see CardStreamLinearLayout#setFirstVisibleCard(String) 212 | */ 213 | public void setFirstVisibleCard(String tag) { 214 | final Card card = mVisibleCards.get(tag); 215 | if (card != null) { 216 | mLayout.setFirstVisibleCard(tag); 217 | } 218 | } 219 | 220 | public int getVisibleCardCount() { 221 | return mVisibleCards.size(); 222 | } 223 | 224 | public Collection getVisibleCards() { 225 | return mVisibleCards.values(); 226 | } 227 | 228 | public void restoreState(CardStreamState state, OnCardClickListener callback) { 229 | // restore hidden cards 230 | for (Card c : state.hiddenCards) { 231 | Card card = new Card.Builder(callback,c).build(getActivity()); 232 | mHiddenCards.put(card.getTag(), card); 233 | } 234 | 235 | // temporarily set up list of dismissible 236 | final HashSet dismissibleCards = state.dismissibleCards; 237 | 238 | //restore shown cards 239 | for (Card c : state.visibleCards) { 240 | Card card = new Card.Builder(callback,c).build(getActivity()); 241 | addCard(card); 242 | final String tag = card.getTag(); 243 | showCard(tag, dismissibleCards.contains(tag)); 244 | } 245 | 246 | // move to first visible card 247 | final String firstShown = state.shownTag; 248 | if (firstShown != null) { 249 | mLayout.setFirstVisibleCard(firstShown); 250 | } 251 | 252 | mLayout.triggerShowInitialAnimation(); 253 | } 254 | 255 | public CardStreamState dumpState() { 256 | final Card[] visible = cloneCards(mVisibleCards.values()); 257 | final Card[] hidden = cloneCards(mHiddenCards.values()); 258 | final HashSet dismissible = new HashSet(mDismissibleCards); 259 | final String firstVisible = mLayout.getFirstVisibleCardTag(); 260 | 261 | return new CardStreamState(visible, hidden, dismissible, firstVisible); 262 | } 263 | 264 | private Card[] cloneCards(Collection cards) { 265 | Card[] cardArray = new Card[cards.size()]; 266 | int i = 0; 267 | for (Card c : cards) { 268 | cardArray[i++] = c.createShallowClone(); 269 | } 270 | 271 | return cardArray; 272 | } 273 | 274 | } 275 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardStreamLinearLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.animation.Animator; 21 | import android.animation.LayoutTransition; 22 | import android.animation.ObjectAnimator; 23 | import android.annotation.SuppressLint; 24 | import android.annotation.TargetApi; 25 | import android.content.Context; 26 | import android.content.res.TypedArray; 27 | import android.graphics.Rect; 28 | import android.os.Build; 29 | import android.util.AttributeSet; 30 | import android.view.MotionEvent; 31 | import android.view.View; 32 | import android.view.ViewConfiguration; 33 | import android.view.ViewGroup; 34 | import android.view.ViewParent; 35 | import android.widget.LinearLayout; 36 | import android.widget.ScrollView; 37 | 38 | import com.example.android.common.logger.Log; 39 | import com.example.android.batchstepsensor.R; 40 | 41 | import java.util.ArrayList; 42 | 43 | /** 44 | * A Layout that contains a stream of card views. 45 | */ 46 | public class CardStreamLinearLayout extends LinearLayout { 47 | 48 | public static final int ANIMATION_SPEED_SLOW = 1001; 49 | public static final int ANIMATION_SPEED_NORMAL = 1002; 50 | public static final int ANIMATION_SPEED_FAST = 1003; 51 | 52 | private static final String TAG = "CardStreamLinearLayout"; 53 | private final ArrayList mFixedViewList = new ArrayList(); 54 | private final Rect mChildRect = new Rect(); 55 | private CardStreamAnimator mAnimators; 56 | private OnDissmissListener mDismissListener = null; 57 | private boolean mLayouted = false; 58 | private boolean mSwiping = false; 59 | private String mFirstVisibleCardTag = null; 60 | private boolean mShowInitialAnimation = false; 61 | 62 | /** 63 | * Handle touch events to fade/move dragged items as they are swiped out 64 | */ 65 | private OnTouchListener mTouchListener = new OnTouchListener() { 66 | 67 | private float mDownX; 68 | private float mDownY; 69 | 70 | @Override 71 | public boolean onTouch(final View v, MotionEvent event) { 72 | 73 | switch (event.getAction()) { 74 | case MotionEvent.ACTION_DOWN: 75 | mDownX = event.getX(); 76 | mDownY = event.getY(); 77 | break; 78 | case MotionEvent.ACTION_CANCEL: 79 | resetAnimatedView(v); 80 | mSwiping = false; 81 | mDownX = 0.f; 82 | mDownY = 0.f; 83 | break; 84 | case MotionEvent.ACTION_MOVE: { 85 | 86 | float x = event.getX() + v.getTranslationX(); 87 | float y = event.getY() + v.getTranslationY(); 88 | 89 | mDownX = mDownX == 0.f ? x : mDownX; 90 | mDownY = mDownY == 0.f ? x : mDownY; 91 | 92 | float deltaX = x - mDownX; 93 | float deltaY = y - mDownY; 94 | 95 | if (!mSwiping && isSwiping(deltaX, deltaY)) { 96 | mSwiping = true; 97 | v.getParent().requestDisallowInterceptTouchEvent(true); 98 | } else { 99 | swipeView(v, deltaX, deltaY); 100 | } 101 | } 102 | break; 103 | case MotionEvent.ACTION_UP: { 104 | // User let go - figure out whether to animate the view out, or back into place 105 | if (mSwiping) { 106 | float x = event.getX() + v.getTranslationX(); 107 | float y = event.getY() + v.getTranslationY(); 108 | 109 | float deltaX = x - mDownX; 110 | float deltaY = y - mDownX; 111 | float deltaXAbs = Math.abs(deltaX); 112 | 113 | // User let go - figure out whether to animate the view out, or back into place 114 | boolean remove = deltaXAbs > v.getWidth() / 4 && !isFixedView(v); 115 | if( remove ) 116 | handleViewSwipingOut(v, deltaX, deltaY); 117 | else 118 | handleViewSwipingIn(v, deltaX, deltaY); 119 | } 120 | mDownX = 0.f; 121 | mDownY = 0.f; 122 | mSwiping = false; 123 | } 124 | break; 125 | default: 126 | return false; 127 | } 128 | return false; 129 | } 130 | }; 131 | private int mSwipeSlop = -1; 132 | /** 133 | * Handle end-transition animation event of each child and launch a following animation. 134 | */ 135 | private LayoutTransition.TransitionListener mTransitionListener 136 | = new LayoutTransition.TransitionListener() { 137 | 138 | @Override 139 | public void startTransition(LayoutTransition transition, ViewGroup container, View 140 | view, int transitionType) { 141 | Log.d(TAG, "Start LayoutTransition animation:" + transitionType); 142 | } 143 | 144 | @Override 145 | public void endTransition(LayoutTransition transition, ViewGroup container, 146 | final View view, int transitionType) { 147 | 148 | Log.d(TAG, "End LayoutTransition animation:" + transitionType); 149 | if (transitionType == LayoutTransition.APPEARING) { 150 | final View area = view.findViewById(R.id.card_actionarea); 151 | if (area != null) { 152 | runShowActionAreaAnimation(container, area); 153 | } 154 | } 155 | } 156 | }; 157 | /** 158 | * Handle a hierarchy change event 159 | * when a new child is added, scroll to bottom and hide action area.. 160 | */ 161 | private OnHierarchyChangeListener mOnHierarchyChangeListener 162 | = new OnHierarchyChangeListener() { 163 | @Override 164 | public void onChildViewAdded(final View parent, final View child) { 165 | 166 | Log.d(TAG, "child is added: " + child); 167 | 168 | ViewParent scrollView = parent.getParent(); 169 | if (scrollView != null && scrollView instanceof ScrollView) { 170 | ((ScrollView) scrollView).fullScroll(FOCUS_DOWN); 171 | } 172 | 173 | if (getLayoutTransition() != null) { 174 | View view = child.findViewById(R.id.card_actionarea); 175 | if (view != null) 176 | view.setAlpha(0.f); 177 | } 178 | } 179 | 180 | @Override 181 | public void onChildViewRemoved(View parent, View child) { 182 | Log.d(TAG, "child is removed: " + child); 183 | mFixedViewList.remove(child); 184 | } 185 | }; 186 | private int mLastDownX; 187 | 188 | public CardStreamLinearLayout(Context context) { 189 | super(context); 190 | initialize(null, 0); 191 | } 192 | 193 | public CardStreamLinearLayout(Context context, AttributeSet attrs) { 194 | super(context, attrs); 195 | initialize(attrs, 0); 196 | } 197 | 198 | @SuppressLint("NewApi") 199 | public CardStreamLinearLayout(Context context, AttributeSet attrs, int defStyle) { 200 | super(context, attrs, defStyle); 201 | initialize(attrs, defStyle); 202 | } 203 | 204 | /** 205 | * add a card view w/ canDismiss flag. 206 | * 207 | * @param cardView a card view 208 | * @param canDismiss flag to indicate this card is dismissible or not. 209 | */ 210 | public void addCard(View cardView, boolean canDismiss) { 211 | if (cardView.getParent() == null) { 212 | initCard(cardView, canDismiss); 213 | 214 | ViewGroup.LayoutParams param = cardView.getLayoutParams(); 215 | if(param == null) 216 | param = generateDefaultLayoutParams(); 217 | 218 | super.addView(cardView, -1, param); 219 | } 220 | } 221 | 222 | @Override 223 | public void addView(View child, int index, ViewGroup.LayoutParams params) { 224 | if (child.getParent() == null) { 225 | initCard(child, true); 226 | super.addView(child, index, params); 227 | } 228 | } 229 | 230 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 231 | @Override 232 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 233 | super.onLayout(changed, l, t, r, b); 234 | Log.d(TAG, "onLayout: " + changed); 235 | 236 | if( changed && !mLayouted ){ 237 | mLayouted = true; 238 | 239 | ObjectAnimator animator; 240 | LayoutTransition layoutTransition = new LayoutTransition(); 241 | 242 | animator = mAnimators.getDisappearingAnimator(getContext()); 243 | layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, animator); 244 | 245 | animator = mAnimators.getAppearingAnimator(getContext()); 246 | layoutTransition.setAnimator(LayoutTransition.APPEARING, animator); 247 | 248 | layoutTransition.addTransitionListener(mTransitionListener); 249 | 250 | if( animator != null ) 251 | layoutTransition.setDuration(animator.getDuration()); 252 | 253 | setLayoutTransition(layoutTransition); 254 | 255 | if( mShowInitialAnimation ) 256 | runInitialAnimations(); 257 | 258 | if (mFirstVisibleCardTag != null) { 259 | scrollToCard(mFirstVisibleCardTag); 260 | mFirstVisibleCardTag = null; 261 | } 262 | } 263 | } 264 | 265 | /** 266 | * Check whether a user moved enough distance to start a swipe action or not. 267 | * 268 | * @param deltaX 269 | * @param deltaY 270 | * @return true if a user is swiping. 271 | */ 272 | protected boolean isSwiping(float deltaX, float deltaY) { 273 | 274 | if (mSwipeSlop < 0) { 275 | //get swipping slop from ViewConfiguration; 276 | mSwipeSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 277 | } 278 | 279 | boolean swipping = false; 280 | float absDeltaX = Math.abs(deltaX); 281 | 282 | if( absDeltaX > mSwipeSlop ) 283 | return true; 284 | 285 | return swipping; 286 | } 287 | 288 | /** 289 | * Swipe a view by moving distance 290 | * 291 | * @param child a target view 292 | * @param deltaX x moving distance by x-axis. 293 | * @param deltaY y moving distance by y-axis. 294 | */ 295 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 296 | protected void swipeView(View child, float deltaX, float deltaY) { 297 | if (isFixedView(child)){ 298 | deltaX = deltaX / 4; 299 | } 300 | 301 | float deltaXAbs = Math.abs(deltaX); 302 | float fractionCovered = deltaXAbs / (float) child.getWidth(); 303 | 304 | child.setTranslationX(deltaX); 305 | child.setAlpha(1.f - fractionCovered); 306 | 307 | if (deltaX > 0) 308 | child.setRotationY(-15.f * fractionCovered); 309 | else 310 | child.setRotationY(15.f * fractionCovered); 311 | } 312 | 313 | protected void notifyOnDismissEvent( View child ){ 314 | if( child == null || mDismissListener == null ) 315 | return; 316 | 317 | mDismissListener.onDismiss((String) child.getTag()); 318 | } 319 | 320 | /** 321 | * get the tag of the first visible child in this layout 322 | * 323 | * @return tag of the first visible child or null 324 | */ 325 | public String getFirstVisibleCardTag() { 326 | 327 | final int count = getChildCount(); 328 | 329 | if (count == 0) 330 | return null; 331 | 332 | for (int index = 0; index < count; ++index) { 333 | //check the position of each view. 334 | View child = getChildAt(index); 335 | if (child.getGlobalVisibleRect(mChildRect) == true) 336 | return (String) child.getTag(); 337 | } 338 | 339 | return null; 340 | } 341 | 342 | /** 343 | * Set the first visible card of this linear layout. 344 | * 345 | * @param tag tag of a card which should already added to this layout. 346 | */ 347 | public void setFirstVisibleCard(String tag) { 348 | if (tag == null) 349 | return; //do nothing. 350 | 351 | if (mLayouted) { 352 | scrollToCard(tag); 353 | } else { 354 | //keep the tag for next use. 355 | mFirstVisibleCardTag = tag; 356 | } 357 | } 358 | 359 | /** 360 | * If this flag is set, 361 | * after finishing initial onLayout event, an initial animation which is defined in DefaultCardStreamAnimator is launched. 362 | */ 363 | public void triggerShowInitialAnimation(){ 364 | mShowInitialAnimation = true; 365 | } 366 | 367 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 368 | public void setCardStreamAnimator( CardStreamAnimator animators ){ 369 | 370 | if( animators == null ) 371 | mAnimators = new CardStreamAnimator.EmptyAnimator(); 372 | else 373 | mAnimators = animators; 374 | 375 | LayoutTransition layoutTransition = getLayoutTransition(); 376 | 377 | if( layoutTransition != null ){ 378 | layoutTransition.setAnimator( LayoutTransition.APPEARING, 379 | mAnimators.getAppearingAnimator(getContext()) ); 380 | layoutTransition.setAnimator( LayoutTransition.DISAPPEARING, 381 | mAnimators.getDisappearingAnimator(getContext()) ); 382 | } 383 | } 384 | 385 | /** 386 | * set a OnDismissListener which called when user dismiss a card. 387 | * 388 | * @param listener 389 | */ 390 | public void setOnDismissListener(OnDissmissListener listener) { 391 | mDismissListener = listener; 392 | } 393 | 394 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 395 | private void initialize(AttributeSet attrs, int defStyle) { 396 | 397 | float speedFactor = 1.f; 398 | 399 | if (attrs != null) { 400 | TypedArray a = getContext().obtainStyledAttributes(attrs, 401 | R.styleable.CardStream, defStyle, 0); 402 | 403 | if( a != null ){ 404 | int speedType = a.getInt(R.styleable.CardStream_animationDuration, 1001); 405 | switch (speedType){ 406 | case ANIMATION_SPEED_FAST: 407 | speedFactor = 0.5f; 408 | break; 409 | case ANIMATION_SPEED_NORMAL: 410 | speedFactor = 1.f; 411 | break; 412 | case ANIMATION_SPEED_SLOW: 413 | speedFactor = 2.f; 414 | break; 415 | } 416 | 417 | String animatorName = a.getString(R.styleable.CardStream_animators); 418 | 419 | try { 420 | if( animatorName != null ) 421 | mAnimators = (CardStreamAnimator) getClass().getClassLoader() 422 | .loadClass(animatorName).newInstance(); 423 | } catch (Exception e) { 424 | Log.e(TAG, "Fail to load animator:" + animatorName, e); 425 | } finally { 426 | if(mAnimators == null) 427 | mAnimators = new DefaultCardStreamAnimator(); 428 | } 429 | a.recycle(); 430 | } 431 | } 432 | 433 | mAnimators.setSpeedFactor(speedFactor); 434 | mSwipeSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 435 | setOnHierarchyChangeListener(mOnHierarchyChangeListener); 436 | } 437 | 438 | private void initCard(View cardView, boolean canDismiss) { 439 | resetAnimatedView(cardView); 440 | cardView.setOnTouchListener(mTouchListener); 441 | if (!canDismiss) 442 | mFixedViewList.add(cardView); 443 | } 444 | 445 | private boolean isFixedView(View v) { 446 | return mFixedViewList.contains(v); 447 | } 448 | 449 | private void resetAnimatedView(View child) { 450 | child.setAlpha(1.f); 451 | child.setTranslationX(0.f); 452 | child.setTranslationY(0.f); 453 | child.setRotation(0.f); 454 | child.setRotationY(0.f); 455 | child.setRotationX(0.f); 456 | child.setScaleX(1.f); 457 | child.setScaleY(1.f); 458 | } 459 | 460 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 461 | private void runInitialAnimations() { 462 | if( mAnimators == null ) 463 | return; 464 | 465 | final int count = getChildCount(); 466 | 467 | for (int index = 0; index < count; ++index) { 468 | final View child = getChildAt(index); 469 | ObjectAnimator animator = mAnimators.getInitalAnimator(getContext()); 470 | if( animator != null ){ 471 | animator.setTarget(child); 472 | animator.start(); 473 | } 474 | } 475 | } 476 | 477 | private void runShowActionAreaAnimation(View parent, View area) { 478 | area.setPivotY(0.f); 479 | area.setPivotX(parent.getWidth() / 2.f); 480 | 481 | area.setAlpha(0.5f); 482 | area.setRotationX(-90.f); 483 | area.animate().rotationX(0.f).alpha(1.f).setDuration(400); 484 | } 485 | 486 | private void handleViewSwipingOut(final View child, float deltaX, float deltaY) { 487 | ObjectAnimator animator = mAnimators.getSwipeOutAnimator(child, deltaX, deltaY); 488 | if( animator != null ){ 489 | animator.addListener(new EndAnimationWrapper() { 490 | @Override 491 | public void onAnimationEnd(Animator animation) { 492 | removeView(child); 493 | notifyOnDismissEvent(child); 494 | } 495 | }); 496 | } else { 497 | removeView(child); 498 | notifyOnDismissEvent(child); 499 | } 500 | 501 | if( animator != null ){ 502 | animator.setTarget(child); 503 | animator.start(); 504 | } 505 | } 506 | 507 | private void handleViewSwipingIn(final View child, float deltaX, float deltaY) { 508 | ObjectAnimator animator = mAnimators.getSwipeInAnimator(child, deltaX, deltaY); 509 | if( animator != null ){ 510 | animator.addListener(new EndAnimationWrapper() { 511 | @Override 512 | public void onAnimationEnd(Animator animation) { 513 | child.setTranslationY(0.f); 514 | child.setTranslationX(0.f); 515 | } 516 | }); 517 | } else { 518 | child.setTranslationY(0.f); 519 | child.setTranslationX(0.f); 520 | } 521 | 522 | if( animator != null ){ 523 | animator.setTarget(child); 524 | animator.start(); 525 | } 526 | } 527 | 528 | private void scrollToCard(String tag) { 529 | 530 | 531 | final int count = getChildCount(); 532 | for (int index = 0; index < count; ++index) { 533 | View child = getChildAt(index); 534 | 535 | if (tag.equals(child.getTag())) { 536 | 537 | ViewParent parent = getParent(); 538 | if( parent != null && parent instanceof ScrollView ){ 539 | ((ScrollView)parent).smoothScrollTo( 540 | 0, child.getTop() - getPaddingTop() - child.getPaddingTop()); 541 | } 542 | return; 543 | } 544 | } 545 | } 546 | 547 | public interface OnDissmissListener { 548 | public void onDismiss(String tag); 549 | } 550 | 551 | /** 552 | * Empty default AnimationListener 553 | */ 554 | private abstract class EndAnimationWrapper implements Animator.AnimatorListener { 555 | 556 | @Override 557 | public void onAnimationStart(Animator animation) { 558 | } 559 | 560 | @Override 561 | public void onAnimationCancel(Animator animation) { 562 | } 563 | 564 | @Override 565 | public void onAnimationRepeat(Animator animation) { 566 | } 567 | }//end of inner class 568 | } 569 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/CardStreamState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import java.util.HashSet; 21 | 22 | /** 23 | * A struct object that holds the state of a {@link CardStreamFragment}. 24 | */ 25 | public class CardStreamState { 26 | protected Card[] visibleCards; 27 | protected Card[] hiddenCards; 28 | protected HashSet dismissibleCards; 29 | protected String shownTag; 30 | 31 | protected CardStreamState(Card[] visible, Card[] hidden, HashSet dismissible, String shownTag) { 32 | visibleCards = visible; 33 | hiddenCards = hidden; 34 | dismissibleCards = dismissible; 35 | this.shownTag = shownTag; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/DefaultCardStreamAnimator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | import android.animation.ObjectAnimator; 21 | import android.animation.PropertyValuesHolder; 22 | import android.annotation.TargetApi; 23 | import android.content.Context; 24 | import android.graphics.Point; 25 | import android.os.Build; 26 | import android.view.View; 27 | import android.view.WindowManager; 28 | import android.view.animation.BounceInterpolator; 29 | 30 | class DefaultCardStreamAnimator extends CardStreamAnimator { 31 | 32 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 33 | @Override 34 | public ObjectAnimator getDisappearingAnimator(Context context){ 35 | 36 | ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(new Object(), 37 | PropertyValuesHolder.ofFloat("alpha", 1.f, 0.f), 38 | PropertyValuesHolder.ofFloat("scaleX", 1.f, 0.f), 39 | PropertyValuesHolder.ofFloat("scaleY", 1.f, 0.f), 40 | PropertyValuesHolder.ofFloat("rotation", 0.f, 270.f)); 41 | 42 | animator.setDuration((long) (200 * mSpeedFactor)); 43 | return animator; 44 | } 45 | 46 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 47 | @Override 48 | public ObjectAnimator getAppearingAnimator(Context context){ 49 | 50 | final Point outPoint = new Point(); 51 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 52 | wm.getDefaultDisplay().getSize(outPoint); 53 | 54 | ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(new Object(), 55 | PropertyValuesHolder.ofFloat("alpha", 0.f, 1.f), 56 | PropertyValuesHolder.ofFloat("translationY", outPoint.y / 2.f, 0.f), 57 | PropertyValuesHolder.ofFloat("rotation", -45.f, 0.f)); 58 | 59 | animator.setDuration((long) (200 * mSpeedFactor)); 60 | return animator; 61 | } 62 | 63 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 64 | @Override 65 | public ObjectAnimator getInitalAnimator(Context context){ 66 | 67 | ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(new Object(), 68 | PropertyValuesHolder.ofFloat("alpha", 0.5f, 1.f), 69 | PropertyValuesHolder.ofFloat("rotation", 60.f, 0.f)); 70 | 71 | animator.setDuration((long) (200 * mSpeedFactor)); 72 | return animator; 73 | } 74 | 75 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 76 | @Override 77 | public ObjectAnimator getSwipeInAnimator(View view, float deltaX, float deltaY){ 78 | 79 | float deltaXAbs = Math.abs(deltaX); 80 | 81 | float fractionCovered = 1.f - (deltaXAbs / view.getWidth()); 82 | long duration = Math.abs((int) ((1 - fractionCovered) * 200 * mSpeedFactor)); 83 | 84 | // Animate position and alpha of swiped item 85 | 86 | ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, 87 | PropertyValuesHolder.ofFloat("alpha", 1.f), 88 | PropertyValuesHolder.ofFloat("translationX", 0.f), 89 | PropertyValuesHolder.ofFloat("rotationY", 0.f)); 90 | 91 | animator.setDuration(duration).setInterpolator(new BounceInterpolator()); 92 | 93 | return animator; 94 | } 95 | 96 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 97 | @Override 98 | public ObjectAnimator getSwipeOutAnimator(View view, float deltaX, float deltaY){ 99 | 100 | float endX; 101 | float endRotationY; 102 | 103 | float deltaXAbs = Math.abs(deltaX); 104 | 105 | float fractionCovered = 1.f - (deltaXAbs / view.getWidth()); 106 | long duration = Math.abs((int) ((1 - fractionCovered) * 200 * mSpeedFactor)); 107 | 108 | endX = deltaX < 0 ? -view.getWidth() : view.getWidth(); 109 | if (deltaX > 0) 110 | endRotationY = -15.f; 111 | else 112 | endRotationY = 15.f; 113 | 114 | // Animate position and alpha of swiped item 115 | return ObjectAnimator.ofPropertyValuesHolder(view, 116 | PropertyValuesHolder.ofFloat("alpha", 0.f), 117 | PropertyValuesHolder.ofFloat("translationX", endX), 118 | PropertyValuesHolder.ofFloat("rotationY", endRotationY)).setDuration(duration); 119 | 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/OnCardClickListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | 18 | package com.example.android.batchstepsensor.cardstream; 19 | 20 | public interface OnCardClickListener { 21 | public void onCardClick(int cardActionId, String cardTag); 22 | } 23 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/batchstepsensor/cardstream/StreamRetentionFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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.cardstream; 18 | 19 | import android.os.Bundle; 20 | import android.support.v4.app.Fragment; 21 | 22 | public class StreamRetentionFragment extends Fragment { 23 | 24 | CardStreamState mState; 25 | 26 | @Override 27 | public void onActivityCreated(Bundle savedInstanceState) { 28 | super.onActivityCreated(savedInstanceState); 29 | setRetainInstance(true); 30 | } 31 | 32 | public void storeCardStream(CardStreamState state) { 33 | mState = state; 34 | } 35 | 36 | public CardStreamState getCardStream() { 37 | return mState; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/activities/SampleActivityBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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.common.activities; 18 | 19 | import android.os.Bundle; 20 | import android.support.v4.app.FragmentActivity; 21 | 22 | import com.example.android.common.logger.Log; 23 | import com.example.android.common.logger.LogWrapper; 24 | 25 | /** 26 | * Base launcher activity, to handle most of the common plumbing for samples. 27 | */ 28 | public class SampleActivityBase extends FragmentActivity { 29 | 30 | public static final String TAG = "SampleActivityBase"; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | } 36 | 37 | @Override 38 | protected void onStart() { 39 | super.onStart(); 40 | initializeLogging(); 41 | } 42 | 43 | /** Set up targets to receive log data */ 44 | public void initializeLogging() { 45 | // Using Log, front-end to the logging chain, emulates android.util.log method signatures. 46 | // Wraps Android's native log framework 47 | LogWrapper logWrapper = new LogWrapper(); 48 | Log.setLogNode(logWrapper); 49 | 50 | Log.i(TAG, "Ready"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/logger/Log.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 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 | package com.example.android.common.logger; 17 | 18 | /** 19 | * Helper class for a list (or tree) of LoggerNodes. 20 | * 21 | *

    When this is set as the head of the list, 22 | * an instance of it can function as a drop-in replacement for {@link android.util.Log}. 23 | * Most of the methods in this class server only to map a method call in Log to its equivalent 24 | * in LogNode.

    25 | */ 26 | public class Log { 27 | // Grabbing the native values from Android's native logging facilities, 28 | // to make for easy migration and interop. 29 | public static final int NONE = -1; 30 | public static final int VERBOSE = android.util.Log.VERBOSE; 31 | public static final int DEBUG = android.util.Log.DEBUG; 32 | public static final int INFO = android.util.Log.INFO; 33 | public static final int WARN = android.util.Log.WARN; 34 | public static final int ERROR = android.util.Log.ERROR; 35 | public static final int ASSERT = android.util.Log.ASSERT; 36 | 37 | // Stores the beginning of the LogNode topology. 38 | private static LogNode mLogNode; 39 | 40 | /** 41 | * Returns the next LogNode in the linked list. 42 | */ 43 | public static LogNode getLogNode() { 44 | return mLogNode; 45 | } 46 | 47 | /** 48 | * Sets the LogNode data will be sent to. 49 | */ 50 | public static void setLogNode(LogNode node) { 51 | mLogNode = node; 52 | } 53 | 54 | /** 55 | * Instructs the LogNode to print the log data provided. Other LogNodes can 56 | * be chained to the end of the LogNode as desired. 57 | * 58 | * @param priority Log level of the data being logged. Verbose, Error, etc. 59 | * @param tag Tag for for the log data. Can be used to organize log statements. 60 | * @param msg The actual message to be logged. 61 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 62 | * to extract and print useful information. 63 | */ 64 | public static void println(int priority, String tag, String msg, Throwable tr) { 65 | if (mLogNode != null) { 66 | mLogNode.println(priority, tag, msg, tr); 67 | } 68 | } 69 | 70 | /** 71 | * Instructs the LogNode to print the log data provided. Other LogNodes can 72 | * be chained to the end of the LogNode as desired. 73 | * 74 | * @param priority Log level of the data being logged. Verbose, Error, etc. 75 | * @param tag Tag for for the log data. Can be used to organize log statements. 76 | * @param msg The actual message to be logged. The actual message to be logged. 77 | */ 78 | public static void println(int priority, String tag, String msg) { 79 | println(priority, tag, msg, null); 80 | } 81 | 82 | /** 83 | * Prints a message at VERBOSE priority. 84 | * 85 | * @param tag Tag for for the log data. Can be used to organize log statements. 86 | * @param msg The actual message to be logged. 87 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 88 | * to extract and print useful information. 89 | */ 90 | public static void v(String tag, String msg, Throwable tr) { 91 | println(VERBOSE, tag, msg, tr); 92 | } 93 | 94 | /** 95 | * Prints a message at VERBOSE priority. 96 | * 97 | * @param tag Tag for for the log data. Can be used to organize log statements. 98 | * @param msg The actual message to be logged. 99 | */ 100 | public static void v(String tag, String msg) { 101 | v(tag, msg, null); 102 | } 103 | 104 | 105 | /** 106 | * Prints a message at DEBUG priority. 107 | * 108 | * @param tag Tag for for the log data. Can be used to organize log statements. 109 | * @param msg The actual message to be logged. 110 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 111 | * to extract and print useful information. 112 | */ 113 | public static void d(String tag, String msg, Throwable tr) { 114 | println(DEBUG, tag, msg, tr); 115 | } 116 | 117 | /** 118 | * Prints a message at DEBUG priority. 119 | * 120 | * @param tag Tag for for the log data. Can be used to organize log statements. 121 | * @param msg The actual message to be logged. 122 | */ 123 | public static void d(String tag, String msg) { 124 | d(tag, msg, null); 125 | } 126 | 127 | /** 128 | * Prints a message at INFO priority. 129 | * 130 | * @param tag Tag for for the log data. Can be used to organize log statements. 131 | * @param msg The actual message to be logged. 132 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 133 | * to extract and print useful information. 134 | */ 135 | public static void i(String tag, String msg, Throwable tr) { 136 | println(INFO, tag, msg, tr); 137 | } 138 | 139 | /** 140 | * Prints a message at INFO priority. 141 | * 142 | * @param tag Tag for for the log data. Can be used to organize log statements. 143 | * @param msg The actual message to be logged. 144 | */ 145 | public static void i(String tag, String msg) { 146 | i(tag, msg, null); 147 | } 148 | 149 | /** 150 | * Prints a message at WARN priority. 151 | * 152 | * @param tag Tag for for the log data. Can be used to organize log statements. 153 | * @param msg The actual message to be logged. 154 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 155 | * to extract and print useful information. 156 | */ 157 | public static void w(String tag, String msg, Throwable tr) { 158 | println(WARN, tag, msg, tr); 159 | } 160 | 161 | /** 162 | * Prints a message at WARN priority. 163 | * 164 | * @param tag Tag for for the log data. Can be used to organize log statements. 165 | * @param msg The actual message to be logged. 166 | */ 167 | public static void w(String tag, String msg) { 168 | w(tag, msg, null); 169 | } 170 | 171 | /** 172 | * Prints a message at WARN priority. 173 | * 174 | * @param tag Tag for for the log data. Can be used to organize log statements. 175 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 176 | * to extract and print useful information. 177 | */ 178 | public static void w(String tag, Throwable tr) { 179 | w(tag, null, tr); 180 | } 181 | 182 | /** 183 | * Prints a message at ERROR priority. 184 | * 185 | * @param tag Tag for for the log data. Can be used to organize log statements. 186 | * @param msg The actual message to be logged. 187 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 188 | * to extract and print useful information. 189 | */ 190 | public static void e(String tag, String msg, Throwable tr) { 191 | println(ERROR, tag, msg, tr); 192 | } 193 | 194 | /** 195 | * Prints a message at ERROR priority. 196 | * 197 | * @param tag Tag for for the log data. Can be used to organize log statements. 198 | * @param msg The actual message to be logged. 199 | */ 200 | public static void e(String tag, String msg) { 201 | e(tag, msg, null); 202 | } 203 | 204 | /** 205 | * Prints a message at ASSERT priority. 206 | * 207 | * @param tag Tag for for the log data. Can be used to organize log statements. 208 | * @param msg The actual message to be logged. 209 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 210 | * to extract and print useful information. 211 | */ 212 | public static void wtf(String tag, String msg, Throwable tr) { 213 | println(ASSERT, tag, msg, tr); 214 | } 215 | 216 | /** 217 | * Prints a message at ASSERT priority. 218 | * 219 | * @param tag Tag for for the log data. Can be used to organize log statements. 220 | * @param msg The actual message to be logged. 221 | */ 222 | public static void wtf(String tag, String msg) { 223 | wtf(tag, msg, null); 224 | } 225 | 226 | /** 227 | * Prints a message at ASSERT priority. 228 | * 229 | * @param tag Tag for for the log data. Can be used to organize log statements. 230 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 231 | * to extract and print useful information. 232 | */ 233 | public static void wtf(String tag, Throwable tr) { 234 | wtf(tag, null, tr); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/logger/LogFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | * Copyright 2013 The Android Open Source Project 18 | * 19 | * Licensed under the Apache License, Version 2.0 (the "License"); 20 | * you may not use this file except in compliance with the License. 21 | * You may obtain a copy of the License at 22 | * 23 | * http://www.apache.org/licenses/LICENSE-2.0 24 | * 25 | * Unless required by applicable law or agreed to in writing, software 26 | * distributed under the License is distributed on an "AS IS" BASIS, 27 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | * See the License for the specific language governing permissions and 29 | * limitations under the License. 30 | */ 31 | 32 | package com.example.android.common.logger; 33 | 34 | import android.graphics.Typeface; 35 | import android.os.Bundle; 36 | import android.support.v4.app.Fragment; 37 | import android.text.Editable; 38 | import android.text.TextWatcher; 39 | import android.view.Gravity; 40 | import android.view.LayoutInflater; 41 | import android.view.View; 42 | import android.view.ViewGroup; 43 | import android.widget.ScrollView; 44 | 45 | /** 46 | * Simple fraggment which contains a LogView and uses is to output log data it receives 47 | * through the LogNode interface. 48 | */ 49 | public class LogFragment extends Fragment { 50 | 51 | private LogView mLogView; 52 | private ScrollView mScrollView; 53 | 54 | public LogFragment() {} 55 | 56 | public View inflateViews() { 57 | mScrollView = new ScrollView(getActivity()); 58 | ViewGroup.LayoutParams scrollParams = new ViewGroup.LayoutParams( 59 | ViewGroup.LayoutParams.MATCH_PARENT, 60 | ViewGroup.LayoutParams.MATCH_PARENT); 61 | mScrollView.setLayoutParams(scrollParams); 62 | 63 | mLogView = new LogView(getActivity()); 64 | ViewGroup.LayoutParams logParams = new ViewGroup.LayoutParams(scrollParams); 65 | logParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; 66 | mLogView.setLayoutParams(logParams); 67 | mLogView.setClickable(true); 68 | mLogView.setFocusable(true); 69 | mLogView.setTypeface(Typeface.MONOSPACE); 70 | 71 | // Want to set padding as 16 dips, setPadding takes pixels. Hooray math! 72 | int paddingDips = 16; 73 | double scale = getResources().getDisplayMetrics().density; 74 | int paddingPixels = (int) ((paddingDips * (scale)) + .5); 75 | mLogView.setPadding(paddingPixels, paddingPixels, paddingPixels, paddingPixels); 76 | mLogView.setCompoundDrawablePadding(paddingPixels); 77 | 78 | mLogView.setGravity(Gravity.BOTTOM); 79 | mLogView.setTextAppearance(getActivity(), android.R.style.TextAppearance_Holo_Medium); 80 | 81 | mScrollView.addView(mLogView); 82 | return mScrollView; 83 | } 84 | 85 | @Override 86 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 87 | Bundle savedInstanceState) { 88 | 89 | View result = inflateViews(); 90 | 91 | mLogView.addTextChangedListener(new TextWatcher() { 92 | @Override 93 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 94 | 95 | @Override 96 | public void onTextChanged(CharSequence s, int start, int before, int count) {} 97 | 98 | @Override 99 | public void afterTextChanged(Editable s) { 100 | mScrollView.fullScroll(ScrollView.FOCUS_DOWN); 101 | } 102 | }); 103 | return result; 104 | } 105 | 106 | public LogView getLogView() { 107 | return mLogView; 108 | } 109 | } -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/logger/LogNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 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 | package com.example.android.common.logger; 17 | 18 | /** 19 | * Basic interface for a logging system that can output to one or more targets. 20 | * Note that in addition to classes that will output these logs in some format, 21 | * one can also implement this interface over a filter and insert that in the chain, 22 | * such that no targets further down see certain data, or see manipulated forms of the data. 23 | * You could, for instance, write a "ToHtmlLoggerNode" that just converted all the log data 24 | * it received to HTML and sent it along to the next node in the chain, without printing it 25 | * anywhere. 26 | */ 27 | public interface LogNode { 28 | 29 | /** 30 | * Instructs first LogNode in the list to print the log data provided. 31 | * @param priority Log level of the data being logged. Verbose, Error, etc. 32 | * @param tag Tag for for the log data. Can be used to organize log statements. 33 | * @param msg The actual message to be logged. The actual message to be logged. 34 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 35 | * to extract and print useful information. 36 | */ 37 | public void println(int priority, String tag, String msg, Throwable tr); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/logger/LogView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 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 | package com.example.android.common.logger; 17 | 18 | import android.app.Activity; 19 | import android.content.Context; 20 | import android.util.*; 21 | import android.widget.TextView; 22 | 23 | /** Simple TextView which is used to output log data received through the LogNode interface. 24 | */ 25 | public class LogView extends TextView implements LogNode { 26 | 27 | public LogView(Context context) { 28 | super(context); 29 | } 30 | 31 | public LogView(Context context, AttributeSet attrs) { 32 | super(context, attrs); 33 | } 34 | 35 | public LogView(Context context, AttributeSet attrs, int defStyle) { 36 | super(context, attrs, defStyle); 37 | } 38 | 39 | /** 40 | * Formats the log data and prints it out to the LogView. 41 | * @param priority Log level of the data being logged. Verbose, Error, etc. 42 | * @param tag Tag for for the log data. Can be used to organize log statements. 43 | * @param msg The actual message to be logged. The actual message to be logged. 44 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 45 | * to extract and print useful information. 46 | */ 47 | @Override 48 | public void println(int priority, String tag, String msg, Throwable tr) { 49 | 50 | 51 | String priorityStr = null; 52 | 53 | // For the purposes of this View, we want to print the priority as readable text. 54 | switch(priority) { 55 | case android.util.Log.VERBOSE: 56 | priorityStr = "VERBOSE"; 57 | break; 58 | case android.util.Log.DEBUG: 59 | priorityStr = "DEBUG"; 60 | break; 61 | case android.util.Log.INFO: 62 | priorityStr = "INFO"; 63 | break; 64 | case android.util.Log.WARN: 65 | priorityStr = "WARN"; 66 | break; 67 | case android.util.Log.ERROR: 68 | priorityStr = "ERROR"; 69 | break; 70 | case android.util.Log.ASSERT: 71 | priorityStr = "ASSERT"; 72 | break; 73 | default: 74 | break; 75 | } 76 | 77 | // Handily, the Log class has a facility for converting a stack trace into a usable string. 78 | String exceptionStr = null; 79 | if (tr != null) { 80 | exceptionStr = android.util.Log.getStackTraceString(tr); 81 | } 82 | 83 | // Take the priority, tag, message, and exception, and concatenate as necessary 84 | // into one usable line of text. 85 | final StringBuilder outputBuilder = new StringBuilder(); 86 | 87 | String delimiter = "\t"; 88 | appendIfNotNull(outputBuilder, priorityStr, delimiter); 89 | appendIfNotNull(outputBuilder, tag, delimiter); 90 | appendIfNotNull(outputBuilder, msg, delimiter); 91 | appendIfNotNull(outputBuilder, exceptionStr, delimiter); 92 | 93 | // In case this was originally called from an AsyncTask or some other off-UI thread, 94 | // make sure the update occurs within the UI thread. 95 | ((Activity) getContext()).runOnUiThread( (new Thread(new Runnable() { 96 | @Override 97 | public void run() { 98 | // Display the text we just generated within the LogView. 99 | appendToLog(outputBuilder.toString()); 100 | } 101 | }))); 102 | 103 | if (mNext != null) { 104 | mNext.println(priority, tag, msg, tr); 105 | } 106 | } 107 | 108 | public LogNode getNext() { 109 | return mNext; 110 | } 111 | 112 | public void setNext(LogNode node) { 113 | mNext = node; 114 | } 115 | 116 | /** Takes a string and adds to it, with a separator, if the bit to be added isn't null. Since 117 | * the logger takes so many arguments that might be null, this method helps cut out some of the 118 | * agonizing tedium of writing the same 3 lines over and over. 119 | * @param source StringBuilder containing the text to append to. 120 | * @param addStr The String to append 121 | * @param delimiter The String to separate the source and appended strings. A tab or comma, 122 | * for instance. 123 | * @return The fully concatenated String as a StringBuilder 124 | */ 125 | private StringBuilder appendIfNotNull(StringBuilder source, String addStr, String delimiter) { 126 | if (addStr != null) { 127 | if (addStr.length() == 0) { 128 | delimiter = ""; 129 | } 130 | 131 | return source.append(addStr).append(delimiter); 132 | } 133 | return source; 134 | } 135 | 136 | // The next LogNode in the chain. 137 | LogNode mNext; 138 | 139 | /** Outputs the string as a new line of log data in the LogView. */ 140 | public void appendToLog(String s) { 141 | append("\n" + s); 142 | } 143 | 144 | 145 | } 146 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/logger/LogWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 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 | package com.example.android.common.logger; 17 | 18 | import android.util.Log; 19 | 20 | /** 21 | * Helper class which wraps Android's native Log utility in the Logger interface. This way 22 | * normal DDMS output can be one of the many targets receiving and outputting logs simultaneously. 23 | */ 24 | public class LogWrapper implements LogNode { 25 | 26 | // For piping: The next node to receive Log data after this one has done its work. 27 | private LogNode mNext; 28 | 29 | /** 30 | * Returns the next LogNode in the linked list. 31 | */ 32 | public LogNode getNext() { 33 | return mNext; 34 | } 35 | 36 | /** 37 | * Sets the LogNode data will be sent to.. 38 | */ 39 | public void setNext(LogNode node) { 40 | mNext = node; 41 | } 42 | 43 | /** 44 | * Prints data out to the console using Android's native log mechanism. 45 | * @param priority Log level of the data being logged. Verbose, Error, etc. 46 | * @param tag Tag for for the log data. Can be used to organize log statements. 47 | * @param msg The actual message to be logged. The actual message to be logged. 48 | * @param tr If an exception was thrown, this can be sent along for the logging facilities 49 | * to extract and print useful information. 50 | */ 51 | @Override 52 | public void println(int priority, String tag, String msg, Throwable tr) { 53 | // There actually are log methods that don't take a msg parameter. For now, 54 | // if that's the case, just convert null to the empty string and move on. 55 | String useMsg = msg; 56 | if (useMsg == null) { 57 | useMsg = ""; 58 | } 59 | 60 | // If an exeption was provided, convert that exception to a usable string and attach 61 | // it to the end of the msg method. 62 | if (tr != null) { 63 | msg += "\n" + Log.getStackTraceString(tr); 64 | } 65 | 66 | // This is functionally identical to Log.x(tag, useMsg); 67 | // For instance, if priority were Log.VERBOSE, this would be the same as Log.v(tag, useMsg) 68 | Log.println(priority, tag, useMsg); 69 | 70 | // If this isn't the last node in the chain, move things along. 71 | if (mNext != null) { 72 | mNext.println(priority, tag, msg, tr); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Application/src/main/java/com/example/android/common/logger/MessageOnlyLogFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 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 | package com.example.android.common.logger; 17 | 18 | /** 19 | * Simple {@link LogNode} filter, removes everything except the message. 20 | * Useful for situations like on-screen log output where you don't want a lot of metadata displayed, 21 | * just easy-to-read message updates as they're happening. 22 | */ 23 | public class MessageOnlyLogFilter implements LogNode { 24 | 25 | LogNode mNext; 26 | 27 | /** 28 | * Takes the "next" LogNode as a parameter, to simplify chaining. 29 | * 30 | * @param next The next LogNode in the pipeline. 31 | */ 32 | public MessageOnlyLogFilter(LogNode next) { 33 | mNext = next; 34 | } 35 | 36 | public MessageOnlyLogFilter() { 37 | } 38 | 39 | @Override 40 | public void println(int priority, String tag, String msg, Throwable tr) { 41 | if (mNext != null) { 42 | getNext().println(Log.NONE, null, msg, null); 43 | } 44 | } 45 | 46 | /** 47 | * Returns the next LogNode in the chain. 48 | */ 49 | public LogNode getNext() { 50 | return mNext; 51 | } 52 | 53 | /** 54 | * Sets the LogNode data will be sent to.. 55 | */ 56 | public void setNext(LogNode node) { 57 | mNext = node; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable-hdpi/ic_action_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-hdpi/ic_action_cancel.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-hdpi/tile.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-hdpi/tile.9.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-mdpi/ic_action_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-mdpi/ic_action_cancel.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-v21/card_action_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable-v21/card_action_bg_negative.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable-v21/card_action_bg_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/card_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/card_bg.9.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_cardaction_negative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_cardaction_negative.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_cardaction_negative_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_cardaction_negative_pressed.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_cardaction_neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_cardaction_neutral.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_cardaction_neutral_pressed.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_cardaction_positive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_cardaction_positive.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_cardaction_positive_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_cardaction_positive_pressed.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xxhdpi/ic_action_cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xxhdpi/ic_action_cancel.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/Application/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_bg_negative.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_bg_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_icon_negative.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_icon_neutral.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_icon_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_text_negative.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_action_text_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_overlay_focused.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | -------------------------------------------------------------------------------- /Application/src/main/res/drawable/card_separator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/card.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | 25 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 51 | 52 | 58 | 59 | 60 | 61 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/card_button_negative.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/card_button_neutral.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/card_button_positive.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/card_button_seperator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/card_progress.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 15 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /Application/src/main/res/layout/cardstream.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Application/src/main/res/values-sw600dp/template-dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | @dimen/margin_huge 22 | @dimen/margin_medium 23 | 24 | 25 | -------------------------------------------------------------------------------- /Application/src/main/res/values-sw600dp/template-styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Application/src/main/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 128dp 5 | 6 | -------------------------------------------------------------------------------- /Application/src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Application/src/main/res/values-v11/template-styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Application/src/main/res/values-v16/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /Application/src/main/res/values-v21/base-colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Application/src/main/res/values-v21/base-template-styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Application/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Application/src/main/res/values/base-strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | BatchStepSensor 20 | 21 | This sample demonstrates the use of the two step sensors (step detector and counter) and 25 | sensor batching.

    26 |

    It shows how to register a SensorEventListener with and without 27 | batching and shows how these events are received.

    28 |

    The Step Detector sensor fires an 29 | event when a step is detected, while the step counter returns the total number of 30 | steps since a listener was first registered for this sensor. 31 | Both sensors only count steps while a listener is registered. This sample only covers the 32 | basic case, where a listener is only registered while the app is running. Likewise, 33 | batched sensors can be used in the background (when the CPU is suspended), which 34 | requires manually flushing the sensor event queue before it overflows, which is not 35 | covered in this sample.

    36 | 37 | 38 | ]]> 39 |
    40 |
    41 | -------------------------------------------------------------------------------- /Application/src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @android:color/white 5 | 6 | #444 7 | 8 | #DDD 9 | #F4F4F4 10 | 11 | 12 | #FFE3F4FC 13 | #FF47B4EA 14 | #CC47B4EA 15 | 16 | 17 | #FFFBCBCA 18 | #FFF64940 19 | #CCF64940 20 | 21 | 22 | #FFE4F0AF 23 | #FFA0CC00 24 | #CCA0CC00 25 | 26 | -------------------------------------------------------------------------------- /Application/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14sp 4 | 24sp 5 | 6 | 15dp 7 | 10dp 8 | 9 | 3dp 10 | 8dp 11 | 12 | 90dp 13 | 14 | 15 | -------------------------------------------------------------------------------- /Application/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Application/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | Introduction 20 | 21 | Background sensor batching 22 | Batching allows the sensor to report sensor events at 23 | a specified frequency.\n\nThe system delays calls to the SensorEventListener and deliver 24 | them in intervals, based on the maximum report latency specified when the listener is 25 | registered. Note that this only means that the call to onSensorChanged() is delayed, the 26 | total number of calls is identical as if no batching was used. Sensors only deliver events 27 | while the CPU is awake. If the CPU is asleep and a batched sensor event listener is still 28 | registered, the sensor will continue to collect events until it runs out of memory and 29 | overwrites old values. This use case is not covered by this sample. (The sensor event queue 30 | should be flushed using a scheduled background thread.) \n\nIn this sample app data is only 31 | collected while the app is running and the CPU is awake. In this case the sensor will 32 | deliver events before the queue fills up. 33 | 34 | 35 | The age of a sensor event describes the delay between 36 | when it was recorded by the sensor until it was delivered to the SensorEventListener. 37 | 38 | 39 | Register step detector sensor 40 | Register a listener for the STEP DETECTOR 41 | sensor.\n\nThis sensor delivers an event when the user takes a step. One event is received 42 | per step. 43 | 44 | 45 | Register step counter sensor 46 | Register a listener for the STEP COUNTER 47 | sensor.\n\nThis sensor triggers events when a step is detected, but applies algorithms to 48 | filter out false positives. Events from this sensor have higher latency than the step 49 | detector and contain the total number of steps taken since the sensor was first registered. 50 | 51 | 52 | No batching (delay=0) 53 | 5s batching (delay=5000ms) 54 | 10s batching (delay=10000ms) 55 | 56 | Total Steps: %1$d 57 | Step Counter 58 | Step Detector 59 | Sensor: %1$s\nMax sensor event delay: %2$,d \u00B5s\nAge of 60 | events in s:\n%3$s 61 | 62 | 63 | Error 64 | This sample requires at least Android KitKat (4.4) and a device 65 | with the step sensor.\n\nThis device does not appear to meet these requirements, as an 66 | alternative you may want to consider using the gyro sensor and implement your own step 67 | recognition as a fallback. 68 | 69 | The listener has been registered, but batch mode could not be 70 | enabled.\n\nIt is likely that it is not supported by this device.\n\nSensor events will be 71 | delivered in continuous mode. 72 | 73 | 74 | Do not show again 75 | -------------------------------------------------------------------------------- /Application/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 15 | 16 | 19 | 20 | 25 | 26 | 30 | 31 | 32 | 36 | 37 | 39 | 40 | 45 | 46 | 48 | 49 | 50 | 58 | 59 | 64 | 65 | 70 | 71 | 76 | 77 | 78 | 85 | 86 | 91 | 92 | -------------------------------------------------------------------------------- /Application/src/main/res/values/template-dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 4dp 22 | 8dp 23 | 16dp 24 | 32dp 25 | 64dp 26 | 27 | 28 | 29 | @dimen/margin_medium 30 | @dimen/margin_medium 31 | 32 | 33 | -------------------------------------------------------------------------------- /Application/src/main/res/values/template-styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 34 | 35 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /CONTRIB.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement (CLA). 9 | 10 | * If you are an individual writing original source code and you're sure you 11 | own the intellectual property, then you'll need to sign an [individual CLA] 12 | (https://developers.google.com/open-source/cla/individual). 13 | * If you work for a company that wants to allow you to contribute your work, 14 | then you'll need to sign a [corporate CLA] 15 | (https://developers.google.com/open-source/cla/corporate). 16 | 17 | Follow either of the two links above to access the appropriate CLA and 18 | instructions for how to sign and return it. Once we receive it, we'll be able to 19 | accept your pull requests. 20 | 21 | ## Contributing A Patch 22 | 23 | 1. Submit an issue describing your proposed change to the repo in question. 24 | 1. The repo owner will respond to your issue promptly. 25 | 1. If your proposed change is accepted, and you haven't already done so, sign a 26 | Contributor License Agreement (see details above). 27 | 1. Fork the desired repo, develop and test your code changes. 28 | 1. Ensure that your code adheres to the existing style in the sample to which 29 | you are contributing. Refer to the 30 | [Android Code Style Guide] 31 | (https://source.android.com/source/code-style.html) for the 32 | recommended coding standards for this organization. 33 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 34 | 1. Submit a pull request. 35 | 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement (CLA). 9 | 10 | * If you are an individual writing original source code and you're sure you 11 | own the intellectual property, then you'll need to sign an [individual CLA] 12 | (https://cla.developers.google.com). 13 | * If you work for a company that wants to allow you to contribute your work, 14 | then you'll need to sign a [corporate CLA] 15 | (https://cla.developers.google.com). 16 | 17 | Follow either of the two links above to access the appropriate CLA and 18 | instructions for how to sign and return it. Once we receive it, we'll be able to 19 | accept your pull requests. 20 | 21 | ## Contributing A Patch 22 | 23 | 1. Submit an issue describing your proposed change to the repo in question. 24 | 1. The repo owner will respond to your issue promptly. 25 | 1. If your proposed change is accepted, and you haven't already done so, sign a 26 | Contributor License Agreement (see details above). 27 | 1. Fork the desired repo, develop and test your code changes. 28 | 1. Ensure that your code adheres to the existing style in the sample to which 29 | you are contributing. Refer to the 30 | [Android Code Style Guide] 31 | (https://source.android.com/source/code-style.html) for the 32 | recommended coding standards for this organization. 33 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 34 | 1. Submit a pull request. 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | -------------- 3 | 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "{}" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright {yyyy} {name of copyright owner} 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Android BatchStepSensor Sample 3 | ============================== 4 | 5 | This repo has been migrated to [github.com/android/sensors][1]. Please check that repo for future updates. Thank you! 6 | 7 | [1]: https://github.com/android/sensors 8 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /packaging.yaml: -------------------------------------------------------------------------------- 1 | # GOOGLE SAMPLE PACKAGING DATA 2 | # 3 | # This file is used by Google as part of our samples packaging process. 4 | # End users may safely ignore this file. It has no relevance to other systems. 5 | --- 6 | 7 | status: PUBLISHED 8 | technologies: [Android] 9 | categories: [Sensors] 10 | languages: [Java] 11 | solutions: [Mobile] 12 | github: googlesamples/android-BatchStepSensor 13 | level: BEGINNER 14 | icon: BatchStepSensorSample/screenshots/big_icon.png 15 | license: apache2-android 16 | -------------------------------------------------------------------------------- /screenshots/big_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/screenshots/big_icon.png -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/screenshots/screenshot2.png -------------------------------------------------------------------------------- /screenshots/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/screenshots/screenshot3.png -------------------------------------------------------------------------------- /screenshots/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/screenshots/screenshot4.png -------------------------------------------------------------------------------- /screenshots/screenshot5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/googlearchive/android-BatchStepSensor/28c93504fd62abac04c0fb8cc152c423713f85f6/screenshots/screenshot5.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'Application' 2 | --------------------------------------------------------------------------------