of 112
PROJECT FORMAT
PROJECT FORMATABSTRACT
INTRODUCTION
FEASIBILITY STUDY
SYSTEM ANALYSIS
Existing system
Proposed system
SYSTEM REQUIREMENTS
Software requirements
Hardware requirements
DATA FLOW DIAGRAMS
ABOUT THE LANGUAGE (FRONT-END AND BACK-END)
SOURCE CODE (its in Microsoft word for documentation)
SCREEN SHOTS (same as above)
TESTING
MAINTENANCE
CONCLUSION
FUTURE ENHANCEMENTS
BIBLIOGRAPHY (related reference books and related project web sites).
ABSTRACT
Abstract:
In this project we intended to create an apps in android which will help us to track the location and change of location while a person carrying a smart phone commute from one place to another within a closed environment. This is intended to develop using the internal sensors embedded in the smart phones. This project will use geometric field sensors API, position sensors API, proximity sensors, motion sensors and accelerometer which are all coming with android to enable as GPS (Global Positioning System) navigation systems will be smartly utilized to detect the current position and in case of changing position into an vector and calculating the speed of monitor using the distance, displace and time taken for that displacement.
This will be base for several projects which can be derived from this base application such as unambiguous pedestrian tracking and blind people in house guidance application etc This navel method of existing APIs and unambiguous algorithm will pave the way for developed and innovation of many project in future.
INTRODUCTION
This project is developed to create new apps that always run in the background, counting our steps, tracking our location, or listening to us as we sleep. These tasks require that a process remains in an active state, and whenever there's any activity going on within a smartphone, battery life takes a hit. Android 4.4 reduces the impact of these processes with new support for hardware sensor batching.
This optimization allows Android to collect and deliver sensor events in clumps, rather than keep track of them individually. Think of this as the difference between ordering one T-shirt and ordering a thousand. Just as producing a product in bulk is easier on the manufacturer, sending bite-sized tracking data in batches puts less of a strain on your phone, as it reduces how often it has to wake up from an idle state.
New Version of KitKat also adds platform support for two new sensors,step detectorandstep counter.The former can recognize when a user takes a step and trigger an event as a result, while the latter tracks the total number of steps taken since the last device reboot. Since these functions are now implemented into the platform and underlying hardware, we are using this functionality of Kitkat to develop the project to identify the motion detection and location identification using the process called batch step sensor.
Android sensorsgive applications access to a mobile device's underlying base sensor(s): accelerometer, gyroscope, and magnetometer. Manufacturers develop the drivers that define additional composite sensor types from those base sensors. For instance, Android offers both calibrated and uncalibrated gyroscopes, a geomagnetic rotation vector and a game rotation vector. This variety gives developers some flexibility in tuning applications for battery life optimization and accuracy. We effectively use these facility of android and ensuring the deduction of steps and speed in this project which can be the base for future innovative applications based on the sensors.
FEASIBILITY STUDYAs we have discussed in the introduction this project is developed based on the functionalities of Android OS and Hardware of smart phones, this is theoretically feasibility is observed and we intended to introspect whether our postulates and hypothesis about the construction of the project. On throw study on this the following finding were evolved.Technical FeasibilityThis Application needs an android phone with sensors and which can work on Android 4.4 Kitkat OS. This project can be developed and built as an application using the ADT Studio which supports Kitkat OS. Even though the ADT is available the simulators can be used to certain extend only due to lack of sensor hardware and simulation of motion in the simulator. So we have decided to develop sensor simulator in java to test the functionality of sensor in the OS. Once it is successful it is understood that we can implement this process of Motion deduction using batch step process. So it is decided that there is a technical feasibility for this project with its own limitation.
The limitations are only during the development and testing because of the absence of hardware sensors in the emulators and the another limitation that this can be developed only in Android Kitkat OS.
SYSTEM ANALYSISExisting SystemThe Existing Sensor based projects are based on real-time running of application directly interact with the hardware and having processor intensive , memory intensive, as well as consumption of battery to the grater extend. Which is very critical for the smart phone? All the application which will be running in foreground and keep on detecting the activities whether the activity is happening or not. Also the hardware need to be controlled or monitored and signal capturing all need to be controlled using hardcore programming due to lack of APIs. In non-batch mode, all sensor events must be reported as soon as they are detected. For example, an accelerometer activated at 50Hz will trigger interrupts 50 times per second. This makes the application processor time intensive and efficiency may drop down drastically. To over come this limitation The batch mode tracking of sensors is utilized.
Proposed System While in batch mode, sensor events do not need to be reported as soon as they are detected. They can be temporarily stored and reported in batches, as long as no event is delayed by more than maxReportingLatency nanoseconds. That is, all events since the previous batch are recorded and returned at once. This reduces the amount of interrupts sent to the SoC and allows the SoC to switch to a lower power mode (idle) while the sensor is capturing and batching data.
nabling batch mode for a given sensor sets the delay between events. max_report_latency sets the maximum time by which events can be delayed and batched together before being reported to the applications. A value of zero disables batch mode for the given sensor. The period_ns parameter is equivalent to calling setDelay() -- this function both enables or disables the batch mode AND sets the event's period in nanoseconds. See setDelay() for a detailed explanation of the period_ns parameter.
These are the power modes of the application processor: on, idle, and suspend. The sensors behave differently in each of these modes. As you would imagine, on mode is when the application processor is running. Idle mode is a medium power mode where the application processor is powered but doesn't perform any tasks. Suspend is a low-power mode where the application processor is not powered. The power consumption of the device in this mode is usually 100 times less than in the On mode.
SYSTEM REQUIREMENTSHARDWRE REQUIRMENTS
For Development :
PC with Intel Duel Core Processor
Minimum of 4 GB RAM
500 GB HDD
VGA Card With Built in Video RAM For Testing
Tablet PC or Smart phone with Sensors which adhering to th standards of Nexus 5 or aboveSoftware RequirementsEither Windows 7 or Ubuntu 13
Software Development
Android ADK Studio
Android Development Kit Bundle 4.4 or later
Android OS 4.41 KIT KAT
+
State machine for the accelerometer x-axis data when the device is placed on a flat surface. Accelerometer data coordinate system in androidControl Flow Diagram
Use Case Diagram for Batch Step process
About the Software
As this project used device level programming there is no concept of back end database used in this Application We have used android OS and and Developed the application using JAVA.Android is an operating system based on the Linux kernel. The project responsible for developing the Android system is called the Android Open Source Project (AOSP) and is primarily lead by Google. The Android system supports background processing, provides a rich user interface library, supports 2-D and 3-D graphics using the OpenGL-ES (short OpenGL) standard and grants access to the file system as well as an embedded SQLite database An Android application typically consists of different visual and non visual components and can reuse components of other applications.
The Android system is a full software stack, which is typically divided into the four areas as depicted in the following graphic.
The levels can be described as:
Applications - The Android Open Source Project contains several default application, like the Browser, Camera, Gallery, Music, Phone and more.
Application framework - An API which allows high-level interactions with the Android system from Android applications.
Libraries and runtime - The libraries for many common functions (e.g.: graphic rendering, data storage, web browsing, etc.) of the Application Framework and the Dalvik runtime, as well as the core Java libraries for running Android applications.
Linux kernel - Communication layer for the underlying hardware.
The Linux kernel, the libraries and the runtime are encapsulated by the application framework. The Android application developer typically works with the two layers on top to create new Android applications.
Android Development Tools
Android SDK
The Android Software Development Kit (Android SDK) contains the necessary tools to create, compile and package Android applications. Most of these tools are command line based. The primary way to develop Android applications is based on the Java programming language.
Android debug bridge (adb)
The Android SDK contains the Android debug bridge (adb), which is a tool that allows you to connect to a virtual or real Android device, for the purpose of managing the device or debugging your application.
Android Developer Tools and Android Studio
Google provides two integrated development environments (IDEs) to develop new applications.
The Android Developer Tools (ADT) are based on the Eclipse IDE. ADT is a set of components (plug-ins), which extend the Eclipse IDE with Android development capabilities.
Google also supports an IDE called Android Studio for creating Android applications. This IDE is based on the IntelliJ IDE.
Both IDEs contain all required functionality to create, compile, debug and deploy Android applications. They also allow the developer to create and start virtual Android devices for testing.
Both tools provide specialized editors for Android specific files. Most of Android's configuration files are based on XML. In this case these editors allow you to switch between the XML representation of the file and a structured user interface for entering the data.
Dalvik Virtual Machine
The Android system uses a special virtual machine, i.e., the Dalvik Virtual Machine (Dalvik) to run Java based applications. Dalvik uses a custom bytecode format which is different from Java bytecode.
Therefore you cannot run Java class files on Android directly; they need to be converted into the Dalvik bytecode format.
Just in time compiler on Dalvik
Similar to the JVM, Dalvik optimizes the application at runtime. This is known as Just In Time (JIT) compilation. If a part of the application is called frequently, Dalvik will optimize this part of the code and compile it into machine code which executes much faster.
Android RunTime (ART)
With Android 4.4, Google introduced the Android RunTime (ART) as optional runtime for Android 4.4. It is expected that versions after 4.4 will use ART as default runtime.
ART uses Ahead Of Time compilation. During the deployment process of an application on an Android device, the application code is translated into machine code. This results in approx. 30% larger compile code, but allows faster execution from the beginning of the application.
How to develop Android applications
Android applications are primarily written in the Java programming language.
During development the developer creates the Android specific configuration files and writes the application logic in the Java programming language.
The ADT or the Android Studio tools convert these application files, transparently to the user, into an Android application. When developers trigger the deployment in their IDE, the whole Android application is compiled, packaged, deployed and started.
Conversion process from source code to Android application
The Java source files are converted to Java class files by the Java compiler.
The Android SDK contains a tool called dx which converts Java class files into a .dex (Dalvik Executable) file. All class files of the application are placed in this .dex file. During this conversion process redundant information in the class files are optimized in the .dex file.
For example, if the same String is found in different class files, the .dex file contains only one reference of this String.
These .dex files are therefore much smaller in size than the corresponding class files.
The .dex file and the resources of an Android project, e.g., the images and XML files, are packed into an .apk (Android Package) file. The program aapt (Android Asset Packaging Tool) performs this step.
The resulting .apk file contains all necessary data to run the Android application and can be deployed to an Android device via the adb tool.
5.Android device emulator and Android Virtual Devices Android emulator and Android Virtual Device
The Android SDK contains an Android device emulator. This emulator can be used to run an Android Virtual Device (AVD), which emulates a real Android phone. Such an emulator is displayed in the following screenshot.
AVDs allow you to test your Android applications on different Android versions and configurations without access to the real hardware.
During the creation of your AVD you define the configuration for the virtual device. This includes, for example, the resolution, the Android API version and the density of your display.
You can define multiple AVDs with different configurations and start them in parallel. This allows you to test different device configurations at once. Project Outline
BatchStepSensor
BatchStepSensor
AndroidManifest.xml
res
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
drawable-hdpi
ic_action_cancel.png
ic_launcher.png
tile.9.png
drawable-mdpi
ic_action_cancel.png
ic_launcher.png
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
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
attrs.xml
base-strings.xml
color.xml
dimens.xml
ids.xml
strings.xml
styles.xml
template-dimens.xml
template-styles.xml
values-sw600dp
template-dimens.xml
template-styles.xml
values-sw720dp-land
dimens.xml
values-v11
styles.xml
values-v14
styles.xml
values-v16
styles.xml
src
com.example.android.batchstepsensor
BatchStepSensorFragment.java
Card.java
CardActionButton.java
CardLayout.java
CardStream.java
CardStreamAnimator.java
CardStreamFragment.java
CardStreamLinearLayout.java
CardStreamState.java
DefaultCardStreamAnimator.java
MainActivity.java
OnCardClickListener.java
StreamRetentionFragment.java
com.example.android.common
activities
SampleActivityBase.java
logger
Log.java
LogFragment.java
LogNode.java
LogView.java
LogWrapper.java
MessageOnlyLogFilter.java
Source Code:
MainActivity.java
package com.example.android.batchstepsensor;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.Menu;
import com.example.android.common.activities.SampleActivityBase;
import com.example.android.common.logger.Log;
public class MainActivity extends SampleActivityBase implements CardStream {
public static final String TAG = "MainActivity";
public static final String FRAGTAG = "BatchStepSensorFragment";
private CardStreamFragment mCardStreamFragment;
private StreamRetentionFragment mRetentionFragment;
private static final String RETENTION_TAG = "retention";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
BatchStepSensorFragment fragment =
(BatchStepSensorFragment) fm.findFragmentByTag(FRAGTAG);
if (fragment == null) {
FragmentTransaction transaction = fm.beginTransaction();
fragment = new BatchStepSensorFragment();
transaction.add(fragment, FRAGTAG);
transaction.commit();
}
// Use fragment as click listener for cards, but must implement correct interface
if(!(fragment instanceof OnCardClickListener)){
throw new ClassCastException("BatchStepSensorFragment must " +
"implement OnCardClickListener interface.");
}
OnCardClickListener clickListener = (OnCardClickListener) fm.findFragmentByTag(FRAGTAG);
mRetentionFragment = (StreamRetentionFragment) fm.findFragmentByTag(RETENTION_TAG);
if (mRetentionFragment == null) {
mRetentionFragment = new StreamRetentionFragment();
fm.beginTransaction().add(mRetentionFragment, RETENTION_TAG).commit();
} else {
// If the retention fragment already existed, we need to pull some state.
// pull state out
CardStreamState state = mRetentionFragment.getCardStream();
// dump it in CardStreamFragment.
mCardStreamFragment =
(CardStreamFragment) fm.findFragmentById(R.id.fragment_cardstream);
mCardStreamFragment.restoreState(state, clickListener);
}
}
public CardStreamFragment getCardStream() {
if (mCardStreamFragment == null) {
mCardStreamFragment = (CardStreamFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_cardstream);
}
return mCardStreamFragment;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
CardStreamState state = getCardStream().dumpState();
mRetentionFragment.storeCardStream(state);
}
}
BatchStepSensorFragment.javapackage com.example.android.batchstepsensor;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import com.example.android.common.logger.Log;
public class BatchStepSensorFragment extends Fragment implements OnCardClickListener {
public static final String TAG = "StepSensorSample";
// Cards
private CardStreamFragment mCards = null;
// Card tags
public static final String CARD_INTRO = "intro";
public static final String CARD_REGISTER_DETECTOR = "register_detector";
public static final String CARD_REGISTER_COUNTER = "register_counter";
public static final String CARD_BATCHING_DESCRIPTION = "register_batching_description";
public static final String CARD_COUNTING = "counting";
public static final String CARD_EXPLANATION = "explanation";
public static final String CARD_NOBATCHSUPPORT = "error";
// Actions from REGISTER cards
public static final int ACTION_REGISTER_DETECT_NOBATCHING = 10;
public static final int ACTION_REGISTER_DETECT_BATCHING_5s = 11;
public static final int ACTION_REGISTER_DETECT_BATCHING_10s = 12;
public static final int ACTION_REGISTER_COUNT_NOBATCHING = 21;
public static final int ACTION_REGISTER_COUNT_BATCHING_5s = 22;
public static final int ACTION_REGISTER_COUNT_BATCHING_10s = 23;
// Action from COUNTING card
public static final int ACTION_UNREGISTER = 1;
// Actions from description cards
private static final int ACTION_BATCHING_DESCRIPTION_DISMISS = 2;
private static final int ACTION_EXPLANATION_DISMISS = 3;
// State of application, used to register for sensors when app is restored
public static final int STATE_OTHER = 0;
public static final int STATE_COUNTER = 1;
public static final int STATE_DETECTOR = 2;
// Bundle tags used to store data when restoring application state
private static final String BUNDLE_STATE = "state";
private static final String BUNDLE_LATENCY = "latency";
private static final String BUNDLE_STEPS = "steps";
// max batch latency is specified in microseconds
private static final int BATCH_LATENCY_0 = 0; // no batching
private static final int BATCH_LATENCY_10s = 10000000;
private static final int BATCH_LATENCY_5s = 5000000;
/*
For illustration we keep track of the last few events and show their delay from when the event occurred until it was received by the event listener.
These variables keep track of the list of timestamps and the number of events.
*/
// Number of events to keep in queue and display on card
private static final int EVENT_QUEUE_LENGTH = 10;
// List of timestamps when sensor events occurred
private float[] mEventDelays = new float[EVENT_QUEUE_LENGTH];
// number of events in event list
private int mEventLength = 0;
// pointer to next entry in sensor event list
private int mEventData = 0;
// Steps counted in current session
private int mSteps = 0;
// Value of the step counter sensor when the listener was registered.
// (Total steps are calculated from this value.)
private int mCounterSteps = 0;
// Steps counted by the step counter previously. Used to keep counter consistent across rotation
// changes
private int mPreviousCounterSteps = 0;
// State of the app (STATE_OTHER, STATE_COUNTER or STATE_DETECTOR)
private int mState = STATE_OTHER;
// When a listener is registered, the batch sensor delay in microseconds
private int mMaxDelay = 0;
@Override
public void onResume() {
super.onResume();
CardStreamFragment stream = getCardStream();
if (stream.getVisibleCardCount() < 1) {
// No cards are visible, started for the first time
// Prepare all cards and show the intro card.
initialiseCards();
showIntroCard();
// Show the registration card if the hardware is supported, show an error otherwise
if (isKitkatWithStepSensor()) {
showRegisterCard();
} else {
showErrorCard();
}
}
}
@Override
public void onPause() {
super.onPause();
// BEGIN_INCLUDE(onpause)
// Unregister the listener when the application is paused
unregisterListeners();
// END_INCLUDE(onpause)
}
/**
* Returns true if this device is supported. It needs to be running Android KitKat (4.4) or
* higher and has a step counter and step detector sensor.
* This check is useful when an app provides an alternative implementation or different
* functionality if the step sensors are not available or this code runs on a platform version
* below Android KitKat. If this functionality is required, then the minSDK parameter should
* be specified appropriately in the AndroidManifest.
*
* @return True iff the device can run this sample
*/
private boolean isKitkatWithStepSensor() {
// BEGIN_INCLUDE(iskitkatsensor)
// Require at least Android KitKat
int currentApiVersion = android.os.Build.VERSION.SDK_INT;
// Check that the device supports the step counter and detector sensors
PackageManager packageManager = getActivity().getPackageManager();
return currentApiVersion >= android.os.Build.VERSION_CODES.KITKAT
&& packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER)
&& packageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
// END_INCLUDE(iskitkatsensor)
}
/**
* Handles a click on a card action.
* Registers a SensorEventListener (see {@link #registerEventListener(int, int)}) with the
* selected delay, dismisses cards and unregisters the listener
* (see {@link #unregisterListeners()}).
* Actions are defined when a card is created.
*
* @param cardActionId
* @param cardTag
*/
@Override
public void onCardClick(int cardActionId, String cardTag) {
switch (cardActionId) {
// BEGIN_INCLUDE(onclick)
// Register Step Counter card
case ACTION_REGISTER_COUNT_NOBATCHING:
registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_COUNTER);
break;
case ACTION_REGISTER_COUNT_BATCHING_5s:
registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_COUNTER);
break;
case ACTION_REGISTER_COUNT_BATCHING_10s:
registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_COUNTER);
break;
// Register Step Detector card
case ACTION_REGISTER_DETECT_NOBATCHING:
registerEventListener(BATCH_LATENCY_0, Sensor.TYPE_STEP_DETECTOR);
break;
case ACTION_REGISTER_DETECT_BATCHING_5s:
registerEventListener(BATCH_LATENCY_5s, Sensor.TYPE_STEP_DETECTOR);
break;
case ACTION_REGISTER_DETECT_BATCHING_10s:
registerEventListener(BATCH_LATENCY_10s, Sensor.TYPE_STEP_DETECTOR);
break;
// Unregister card
case ACTION_UNREGISTER:
showRegisterCard();
unregisterListeners();
// reset the application state when explicitly unregistered
mState = STATE_OTHER;
break;
// END_INCLUDE(onclick)
// Explanation cards
case ACTION_BATCHING_DESCRIPTION_DISMISS:
// permanently remove the batch description card, it will not be shown again
getCardStream().removeCard(CARD_BATCHING_DESCRIPTION);
break;
case ACTION_EXPLANATION_DISMISS:
// permanently remove the explanation card, it will not be shown again
getCardStream().removeCard(CARD_EXPLANATION);
}
// For register cards, display the counting card
if (cardTag.equals(CARD_REGISTER_COUNTER) || cardTag.equals(CARD_REGISTER_DETECTOR)) {
showCountingCards();
}
}
/**
* Register a {@link android.hardware.SensorEventListener} for the sensor and max batch delay.
* The maximum batch delay specifies the maximum duration in microseconds for which subsequent
* sensor events can be temporarily stored by the sensor before they are delivered to the
* registered SensorEventListener. A larger delay allows the system to handle sensor events more
* efficiently, allowing the system to switch to a lower power state while the sensor is
* capturing events. Once the max delay is reached, all stored events are delivered to the
* registered listener. Note that this value only specifies the maximum delay, the listener may
* receive events quicker. A delay of 0 disables batch mode and registers the listener in
* continuous mode.
* The optimium batch delay depends on the application. For example, a delay of 5 seconds or
* higher may be appropriate for an application that does not update the UI in real time.
*
* @param maxdelay
* @param sensorType
*/
private void registerEventListener(int maxdelay, int sensorType) {
// BEGIN_INCLUDE(register)
// Keep track of state so that the correct sensor type and batch delay can be set up when
// the app is restored (for example on screen rotation).
mMaxDelay = maxdelay;
if (sensorType == Sensor.TYPE_STEP_COUNTER) {
mState = STATE_COUNTER;
/*
Reset the initial step counter value, the first event received by the event listener is
stored in mCounterSteps and used to calculate the total number of steps taken.
*/
mCounterSteps = 0;
Log.i(TAG, "Event listener for step counter sensor registered with a max delay of "
+ mMaxDelay);
} else {
mState = STATE_DETECTOR;
Log.i(TAG, "Event listener for step detector sensor registered with a max delay of "
+ mMaxDelay);
}
// Get the default sensor for the sensor type from the SenorManager
SensorManager sensorManager =
(SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE);
// sensorType is either Sensor.TYPE_STEP_COUNTER or Sensor.TYPE_STEP_DETECTOR
Sensor sensor = sensorManager.getDefaultSensor(sensorType);
// Register the listener for this sensor in batch mode.
// If the max delay is 0, events will be delivered in continuous mode without batching.
final boolean batchMode = sensorManager.registerListener(
mListener, sensor, SensorManager.SENSOR_DELAY_NORMAL, maxdelay);
if (!batchMode) {
// Batch mode could not be enabled, show a warning message and switch to continuous mode
getCardStream().getCard(CARD_NOBATCHSUPPORT)
.setDescription(getString(R.string.warning_nobatching));
getCardStream().showCard(CARD_NOBATCHSUPPORT);
Log.w(TAG, "Could not register sensor listener in batch mode, " +
"falling back to continuous mode.");
}
if (maxdelay > 0 && batchMode) {
// Batch mode was enabled successfully, show a description card
getCardStream().showCard(CARD_BATCHING_DESCRIPTION);
}
// Show the explanation card
getCardStream().showCard(CARD_EXPLANATION);
// END_INCLUDE(register)
}
/**
* Unregisters the sensor listener if it is registered.
*/
private void unregisterListeners() {
// BEGIN_INCLUDE(unregister)
SensorManager sensorManager =
(SensorManager) getActivity().getSystemService(Activity.SENSOR_SERVICE);
sensorManager.unregisterListener(mListener);
Log.i(TAG, "Sensor listener unregistered.");
// END_INCLUDE(unregister)
}
/**
* Resets the step counter by clearing all counting variables and lists.
*/
private void resetCounter() {
// BEGIN_INCLUDE(reset)
mSteps = 0;
mCounterSteps = 0;
mEventLength = 0;
mEventDelays = new float[EVENT_QUEUE_LENGTH];
mPreviousCounterSteps = 0;
// END_INCLUDE(reset)
}
/**
* Listener that handles step sensor events for step detector and step counter sensors.
*/
private final SensorEventListener mListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
// BEGIN_INCLUDE(sensorevent)
// store the delay of this event
recordDelay(event);
final String delayString = getDelayString();
if (event.sensor.getType() == Sensor.TYPE_STEP_DETECTOR) {
// A step detector event is received for each step.
// This means we need to count steps ourselves
mSteps += event.values.length;
// Update the card with the latest step count
getCardStream().getCard(CARD_COUNTING)
.setTitle(getString(R.string.counting_title, mSteps))
.setDescription(getString(R.string.counting_description,
getString(R.string.sensor_detector), mMaxDelay, delayString));
Log.i(TAG,
"New step detected by STEP_DETECTOR sensor. Total step count: " + mSteps);
} else if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
/*
A step counter event contains the total number of steps since the listener
was first registered. We need to keep track of this initial value to calculate the
number of steps taken, as the first value a listener receives is undefined.
*/
if (mCounterSteps < 1) {
// initial value
mCounterSteps = (int) event.values[0];
}
// Calculate steps taken based on first counter value received.
mSteps = (int) event.values[0] - mCounterSteps;
// Add the number of steps previously taken, otherwise the counter would start at 0.
// This is needed to keep the counter consistent across rotation changes.
mSteps = mSteps + mPreviousCounterSteps;
// Update the card with the latest step count
getCardStream().getCard(CARD_COUNTING)
.setTitle(getString(R.string.counting_title, mSteps))
.setDescription(getString(R.string.counting_description,
getString(R.string.sensor_counter), mMaxDelay, delayString));
Log.i(TAG, "New step detected by STEP_COUNTER sensor. Total step count: " + mSteps);
// END_INCLUDE(sensorevent)
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
/**
* Records the delay for the event.
*
* @param event
*/
private void recordDelay(SensorEvent event) {
// Calculate the delay from when event was recorded until it was received here in ms
// Event timestamp is recorded in us accuracy, but ms accuracy is sufficient here
mEventDelays[mEventData] = System.currentTimeMillis() - (event.timestamp / 1000000L);
// Increment length counter
mEventLength = Math.min(EVENT_QUEUE_LENGTH, mEventLength + 1);
// Move pointer to the next (oldest) location
mEventData = (mEventData + 1) % EVENT_QUEUE_LENGTH;
}
private final StringBuffer mDelayStringBuffer = new StringBuffer();
/**
* Returns a string describing the sensor delays recorded in
* {@link #recordDelay(android.hardware.SensorEvent)}.
*
* @return
*/
private String getDelayString() {
// Empty the StringBuffer
mDelayStringBuffer.setLength(0);
// Loop over all recorded delays and append them to the buffer as a decimal
for (int i = 0; i < mEventLength; i++) {
if (i > 0) {
mDelayStringBuffer.append(", ");
}
final int index = (mEventData + i) % EVENT_QUEUE_LENGTH;
final float delay = mEventDelays[index] / 1000f; // convert delay from ms into s
mDelayStringBuffer.append(String.format("%1.1f", delay));
}
return mDelayStringBuffer.toString();
}
/**
* Records the state of the application into the {@link android.os.Bundle}.
*
* @param outState
*/
@Override
public void onSaveInstanceState(Bundle outState) {
// BEGIN_INCLUDE(saveinstance)
super.onSaveInstanceState(outState);
// Store all variables required to restore the state of the application
outState.putInt(BUNDLE_LATENCY, mMaxDelay);
outState.putInt(BUNDLE_STATE, mState);
outState.putInt(BUNDLE_STEPS, mSteps);
// END_INCLUDE(saveinstance)
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// BEGIN_INCLUDE(restore)
// Fragment is being restored, reinitialise its state with data from the bundle
if (savedInstanceState != null) {
resetCounter();
mSteps = savedInstanceState.getInt(BUNDLE_STEPS);
mState = savedInstanceState.getInt(BUNDLE_STATE);
mMaxDelay = savedInstanceState.getInt(BUNDLE_LATENCY);
// Register listeners again if in detector or counter states with restored delay
if (mState == STATE_DETECTOR) {
registerEventListener(mMaxDelay, Sensor.TYPE_STEP_DETECTOR);
} else if (mState == STATE_COUNTER) {
// store the previous number of steps to keep step counter count consistent
mPreviousCounterSteps = mSteps;
registerEventListener(mMaxDelay, Sensor.TYPE_STEP_COUNTER);
}
}
// END_INCLUDE(restore)
}
/**
* Hides the registration cards, reset the counter and show the step counting card.
*/
private void showCountingCards() {
// Hide the registration cards
getCardStream().hideCard(CARD_REGISTER_DETECTOR);
getCardStream().hideCard(CARD_REGISTER_COUNTER);
// Show the explanation card if it has not been dismissed
getCardStream().showCard(CARD_EXPLANATION);
// Reset the step counter, then show the step counting card
resetCounter();
// Set the inital text for the step counting card before a step is recorded
String sensor = "-";
if (mState == STATE_COUNTER) {
sensor = getString(R.string.sensor_counter);
} else if (mState == STATE_DETECTOR) {
sensor = getString(R.string.sensor_detector);
}
// Set initial text
getCardStream().getCard(CARD_COUNTING)
.setTitle(getString(R.string.counting_title, 0))
.setDescription(getString(R.string.counting_description, sensor, mMaxDelay, "-"));
// Show the counting card and make it undismissable
getCardStream().showCard(CARD_COUNTING, false);
}
/**
* Show the introduction card
*/
private void showIntroCard() {
Card c = new Card.Builder(this, CARD_INTRO)
.setTitle(getString(R.string.intro_title))
.setDescription(getString(R.string.intro_message))
.build(getActivity());
getCardStream().addCard(c, true);
}
/**
* Show two registration cards, one for the step detector and counter sensors.
*/
private void showRegisterCard() {
// Hide the counting and explanation cards
getCardStream().hideCard(CARD_BATCHING_DESCRIPTION);
getCardStream().hideCard(CARD_EXPLANATION);
getCardStream().hideCard(CARD_COUNTING);
// Show two undismissable registration cards, one for each step sensor
getCardStream().showCard(CARD_REGISTER_DETECTOR, false);
getCardStream().showCard(CARD_REGISTER_COUNTER, false);
}
/**
* Show the error card.
*/
private void showErrorCard() {
getCardStream().showCard(CARD_NOBATCHSUPPORT, false);
}
/**
* Initialise Cards.
*/
private void initialiseCards() {
// Step counting
Card c = new Card.Builder(this, CARD_COUNTING)
.setTitle("Steps")
.setDescription("")
.addAction("Unregister Listener", ACTION_UNREGISTER, Card.ACTION_NEGATIVE)
.build(getActivity());
getCardStream().addCard(c);
// Register step detector listener
c = new Card.Builder(this, CARD_REGISTER_DETECTOR)
.setTitle(getString(R.string.register_detector_title))
.setDescription(getString(R.string.register_detector_description))
.addAction(getString(R.string.register_0),
ACTION_REGISTER_DETECT_NOBATCHING, Card.ACTION_NEUTRAL)
.addAction(getString(R.string.register_5),
ACTION_REGISTER_DETECT_BATCHING_5s, Card.ACTION_NEUTRAL)
.addAction(getString(R.string.register_10),
ACTION_REGISTER_DETECT_BATCHING_10s, Card.ACTION_NEUTRAL)
.build(getActivity());
getCardStream().addCard(c);
// Register step counter listener
c = new Card.Builder(this, CARD_REGISTER_COUNTER)
.setTitle(getString(R.string.register_counter_title))
.setDescription(getString(R.string.register_counter_description))
.addAction(getString(R.string.register_0),
ACTION_REGISTER_COUNT_NOBATCHING, Card.ACTION_NEUTRAL)
.addAction(getString(R.string.register_5),
ACTION_REGISTER_COUNT_BATCHING_5s, Card.ACTION_NEUTRAL)
.addAction(getString(R.string.register_10),
ACTION_REGISTER_COUNT_BATCHING_10s, Card.ACTION_NEUTRAL)
.build(getActivity());
getCardStream().addCard(c);
// Batching description
c = new Card.Builder(this, CARD_BATCHING_DESCRIPTION)
.setTitle(getString(R.string.batching_queue_title))
.setDescription(getString(R.string.batching_queue_description))
.addAction(getString(R.string.action_notagain),
ACTION_BATCHING_DESCRIPTION_DISMISS, Card.ACTION_POSITIVE)
.build(getActivity());
getCardStream().addCard(c);
// Explanation
c = new Card.Builder(this, CARD_EXPLANATION)
.setDescription(getString(R.string.explanation_description))
.addAction(getString(R.string.action_notagain),
ACTION_EXPLANATION_DISMISS, Card.ACTION_POSITIVE)
.build(getActivity());
getCardStream().addCard(c);
// Error
c = new Card.Builder(this, CARD_NOBATCHSUPPORT)
.setTitle(getString(R.string.error_title))
.setDescription(getString(R.string.error_nosensor))
.build(getActivity());
getCardStream().addCard(c);
}
/**
* Returns the cached CardStreamFragment used to show cards.
*
* @return
*/
private CardStreamFragment getCardStream() {
if (mCards == null) {
mCards = ((CardStream) getActivity()).getCardStream();
}
return mCards;
}
}
Card.java
package com.example.android.batchstepsensor;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
/**
* A Card contains a description and has a visual state. Optionally a card also contains a title,
* progress indicator and zero or more actions. It is constructed through the {@link Builder}.
*/
public class Card {
public static final int ACTION_POSITIVE = 1;
public static final int ACTION_NEGATIVE = 2;
public static final int ACTION_NEUTRAL = 3;
public static final int PROGRESS_TYPE_NO_PROGRESS = 0;
public static final int PROGRESS_TYPE_NORMAL = 1;
public static final int PROGRESS_TYPE_INDETERMINATE = 2;
public static final int PROGRESS_TYPE_LABEL = 3;
private OnCardClickListener mClickListener;
// The card model contains a reference to its desired layout (for extensibility), title,
// description, zero to many action buttons, and zero or 1 progress indicators.
private int mLayoutId = R.layout.card;
/**
* Tag that uniquely identifies this card.
*/
private String mTag = null;
private String mTitle = null;
private String mDescription = null;
private View mCardView = null;
private View mOverlayView = null;
private TextView mTitleView = null;
private TextView mDescView = null;
private View mActionAreaView = null;
private Animator mOngoingAnimator = null;
/**
* Visual state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or
* {@link #CARD_STATE_INACTIVE}.
*/
private int mCardState = CARD_STATE_NORMAL;
public static final int CARD_STATE_NORMAL = 1;
public static final int CARD_STATE_FOCUSED = 2;
public static final int CARD_STATE_INACTIVE = 3;
/**
* Represent actions that can be taken from the card. Stylistically the developer can
* designate the action as positive, negative (ok/cancel, for instance), or neutral.
* This "type" can be used as a UI hint.
* @see com.example.android.sensors.batchstepsensor.Card.CardAction
*/
private ArrayList mCardActions = new ArrayList();
/**
* Some cards will have a sense of "progress" which should be associated with, but separated
* from its "parent" card. To push for simplicity in samples, Cards are designed to have
* a maximum of one progress indicator per Card.
*/
private CardProgress mCardProgress = null;
public Card() {
}
public String getTag() {
return mTag;
}
public View getView() {
return mCardView;
}
public Card setDescription(String desc) {
if (mDescView != null) {
mDescription = desc;
mDescView.setText(desc);
}
return this;
}
public Card setTitle(String title) {
if (mTitleView != null) {
mTitle = title;
mTitleView.setText(title);
}
return this;
}
/**
* Return the UI state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED}
* or {@link #CARD_STATE_INACTIVE}.
*/
public int getState() {
return mCardState;
}
/**
* Set the UI state. The parameter describes the state and must be either
* {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or {@link #CARD_STATE_INACTIVE}.
* Note: This method must be called from the UI Thread.
* @param state
* @return The card itself, allows for chaining of calls
*/
public Card setState(int state) {
mCardState = state;
if (null != mOverlayView) {
if (null != mOngoingAnimator) {
mOngoingAnimator.end();
mOngoingAnimator = null;
}
switch (state) {
case CARD_STATE_NORMAL: {
mOverlayView.setVisibility(View.GONE);
mOverlayView.setAlpha(1.f);
break;
}
case CARD_STATE_FOCUSED: {
mOverlayView.setVisibility(View.VISIBLE);
mOverlayView.setBackgroundResource(R.drawable.card_overlay_focused);
ObjectAnimator animator = ObjectAnimator.ofFloat(mOverlayView, "alpha", 0.f);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setDuration(1000);
animator.start();
mOngoingAnimator = animator;
break;
}
case CARD_STATE_INACTIVE: {
mOverlayView.setVisibility(View.VISIBLE);
mOverlayView.setAlpha(1.f);
mOverlayView.setBackgroundColor(Color.argb(0xaa, 0xcc, 0xcc, 0xcc));
break;
}
}
}
return this;
}
/**
* Set the type of progress indicator.
* The progress type can only be changed if the Card was initially build with a progress
* indicator.
* See {@link Builder#setProgressType(int)}.
* Must be a value of either {@link #PROGRESS_TYPE_NORMAL},
* {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL} or
* {@link #PROGRESS_TYPE_NO_PROGRESS}.
* @param progressType
* @return The card itself, allows for chaining of calls
*/
public Card setProgressType(int progressType) {
if (mCardProgress == null) {
mCardProgress = new CardProgress();
}
mCardProgress.setProgressType(progressType);
return this;
}
/**
* Return the progress indicator type. A value of either {@link #PROGRESS_TYPE_NORMAL},
* {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL}. Otherwise if no progress
* indicator is enabled, {@link #PROGRESS_TYPE_NO_PROGRESS} is returned.
* @return
*/
public int getProgressType() {
if (mCardProgress == null) {
return PROGRESS_TYPE_NO_PROGRESS;
}
return mCardProgress.progressType;
}
/**
* Set the progress to the specified value. Only applicable if the card has a
* {@link #PROGRESS_TYPE_NORMAL} progress type.
* @param progress
* @return
* @see #setMaxProgress(int)
*/
public Card setProgress(int progress) {
if (mCardProgress != null) {
mCardProgress.setProgress(progress);
}
return this;
}
/**
* Set the range of the progress to 0...max. Only applicable if the card has a
* {@link #PROGRESS_TYPE_NORMAL} progress type.
* @return
*/
public Card setMaxProgress(int max){
if (mCardProgress != null) {
mCardProgress.setMax(max);
}
return this;
}
/**
* Set the label text for the progress if the card has a progress type of
* {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or
* {@link #PROGRESS_TYPE_LABEL}
* @param text
* @return
*/
public Card setProgressLabel(String text) {
if (mCardProgress != null) {
mCardProgress.setProgressLabel(text);
}
return this;
}
/**
* Toggle the visibility of the progress section of the card. Only applicable if
* the card has a progress type of
* {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or
* {@link #PROGRESS_TYPE_LABEL}.
* @param isVisible
* @return
*/
public Card setProgressVisibility(boolean isVisible) {
if (mCardProgress.progressView == null) {
return this; // Card does not have progress
}
mCardProgress.progressView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
return this;
}
/**
* Adds an action to this card during build time.
*
* @param label
* @param id
* @param type
*/
private void addAction(String label, int id, int type) {
CardAction cardAction = new CardAction();
cardAction.label = label;
cardAction.id = id;
cardAction.type = type;
mCardActions.add(cardAction);
}
/**
* Toggles the visibility of a card action.
* @param actionId
* @param isVisible
* @return
*/
public Card setActionVisibility(int actionId, boolean isVisible) {
int visibilityFlag = isVisible ? View.VISIBLE : View.GONE;
for (CardAction action : mCardActions) {
if (action.id == actionId && action.actionView != null) {
action.actionView.setVisibility(visibilityFlag);
}
}
return this;
}
/**
* Toggles visibility of the action area of this Card through an animation.
* @param isVisible
* @return
*/
public Card setActionAreaVisibility(boolean isVisible) {
if (mActionAreaView == null) {
return this; // Card does not have an action area
}
if (isVisible) {
// Show the action area
mActionAreaView.setVisibility(View.VISIBLE);
mActionAreaView.setPivotY(0.f);
mActionAreaView.setPivotX(mCardView.getWidth() / 2.f);
mActionAreaView.setAlpha(0.5f);
mActionAreaView.setRotationX(-90.f);
mActionAreaView.animate().rotationX(0.f).alpha(1.f).setDuration(400);
} else {
// Hide the action area
mActionAreaView.setPivotY(0.f);
mActionAreaView.setPivotX(mCardView.getWidth() / 2.f);
mActionAreaView.animate().rotationX(-90.f).alpha(0.f).setDuration(400).setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mActionAreaView.setVisibility(View.GONE);
}
});
}
return this;
}
/**
* Creates a shallow clone of the card. Shallow means all values are present, but no views.
* This is useful for saving/restoring in the case of configuration changes, like screen
* rotation.
*
* @return A shallow clone of the card instance
*/
public Card createShallowClone() {
Card cloneCard = new Card();
// Outer card values
cloneCard.mTitle = mTitle;
cloneCard.mDescription = mDescription;
cloneCard.mTag = mTag;
cloneCard.mLayoutId = mLayoutId;
cloneCard.mCardState = mCardState;
// Progress
if (mCardProgress != null) {
cloneCard.mCardProgress = mCardProgress.createShallowClone();
}
// Actions
for (CardAction action : mCardActions) {
cloneCard.mCardActions.add(action.createShallowClone());
}
return cloneCard;
}
/**
* Prepare the card to be stored for configuration change.
*/
public void prepareForConfigurationChange() {
// Null out views.
mCardView = null;
for (CardAction action : mCardActions) {
action.actionView = null;
}
mCardProgress.progressView = null;
}
/**
* Creates a new {@link #Card}.
*/
public static class Builder {
private Card mCard;
/**
* Instantiate the builder with data from a shallow clone.
* @param listener
* @param card
* @see Card#createShallowClone()
*/
protected Builder(OnCardClickListener listener, Card card) {
mCard = card;
mCard.mClickListener = listener;
}
/**
* Instantiate the builder with the tag of the card.
* @param listener
* @param tag
*/
public Builder(OnCardClickListener listener, String tag) {
mCard = new Card();
mCard.mTag = tag;
mCard.mClickListener = listener;
}
public Builder setTitle(String title) {
mCard.mTitle = title;
return this;
}
public Builder setDescription(String desc) {
mCard.mDescription = desc;
return this;
}
/**
* Add an action.
* The type describes how this action will be displayed. Accepted values are
* {@link #ACTION_NEUTRAL}, {@link #ACTION_POSITIVE} or {@link #ACTION_NEGATIVE}.
*
* @param label The text to display for this action
* @param id Identifier for this action, supplied in the click listener
* @param type UI style of action
* @return
*/
public Builder addAction(String label, int id, int type) {
mCard.addAction(label, id, type);
return this;
}
/**
* Override the default layout.
* The referenced layout file has to contain the same identifiers as defined in the default
* layout configuration.
* @param layout
* @return
* @see R.layout.card
*/
public Builder setLayout(int layout) {
mCard.mLayoutId = layout;
return this;
}
/**
* Set the type of progress bar to display.
* Accepted values are:
*
{@link #PROGRESS_TYPE_NORMAL}
* displays a standard, linear progress indicator.
*{@link #PROGRESS_TYPE_INDETERMINATE} displays an indeterminate (infite) progress
* indicator.
*{@link #PROGRESS_TYPE_LABEL} only displays a label text in the progress area
* of the card.
**
* @param progressType
* @return
*/
public Builder setProgressType(int progressType) {
mCard.setProgressType(progressType);
return this;
}
public Builder setProgressLabel(String label) {
// ensure the progress layout has been initialized, use 'no progress' by default
if (mCard.mCardProgress == null) {
mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS);
}
mCard.mCardProgress.label = label;
return this;
}
public Builder setProgressMaxValue(int maxValue) {
// ensure the progress layout has been initialized, use 'no progress' by default
if (mCard.mCardProgress == null) {
mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS);
}
mCard.mCardProgress.maxValue = maxValue;
return this;
}
public Builder setStatus(int status) {
mCard.setState(status);
return this;
}
public Card build(Activity activity) {
LayoutInflater inflater = activity.getLayoutInflater();
// Inflating the card.
ViewGroup cardView = (ViewGroup) inflater.inflate(mCard.mLayoutId,
(ViewGroup) activity.findViewById(R.id.card_stream), false);
// Check that the layout contains a TextView with the card_title id
View viewTitle = cardView.findViewById(R.id.card_title);
if (mCard.mTitle != null && viewTitle != null) {
mCard.mTitleView = (TextView) viewTitle;
mCard.mTitleView.setText(mCard.mTitle);
} else if (viewTitle != null) {
viewTitle.setVisibility(View.GONE);
}
// Check that the layout contains a TextView with the card_content id
View viewDesc = cardView.findViewById(R.id.card_content);
if (mCard.mDescription != null && viewDesc != null) {
mCard.mDescView = (TextView) viewDesc;
mCard.mDescView.setText(mCard.mDescription);
} else if (viewDesc != null) {
cardView.findViewById(R.id.card_content).setVisibility(View.GONE);
}
ViewGroup actionArea = (ViewGroup) cardView.findViewById(R.id.card_actionarea);
// Inflate Progress
initializeProgressView(inflater, actionArea);
// Inflate all action views.
initializeActionViews(inflater, cardView, actionArea);
mCard.mCardView = cardView;
mCard.mOverlayView = cardView.findViewById(R.id.card_overlay);
return mCard;
}
/**
* Initialize data from the given card.
* @param card
* @return
* @see Card#createShallowClone()
*/
public Builder cloneFromCard(Card card) {
mCard = card.createShallowClone();
return this;
}
/**
* Build the action views by inflating the appropriate layouts and setting the text and
* values.
* @param inflater
* @param cardView
* @param actionArea
*/
private void initializeActionViews(LayoutInflater inflater, ViewGroup cardView,
ViewGroup actionArea) {
if (!mCard.mCardActions.isEmpty()) {
// Set action area to visible only when actions are visible
actionArea.setVisibility(View.VISIBLE);
mCard.mActionAreaView = actionArea;
}
// Inflate all card actions
for (final CardAction action : mCard.mCardActions) {
int useActionLayout = 0;
switch (action.type) {
case Card.ACTION_POSITIVE:
useActionLayout = R.layout.card_button_positive;
break;
case Card.ACTION_NEGATIVE:
useActionLayout = R.layout.card_button_negative;
break;
case Card.ACTION_NEUTRAL:
default:
useActionLayout = R.layout.card_button_neutral;
break;
}
action.actionView = inflater.inflate(useActionLayout, actionArea, false);
Button actionButton = (Button) action.actionView.findViewById(R.id.card_button);
actionButton.setText(action.label);
actionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCard.mClickListener.onCardClick(action.id, mCard.mTag);
}
});
actionArea.addView(action.actionView);
}
}
/**
* Build the progress view into the given ViewGroup.
*
* @param inflater
* @param actionArea
*/
private void initializeProgressView(LayoutInflater inflater, ViewGroup actionArea) {
// Only inflate progress layout if a progress type other than NO_PROGRESS was set.
if (mCard.mCardProgress != null) {
//Setup progress card.
View progressView = inflater.inflate(R.layout.card_progress, actionArea, false);
ProgressBar progressBar =
(ProgressBar) progressView.findViewById(R.id.card_progress);
((TextView) progressView.findViewById(R.id.card_progress_text))
.setText(mCard.mCardProgress.label);
progressBar.setMax(mCard.mCardProgress.maxValue);
progressBar.setProgress(0);
mCard.mCardProgress.progressView = progressView;
mCard.mCardProgress.setProgressType(mCard.getProgressType());
actionArea.addView(progressView);
}
}
}
/**
* Represents a clickable action, accessible from the bottom of the card.
* Fields include the label, an ID to specify the action that was performed in the callback,
* an action type (positive, negative, neutral), and the callback.
*/
public class CardAction {
public String label;
public int id;
public int type;
public View actionView;
public CardAction createShallowClone() {
CardAction actionClone = new CardAction();
actionClone.label = label;
actionClone.id = id;
actionClone.type = type;
return actionClone;
// Not the view. Never the view (don't want to hold view references for
// onConfigurationChange.
}
}
/**
* Describes the progress of a {@link Card}.
* Three types of progress are supported:
*
*/
public class CardProgress {
private int progressType = Card.PROGRESS_TYPE_NO_PROGRESS;
private String label = "";
private int currProgress = 0;
private int maxValue = 100;
public View progressView = null;
private ProgressBar progressBar = null;
private TextView progressLabel = null;
public CardProgress createShallowClone() {
CardProgress progressClone = new CardProgress();
progressClone.label = label;
progressClone.currProgress = currProgress;
progressClone.maxValue = maxValue;
progressClone.progressType = progressType;
return progressClone;
}
/**
* Set the progress. Only useful for the type {@link #PROGRESS_TYPE_NORMAL}.
* @param progress
* @see android.widget.ProgressBar#setProgress(int)
*/
public void setProgress(int progress) {
currProgress = progress;
final ProgressBar bar = getProgressBar();
if (bar != null) {
bar.setProgress(currProgress);
bar.invalidate();
}
}
/**
* Set the range of the progress to 0...max.
* Only useful for the type {@link #PROGRESS_TYPE_NORMAL}.
* @param max
* @see android.widget.ProgressBar#setMax(int)
*/
public void setMax(int max) {
maxValue = max;
final ProgressBar bar = getProgressBar();
if (bar != null) {
bar.setMax(maxValue);
}
}
/**
* Set the label text that appears near the progress indicator.
* @param text
*/
public void setProgressLabel(String text) {
label = text;
final TextView labelView = getProgressLabel();
if (labelView != null) {
labelView.setText(text);
}
}
/**
* Set how progress is displayed. The parameter must be one of three supported types:
*
{@link Card#PROGRESS_TYPE_INDETERMINATE}:
* Indeterminate progress bar with label txt
*