1.CaloriesNotifier.java
package com.tdc.jibuqi;
import java.util.Locale;
/**
* Calculates and displays the approximate calories.
*/
public class CaloriesNotifier implements StepListener, SpeakingTimer.Listener {
public interface Listener {
public void valueChanged(float value);
public void passValue();
}
private Listener mListener;
private static double METRIC_RUNNING_FACTOR = 1.02784823;
private static double IMPERIAL_RUNNING_FACTOR = 0.75031498;
private static double METRIC_WALKING_FACTOR = 0.708;
private static double IMPERIAL_WALKING_FACTOR = 0.517;
private double mCalories = 0;
PedometerSettings mSettings;
Utils mUtils;
boolean mIsMetric;
boolean mIsRunning;
float mStepLength;
float mBodyWeight;
public CaloriesNotifier(Listener listener, PedometerSettings settings, Utils utils) {
mListener = listener;
mUtils = utils;
mSettings = settings;
reloadSettings();
}
public void setCalories(float calories) {
mCalories = calories;
notifyListener();
}
public void reloadSettings() {
mIsMetric = mSettings.isMetric();
mIsRunning = mSettings.isRunning();
mStepLength = mSettings.getStepLength();
mBodyWeight = mSettings.getBodyWeight();
notifyListener();
}
public void resetValues() {
mCalories = 0;
}
public void isMetric(boolean isMetric) {
mIsMetric = isMetric;
}
public void setStepLength(float stepLength) {
mStepLength = stepLength;
}
public void onStep() {
if (mIsMetric) {
mCalories +=
(mBodyWeight * (mIsRunning ? METRIC_RUNNING_FACTOR : METRIC_WALKING_FACTOR))
// Distance:
* mStepLength // centimeters
/ 100000.0; // centimeters/kilometer
}
else {
mCalories +=
(mBodyWeight * (mIsRunning ? IMPERIAL_RUNNING_FACTOR : IMPERIAL_WALKING_FACTOR))
// Distance:
* mStepLength // inches
/ 63360.0; // inches/mile
}
notifyListener();
}
private void notifyListener() {
mListener.valueChanged((float)mCalories);
}
public void passValue() {
}
public void speak() {
if (mSettings.shouldTellCalories()) {
if (mCalories > 0) {
if(Locale.getDefault().getLanguage().equals("zh")&&
Locale.getDefault().getCountry().equals("CN")){
mUtils.say("当前消耗" + (int)mCalories + "卡路里");
}else{
mUtils.say("" + (int)mCalories + " calories burned");
}
}
}
}
}
2.DistanceNotifier.java
package com.tdc.jibuqi;
import java.util.Locale;
/**
* Calculates and displays the distance walked.
* @author Levente Bagi
*/
public class DistanceNotifier implements StepListener, SpeakingTimer.Listener {
public interface Listener {
public void valueChanged(float value);
public void passValue();
}
private Listener mListener;
float mDistance = 0;
PedometerSettings mSettings;
Utils mUtils;
boolean mIsMetric;
float mStepLength;
public DistanceNotifier(Listener listener, PedometerSettings settings, Utils utils) {
mListener = listener;
mUtils = utils;
mSettings = settings;
reloadSettings();
}
public void setDistance(float distance) {
mDistance = distance;
notifyListener();
}
public void reloadSettings() {
mIsMetric = mSettings.isMetric();
mStepLength = mSettings.getStepLength();
notifyListener();
}
public void onStep() {
//判断步长的距离
if (mIsMetric) {//厘米转换为千米
mDistance += (float)(// kilometers
mStepLength // centimeters
/ 100000.0); // centimeters/kilometer
}
else {//英寸转换为英里
mDistance += (float)(// miles
mStepLength // inches
/ 63360.0); // inches/mile
}
notifyListener();
}
private void notifyListener() {
mListener.valueChanged(mDistance);
}
public void passValue() {
// Callback of StepListener - Not implemented
}
public void speak() {
if (mSettings.shouldTellDistance()) {
if (mDistance >= .001f) {
if(Locale.getDefault().getLanguage().equals("zh")&&Locale.getDefault().getCountry().equals("CN")){
mUtils.say("当前距离"+("" + (mDistance + 0.000001f)).substring(0, 5) + (mIsMetric ? "公里," : "英里,"));
}else{
mUtils.say(("" + (mDistance + 0.000001f)).substring(0, 5) + (mIsMetric ? " kilometers" : " miles"));
}
}
}
}
}
3.PaceNotifier.java
package com.tdc.jibuqi;
import java.util.ArrayList;
import java.util.Locale;
public class PaceNotifier implements StepListener, SpeakingTimer.Listener {
public interface Listener {
public void paceChanged(int value);
public void passValue();
}
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
int mCounter = 0;
private long mLastStepTime = 0;
private long[] mLastStepDeltas = {-1, -1, -1, -1};
private int mLastStepDeltasIndex = 0;
private long mPace = 0;
PedometerSettings mSettings;
Utils mUtils;
/** Desired pace, adjusted by the user */
int mDesiredPace;
/** Should we speak? */
boolean mShouldTellFasterslower;
/** When did the TTS speak last time */
private long mSpokenAt = 0;
public PaceNotifier(PedometerSettings settings, Utils utils) {
mUtils = utils;
mSettings = settings;
mDesiredPace = mSettings.getDesiredPace();
reloadSettings();
}
public void setPace(int pace) {
mPace = pace;
int avg = (int)(60*1000.0 / mPace);
for (int i = 0; i < mLastStepDeltas.length; i++) {
mLastStepDeltas[i] = avg;
}
notifyListener();
}
public void reloadSettings() {
mShouldTellFasterslower =
mSettings.shouldTellFasterslower()
&& mSettings.getMaintainOption() == PedometerSettings.M_PACE;
notifyListener();
}
public void addListener(Listener l) {
mListeners.add(l);
}
public void setDesiredPace(int desiredPace) {
mDesiredPace = desiredPace;
}
public void onStep() {
long thisStepTime = System.currentTimeMillis();
mCounter ++;
// Calculate pace based on last x steps
if (mLastStepTime > 0) {
long delta = thisStepTime - mLastStepTime;
mLastStepDeltas[mLastStepDeltasIndex] = delta;
mLastStepDeltasIndex = (mLastStepDeltasIndex + 1) % mLastStepDeltas.length;
long sum = 0;
boolean isMeaningfull = true;
for (int i = 0; i < mLastStepDeltas.length; i++) {
if (mLastStepDeltas[i] < 0) {
isMeaningfull = false;
break;
}
sum += mLastStepDeltas[i];
}
if (isMeaningfull && sum > 0) {
long avg = sum / mLastStepDeltas.length;
mPace = 60*1000 / avg;
// TODO: remove duplication. This also exists in SpeedNotifier
if (mShouldTellFasterslower && mUtils.isSpeakingEnabled()) {
if (thisStepTime - mSpokenAt > 3000 && !mUtils.isSpeakingNow()) {
float little = 0.10f;
float normal = 0.30f;
float much = 0.50f;
boolean spoken = true;
if(Locale.getDefault().getLanguage().equals("zh")&&Locale.getDefault().getCountry().equals("CN")){
if (mPace < mDesiredPace * (1 - much)) {
mUtils.say("您的步调很慢!");
}
else if (mPace > mDesiredPace * (1 + much)) {
mUtils.say("您的步调很快!");
}
else if (mPace < mDesiredPace * (1 - normal)) {
mUtils.say("您的步调有点慢!");
}
else if (mPace > mDesiredPace * (1 + normal)) {
mUtils.say("您的步调有点快!");
}
else if (mPace < mDesiredPace * (1 - little)) {
mUtils.say("您的步调慢!");
}
else if (mPace > mDesiredPace * (1 + little)) {
mUtils.say("您的步调快!");
}else {
spoken = false;
}
}else{
if (mPace < mDesiredPace * (1 - much)) {
mUtils.say("much faster!");
}
else if (mPace > mDesiredPace * (1 + much)) {
mUtils.say("much slower!");
}
else if (mPace < mDesiredPace * (1 - normal)) {
mUtils.say("faster!");
}
else if (mPace > mDesiredPace * (1 + normal)) {
mUtils.say("slower!");
}
else if (mPace < mDesiredPace * (1 - little)) {
mUtils.say("a little faster!");
}
else if (mPace > mDesiredPace * (1 + little)) {
mUtils.say("a little slower!");
}else {
spoken = false;
}
}
if (spoken) {
mSpokenAt = thisStepTime;
}
}
}
}
else {
mPace = -1;
}
}
mLastStepTime = thisStepTime;
notifyListener();
}
private void notifyListener() {
for (Listener listener : mListeners) {
listener.paceChanged((int)mPace);
}
}
public void passValue() {
// Not used
}
//-----------------------------------------------------
// Speaking
public void speak() {
if (mSettings.shouldTellPace()) {
if (mPace > 0) {
if(Locale.getDefault().getLanguage().equals("zh")&&Locale.getDefault().getCountry().equals("CN")){
mUtils.say("当前步速"+mPace + "步每分钟,");
}else{
mUtils.say(mPace + " steps per minute");
}
}
}
}
}
4.Pedometer.java
package com.tdc.jibuqi;
import com.tdc.jibuqi.R;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
//主Activity
public class Pedometer extends Activity {
private static final String TAG = "Pedometer";
private SharedPreferences mSettings;
private PedometerSettings mPedometerSettings;
private Utils mUtils;
private TextView mStepValueView;
private TextView mPaceValueView;
private TextView mDistanceValueView;
private TextView mSpeedValueView;
private TextView mCaloriesValueView;
TextView mDesiredPaceView;
private int mStepValue;
private int mPaceValue;
private float mDistanceValue;
private float mSpeedValue;
private int mCaloriesValue;
private float mDesiredPaceOrSpeed;
private int mMaintain;
private boolean mIsMetric;
private float mMaintainInc;
private boolean mQuitting = false; // Set when user selected Quit from menu, can be used by onPause, onStop, onDestroy
/**
* True, when service is running.
*/
private boolean mIsRunning;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "[ACTIVITY] onCreate");
super.onCreate(savedInstanceState);
mStepValue = 0;//步数
mPaceValue = 0;//步速
setContentView(R.layout.main);//绑定视图
mUtils = Utils.getInstance();//获取Utils这个类
}
@Override
protected void onStart() {
Log.i(TAG, "[ACTIVITY] onStart");
super.onStart();
}
@Override
protected void onResume() {
Log.i(TAG, "[ACTIVITY] onResume");
super.onResume();
//获取配置
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
mPedometerSettings = new PedometerSettings(mSettings);
mUtils.setSpeak(mSettings.getBoolean("speak", false));
// Read from preferences if the service was running on the last onPause
mIsRunning = mPedometerSettings.isServiceRunning();
// Start the service if this is considered to be an application start (last onPause was long ago)
if (!mIsRunning && mPedometerSettings.isNewStart()) {
startStepService();
bindStepService();
}
else if (mIsRunning) {
bindStepService();
}
mPedometerSettings.clearServiceRunning();
mStepValueView = (TextView) findViewById(R.id.step_value);
mPaceValueView = (TextView) findViewById(R.id.pace_value);
mDistanceValueView = (TextView) findViewById(R.id.distance_value);
mSpeedValueView = (TextView) findViewById(R.id.speed_value);
mCaloriesValueView = (TextView) findViewById(R.id.calories_value);
mDesiredPaceView = (TextView) findViewById(R.id.desired_pace_value);
mIsMetric = mPedometerSettings.isMetric();
((TextView) findViewById(R.id.distance_units)).setText(getString(
mIsMetric
? R.string.kilometers
: R.string.miles
));
((TextView) findViewById(R.id.speed_units)).setText(getString(
mIsMetric
? R.string.kilometers_per_hour
: R.string.miles_per_hour
));
mMaintain = mPedometerSettings.getMaintainOption();
((LinearLayout) this.findViewById(R.id.desired_pace_control)).setVisibility(
mMaintain != PedometerSettings.M_NONE
? View.VISIBLE
: View.GONE
);
if (mMaintain == PedometerSettings.M_PACE) {
mMaintainInc = 5f;
mDesiredPaceOrSpeed = (float)mPedometerSettings.getDesiredPace();
}else if (mMaintain == PedometerSettings.M_SPEED) {
mDesiredPaceOrSpeed = mPedometerSettings.getDesiredSpeed();
mMaintainInc = 0.1f;
}
Button button1 = (Button) findViewById(R.id.button_desired_pace_lower);
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mDesiredPaceOrSpeed -= mMaintainInc;
mDesiredPaceOrSpeed = Math.round(mDesiredPaceOrSpeed * 10) / 10f;
displayDesiredPaceOrSpeed();
setDesiredPaceOrSpeed(mDesiredPaceOrSpeed);
}
});
Button button2 = (Button) findViewById(R.id.button_desired_pace_raise);
button2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mDesiredPaceOrSpeed += mMaintainInc;
mDesiredPaceOrSpeed = Math.round(mDesiredPaceOrSpeed * 10) / 10f;
displayDesiredPaceOrSpeed();
setDesiredPaceOrSpeed(mDesiredPaceOrSpeed);
}
});
if (mMaintain != PedometerSettings.M_NONE) {
((TextView) findViewById(R.id.desired_pace_label)).setText(
mMaintain == PedometerSettings.M_PACE
? R.string.desired_pace
: R.string.desired_speed
);
}
displayDesiredPaceOrSpeed();
}
private void displayDesiredPaceOrSpeed() {
if (mMaintain == PedometerSettings.M_PACE) {
mDesiredPaceView.setText("" + (int)mDesiredPaceOrSpeed);
}
else {
mDesiredPaceView.setText("" + mDesiredPaceOrSpeed);
}
}
@Override
protected void onPause() {
Log.i(TAG, "[ACTIVITY] onPause");
if (mIsRunning) {
unbindStepService();
}
if (mQuitting) {
mPedometerSettings.saveServiceRunningWithNullTimestamp(mIsRunning);
}
else {
mPedometerSettings.saveServiceRunningWithTimestamp(mIsRunning);
}
super.onPause();
savePaceSetting();
}
@Override
protected void onStop() {
Log.i(TAG, "[ACTIVITY] onStop");
super.onStop();
}
protected void onDestroy() {
Log.i(TAG, "[ACTIVITY] onDestroy");
super.onDestroy();
}
protected void onRestart() {
Log.i(TAG, "[ACTIVITY] onRestart");
super.onDestroy();
}
private void setDesiredPaceOrSpeed(float desiredPaceOrSpeed) {
if (mService != null) {
if (mMaintain == PedometerSettings.M_PACE) {
mService.setDesiredPace((int)desiredPaceOrSpeed);
}
else
if (mMaintain == PedometerSettings.M_SPEED) {
mService.setDesiredSpeed(desiredPaceOrSpeed);
}
}
}
private void savePaceSetting() {
mPedometerSettings.savePaceOrSpeedSetting(mMaintain, mDesiredPaceOrSpeed);
}
private StepService mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ((StepService.StepBinder)service).getService();
mService.registerCallback(mCallback);
mService.reloadSettings();
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
private void startStepService() {
if (! mIsRunning) {
Log.i(TAG, "[SERVICE] Start");
mIsRunning = true;
startService(new Intent(Pedometer.this,
StepService.class));
}
}
private void bindStepService() {
Log.i(TAG, "[SERVICE] Bind");
bindService(new Intent(Pedometer.this,
StepService.class), mConnection, Context.BIND_AUTO_CREATE + Context.BIND_DEBUG_UNBIND);
}
private void unbindStepService() {
Log.i(TAG, "[SERVICE] Unbind");
unbindService(mConnection);
}
private void stopStepService() {
Log.i(TAG, "[SERVICE] Stop");
if (mService != null) {
Log.i(TAG, "[SERVICE] stopService");
stopService(new Intent(Pedometer.this,
StepService.class));
}
mIsRunning = false;
}
private void resetValues(boolean updateDisplay) {
if (mService != null && mIsRunning) {
mService.resetValues();
}
else {
mStepValueView.setText("0");
mPaceValueView.setText("0");
mDistanceValueView.setText("0");
mSpeedValueView.setText("0");
mCaloriesValueView.setText("0");
SharedPreferences state = getSharedPreferences("state", 0);
SharedPreferences.Editor stateEditor = state.edit();
if (updateDisplay) {
stateEditor.putInt("steps", 0);
stateEditor.putInt("pace", 0);
stateEditor.putFloat("distance", 0);
stateEditor.putFloat("speed", 0);
stateEditor.putFloat("calories", 0);
stateEditor.commit();
}
}
}
private static final int MENU_SETTINGS = 8;
private static final int MENU_QUIT = 9;
private static final int MENU_PAUSE = 1;
private static final int MENU_RESUME = 2;
private static final int MENU_RESET = 3;
/* Creates the menu items */
/*创建menu*/
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
if (mIsRunning) {//判断程序是否在运行
menu.add(0, MENU_PAUSE, 0, R.string.pause)
.setIcon(android.R.drawable.ic_media_pause)
.setShortcut('1', 'p');
}
else {
menu.add(0, MENU_RESUME, 0, R.string.resume)
.setIcon(android.R.drawable.ic_media_play)
.setShortcut('1', 'p');
}
menu.add(0, MENU_RESET, 0, R.string.reset)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel)
.setShortcut('2', 'r');
menu.add(0, MENU_SETTINGS, 0, R.string.settings)
.setIcon(android.R.drawable.ic_menu_preferences)
.setShortcut('8', 's')
.setIntent(new Intent(this, Settings.class));
menu.add(0, MENU_QUIT, 0, R.string.quit)
.setIcon(android.R.drawable.ic_lock_power_off)
.setShortcut('9', 'q');
return true;
}
/* Handles item selections */
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_PAUSE:
unbindStepService();
stopStepService();
return true;
case MENU_RESUME:
startStepService();
bindStepService();
return true;
case MENU_RESET:
resetValues(true);
return true;
case MENU_QUIT:
resetValues(false);
unbindStepService();
stopStepService();
mQuitting = true;
finish();
return true;
}
return false;
}
// TODO: unite all into 1 type of message
private StepService.ICallback mCallback = new StepService.ICallback() {
public void stepsChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(STEPS_MSG, value, 0));
}
public void paceChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(PACE_MSG, value, 0));
}
public void distanceChanged(float value) {
mHandler.sendMessage(mHandler.obtainMessage(DISTANCE_MSG, (int)(value*1000), 0));
}
public void speedChanged(float value) {
mHandler.sendMessage(mHandler.obtainMessage(SPEED_MSG, (int)(value*1000), 0));
}
public void caloriesChanged(float value) {
mHandler.sendMessage(mHandler.obtainMessage(CALORIES_MSG, (int)(value), 0));
}
};
private static final int STEPS_MSG = 1;
private static final int PACE_MSG = 2;
private static final int DISTANCE_MSG = 3;
private static final int SPEED_MSG = 4;
private static final int CALORIES_MSG = 5;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case STEPS_MSG:
mStepValue = (int)msg.arg1;
mStepValueView.setText("" + mStepValue);
break;
case PACE_MSG:
mPaceValue = msg.arg1;
if (mPaceValue <= 0) {
mPaceValueView.setText("0");
}
else {
mPaceValueView.setText("" + (int)mPaceValue);
}
break;
case DISTANCE_MSG:
mDistanceValue = ((int)msg.arg1)/1000f;
if (mDistanceValue <= 0) {
mDistanceValueView.setText("0");
}
else {
mDistanceValueView.setText(
("" + (mDistanceValue + 0.000001f)).substring(0, 5)
);
}
break;
case SPEED_MSG:
mSpeedValue = ((int)msg.arg1)/1000f;
if (mSpeedValue <= 0) {
mSpeedValueView.setText("0");
}
else {
mSpeedValueView.setText(
("" + (mSpeedValue + 0.000001f)).substring(0, 4)
);
}
break;
case CALORIES_MSG:
mCaloriesValue = msg.arg1;
if (mCaloriesValue <= 0) {
mCaloriesValueView.setText("0");
}
else {
mCaloriesValueView.setText("" + (int)mCaloriesValue);
}
break;
default:
super.handleMessage(msg);
}
}
};
}
5.PedometerSettings.java
package com.tdc.jibuqi;
import android.content.SharedPreferences;
public class PedometerSettings {
SharedPreferences mSettings;
public static int M_NONE = 1;
public static int M_PACE = 2;
public static int M_SPEED = 3;
public PedometerSettings(SharedPreferences settings) {
mSettings = settings;
}
public boolean isMetric() {//判断距离的单位
return mSettings.getString("units", "metric").equals("metric");//是千米/还是英寸
}
public float getStepLength() {//获取步长
try {
return Float.valueOf(mSettings.getString("step_length", "70").trim());
}
catch (NumberFormatException e) {
// TODO: reset value, & notify user somehow
return 0f;
}
}
public float getBodyWeight() {//获取体重
try {
return Float.valueOf(mSettings.getString("body_weight", "50").trim());
}
catch (NumberFormatException e) {
// TODO: reset value, & notify user somehow
return 0f;
}
}
public boolean isRunning() {//获取运动类型
return mSettings.getString("exercise_type", "running").equals("running");
}
//是否设置目标步伐或者速度
public int getMaintainOption() {
String p = mSettings.getString("maintain", "none");
return
p.equals("none") ? M_NONE : (
p.equals("pace") ? M_PACE : (
p.equals("speed") ? M_SPEED : (
0)));
}
//-------------------------------------------------------------------
// Desired pace & speed:
// these can not be set in the preference activity, only on the main
// screen if "maintain" is set to "pace" or "speed"
public int getDesiredPace() {
return mSettings.getInt("desired_pace", 180); // steps/minute
}
public float getDesiredSpeed() {
return mSettings.getFloat("desired_speed", 4f); // km/h or mph
}
public void savePaceOrSpeedSetting(int maintain, float desiredPaceOrSpeed) {
SharedPreferences.Editor editor = mSettings.edit();
if (maintain == M_PACE) {
editor.putInt("desired_pace", (int)desiredPaceOrSpeed);
}
else
if (maintain == M_SPEED) {
editor.putFloat("desired_speed", desiredPaceOrSpeed);
}
editor.commit();
}
//-------------------------------------------------------------------
// Speaking:
public boolean shouldSpeak() {
return mSettings.getBoolean("speak", false);
}
public float getSpeakingInterval() {
try {
return Float.valueOf(mSettings.getString("speaking_interval", "1"));
}
catch (NumberFormatException e) {
// This could not happen as the value is selected from a list.
return 1;
}
}
public boolean shouldTellSteps() {
return mSettings.getBoolean("speak", false)
&& mSettings.getBoolean("tell_steps", false);
}
public boolean shouldTellPace() {
return mSettings.getBoolean("speak", false)
&& mSettings.getBoolean("tell_pace", false);
}
public boolean shouldTellDistance() {
return mSettings.getBoolean("speak", false)
&& mSettings.getBoolean("tell_distance", false);
}
public boolean shouldTellSpeed() {
return mSettings.getBoolean("speak", false)
&& mSettings.getBoolean("tell_speed", false);
}
public boolean shouldTellCalories() {
return mSettings.getBoolean("speak", false)
&& mSettings.getBoolean("tell_calories", false);
}
public boolean shouldTellFasterslower() {
return mSettings.getBoolean("speak", false)
&& mSettings.getBoolean("tell_fasterslower", false);
}
public boolean wakeAggressively() {
return mSettings.getString("operation_level", "run_in_background").equals("wake_up");
}
public boolean keepScreenOn() {
return mSettings.getString("operation_level", "run_in_background").equals("keep_screen_on");
}
//
// Internal
public void saveServiceRunningWithTimestamp(boolean running) {
SharedPreferences.Editor editor = mSettings.edit();
editor.putBoolean("service_running", running);
editor.putLong("last_seen", Utils.currentTimeInMillis());
editor.commit();
}
public void saveServiceRunningWithNullTimestamp(boolean running) {
SharedPreferences.Editor editor = mSettings.edit();
editor.putBoolean("service_running", running);
editor.putLong("last_seen", 0);
editor.commit();
}
public void clearServiceRunning() {
SharedPreferences.Editor editor = mSettings.edit();
editor.putBoolean("service_running", false);
editor.putLong("last_seen", 0);
editor.commit();
}
public boolean isServiceRunning() {
return mSettings.getBoolean("service_running", false);
}
public boolean isNewStart() {
// activity last paused more than 10 minutes ago
return mSettings.getLong("last_seen", 0) < Utils.currentTimeInMillis() - 1000*60*10;
}
}
6.Settings.java
package com.tdc.jibuqi;
import com.tdc.jibuqi.R;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class Settings extends PreferenceActivity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
7.SpeakingTimer
package com.tdc.jibuqi;
import java.util.ArrayList;
public class SpeakingTimer implements StepListener {
PedometerSettings mSettings;
Utils mUtils;
boolean mShouldSpeak;
float mInterval;
long mLastSpeakTime;
public SpeakingTimer(PedometerSettings settings, Utils utils) {
mLastSpeakTime = System.currentTimeMillis();
mSettings = settings;
mUtils = utils;
reloadSettings();
}
public void reloadSettings() {
mShouldSpeak = mSettings.shouldSpeak();
mInterval = mSettings.getSpeakingInterval();
}
public void onStep() {
long now = System.currentTimeMillis();
long delta = now - mLastSpeakTime;
if (delta / 60000.0 >= mInterval) {
mLastSpeakTime = now;
notifyListeners();
}
}
public void passValue() {
// not used
}
//-----------------------------------------------------
// Listener
public interface Listener {
public void speak();
}
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
public void addListener(Listener l) {
mListeners.add(l);
}
public void notifyListeners() {
mUtils.ding();
for (Listener listener : mListeners) {
listener.speak();
}
}
//-----------------------------------------------------
// Speaking
public boolean isSpeaking() {
return mUtils.isSpeakingNow();
}
}
8.SpeedNotifier.java
package com.tdc.jibuqi;
import java.util.Locale;
public class SpeedNotifier implements PaceNotifier.Listener, SpeakingTimer.Listener {
public interface Listener {
public void valueChanged(float value);
public void passValue();
}
private Listener mListener;
int mCounter = 0;
float mSpeed = 0;
boolean mIsMetric;
float mStepLength;
PedometerSettings mSettings;
Utils mUtils;
/** Desired speed, adjusted by the user */
float mDesiredSpeed;
/** Should we speak? */
boolean mShouldTellFasterslower;
boolean mShouldTellSpeed;
/** When did the TTS speak last time */
private long mSpokenAt = 0;
public SpeedNotifier(Listener listener, PedometerSettings settings, Utils utils) {
mListener = listener;
mUtils = utils;
mSettings = settings;
mDesiredSpeed = mSettings.getDesiredSpeed();
reloadSettings();
}
public void setSpeed(float speed) {
mSpeed = speed;
notifyListener();
}
public void reloadSettings() {
mIsMetric = mSettings.isMetric();
mStepLength = mSettings.getStepLength();
mShouldTellSpeed = mSettings.shouldTellSpeed();
mShouldTellFasterslower =
mSettings.shouldTellFasterslower()
&& mSettings.getMaintainOption() == PedometerSettings.M_SPEED;
notifyListener();
}
public void setDesiredSpeed(float desiredSpeed) {
mDesiredSpeed = desiredSpeed;
}
private void notifyListener() {
mListener.valueChanged(mSpeed);
}
public void paceChanged(int value) {
if (mIsMetric) {
mSpeed = // kilometers / hour
value * mStepLength // centimeters / minute
/ 100000f * 60f; // centimeters/kilometer
}
else {
mSpeed = // miles / hour
value * mStepLength // inches / minute
/ 63360f * 60f; // inches/mile
}
tellFasterSlower();
notifyListener();
}
/**
* Say slower/faster, if needed.
*/
private void tellFasterSlower() {
if (mShouldTellFasterslower && mUtils.isSpeakingEnabled()) {
long now = System.currentTimeMillis();
if (now - mSpokenAt > 3000 && !mUtils.isSpeakingNow()) {
float little = 0.10f;
float normal = 0.30f;
float much = 0.50f;
boolean spoken = true;
if(Locale.getDefault().getLanguage().equals("zh")&&Locale.getDefault().getCountry().equals("CN")){
if (mSpeed < mDesiredSpeed * (1 - much)) {
mUtils.say("您的速度离您的目标值还很远!");
}
else if (mSpeed > mDesiredSpeed * (1 + much)) {
mUtils.say("您的速度远远超过了目标速度!");
}
else
if (mSpeed < mDesiredSpeed * (1 - normal)) {
mUtils.say("您的速度离您的目标值还有点远!");
}
else
if (mSpeed > mDesiredSpeed * (1 + normal)) {
mUtils.say("您的速度已经超过了目标速度");
}
else
if (mSpeed < mDesiredSpeed * (1 - little)) {
mUtils.say("您的速度离您的目标值还差一点点!");
}
else
if (mSpeed > mDesiredSpeed * (1 + little)) {
mUtils.say("您的速度刚刚超过了目标速度!");
}
else {
spoken = false;
}
}else{
if (mSpeed < mDesiredSpeed * (1 - much)) {
mUtils.say("much faster!");
}
else
if (mSpeed > mDesiredSpeed * (1 + much)) {
mUtils.say("much slower!");
}
else
if (mSpeed < mDesiredSpeed * (1 - normal)) {
mUtils.say("faster!");
}
else
if (mSpeed > mDesiredSpeed * (1 + normal)) {
mUtils.say("slower!");
}
else
if (mSpeed < mDesiredSpeed * (1 - little)) {
mUtils.say("a little faster!");
}
else
if (mSpeed > mDesiredSpeed * (1 + little)) {
mUtils.say("a little slower!");
}
else {
spoken = false;
}
}
if (spoken) {
mSpokenAt = now;
}
}
}
}
public void passValue() {
// Not used
}
public void speak() {
if (mSettings.shouldTellSpeed()) {
if (mSpeed >= .01f) {
if(Locale.getDefault().getLanguage().equals("zh")&&Locale.getDefault().getCountry().equals("CN")){
mUtils.say("当前速度"+("" + (mSpeed + 0.000001f)).substring(0, 4) + (mIsMetric ? "公里每小时" : "英里每小时"));
}else{
mUtils.say(("" + (mSpeed + 0.000001f)).substring(0, 4) + (mIsMetric ? " kilometers per hour" : " miles per hour"));
}
}
}
}
}
9.StepBuzzer
package com.tdc.jibuqi;
import android.content.Context;
import android.os.Vibrator;
public class StepBuzzer implements StepListener {
private Context mContext;
private Vibrator mVibrator;
public StepBuzzer(Context context) {
mContext = context;
mVibrator = (Vibrator)mContext.getSystemService(Context.VIBRATOR_SERVICE);
}
public void onStep() {
buzz();
}
public void passValue() {
}
private void buzz() {
mVibrator.vibrate(50);
}
}
10.StepDetector
package com.tdc.jibuqi;
import java.util.ArrayList;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
public class StepDetector implements SensorEventListener
{
private final static String TAG = "StepDetector";
private float mLimit = 10;
private float mLastValues[] = new float[3*2];
private float mScale[] = new float[2];
private float mYOffset;
private float mLastDirections[] = new float[3*2];
private float mLastExtremes[][] = { new float[3*2], new float[3*2] };
private float mLastDiff[] = new float[3*2];
private int mLastMatch = -1;
private long start,end;
private ArrayList<StepListener> mStepListeners = new ArrayList<StepListener>();
public StepDetector() {
int h = 480; // TODO: remove this constant
mYOffset = h * 0.5f;
mScale[0] = - (h * 0.5f * (1.0f / (SensorManager.STANDARD_GRAVITY * 2)));
mScale[1] = - (h * 0.5f * (1.0f / (SensorManager.MAGNETIC_FIELD_EARTH_MAX)));
}
public void setSensitivity(float sensitivity) {
mLimit = sensitivity; // 1.97 2.96 4.44 6.66 10.00 15.00 22.50 33.75 50.62
}
public void addStepListener(StepListener sl) {
mStepListeners.add(sl);
}
//public void onSensorChanged(int sensor, float[] values) {
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
synchronized (this) {
if (sensor.getType() == Sensor.TYPE_ORIENTATION) {
}
else {
int j = (sensor.getType() == Sensor.TYPE_ACCELEROMETER) ? 1 : 0;
if (j == 1) {
float vSum = 0;
//System.out.println("x="+event.values[0]+",y="+event.values[1]+",z="+event.values[2]);
for (int i=0 ; i<3 ; i++) {
final float v = mYOffset + event.values[i] * mScale[j];
vSum += v;
}
int k = 0;
float v = vSum / 3;
float direction = (v > mLastValues[k] ? 1 : (v < mLastValues[k] ? -1 : 0));
//System.out.println("direction="+direction+",mLastValues[k]="+mLastValues[k]+", mLastDirections[k]="+mLastDirections[k]);
if (direction == - mLastDirections[k]) {
// Direction changed
int extType = (direction > 0 ? 0 : 1); // minumum or maximum?
mLastExtremes[extType][k] = mLastValues[k];
float diff = Math.abs(mLastExtremes[extType][k] - mLastExtremes[1 - extType][k]);
//System.out.println("mLimit="+ mLimit+",diff="+diff);
if (diff > mLimit) {
boolean isAlmostAsLargeAsPrevious = diff > (mLastDiff[k]*2/3);
boolean isPreviousLargeEnough = mLastDiff[k] > (diff/3);
boolean isNotContra = (mLastMatch != 1 - extType);
if (isAlmostAsLargeAsPrevious && isPreviousLargeEnough && isNotContra) {
end=System.currentTimeMillis();
//Log.i(TAG, "step");
if(end-start>500){
for (StepListener stepListener : mStepListeners) {
stepListener.onStep();
}
start=end;
mLastMatch = extType;
}
}
else {
mLastMatch = -1;
}
}
mLastDiff[k] = diff;
}
mLastDirections[k] = direction;
mLastValues[k] = v;
}
}
}
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
}
11.StepDisplayer
package com.tdc.jibuqi;
import java.util.ArrayList;
import java.util.Locale;
public class StepDisplayer implements StepListener, SpeakingTimer.Listener {
private int mCount = 0;
PedometerSettings mSettings;
Utils mUtils;
public StepDisplayer(PedometerSettings settings, Utils utils) {
mUtils = utils;
mSettings = settings;
notifyListener();
}
public void setUtils(Utils utils) {
mUtils = utils;
}
public void setSteps(int steps) {
mCount = steps;
notifyListener();
}
public void onStep() {
mCount ++;
notifyListener();
}
public void reloadSettings() {
notifyListener();
}
public void passValue() {
}
//-----------------------------------------------------
// Listener
public interface Listener {
public void stepsChanged(int value);
public void passValue();
}
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
public void addListener(Listener l) {
mListeners.add(l);
}
public void notifyListener() {
for (Listener listener : mListeners) {
listener.stepsChanged((int)mCount);
}
}
//-----------------------------------------------------
// Speaking
public void speak() {
if (mSettings.shouldTellSteps()) {
if (mCount > 0) {
if(Locale.getDefault().getLanguage().equals("zh")&&Locale.getDefault().getCountry().equals("CN")){
mUtils.say("您走了" + mCount + "步");
}else{
mUtils.say("" + mCount + " steps");
}
}
}
}
}
12.StepListener
package com.tdc.jibuqi;
public interface StepListener {
public void onStep();
public void passValue();
}
13.StepService
package com.tdc.jibuqi;
import com.tdc.jibuqi.R;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
public class StepService extends Service {
private static final String TAG = "name.bagi.levente.pedometer.StepService";
private SharedPreferences mSettings;
private PedometerSettings mPedometerSettings;
private SharedPreferences mState;
private SharedPreferences.Editor mStateEditor;
private Utils mUtils;
private SensorManager mSensorManager;
private Sensor mSensor;
private StepDetector mStepDetector;
// private StepBuzzer mStepBuzzer; // used for debugging
private StepDisplayer mStepDisplayer;
private PaceNotifier mPaceNotifier;
private DistanceNotifier mDistanceNotifier;
private SpeedNotifier mSpeedNotifier;
private CaloriesNotifier mCaloriesNotifier;
private SpeakingTimer mSpeakingTimer;
private PowerManager.WakeLock wakeLock;
private NotificationManager mNM;
private int mSteps;
private int mPace;
private float mDistance;
private float mSpeed;
private float mCalories;
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class StepBinder extends Binder {
StepService getService() {
return StepService.this;
}
}
@Override
public void onCreate() {
Log.i(TAG, "[SERVICE] onCreate");
super.onCreate();
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
showNotification();
// Load settings
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
mPedometerSettings = new PedometerSettings(mSettings);
mState = getSharedPreferences("state", 0);
mUtils = Utils.getInstance();
mUtils.setService(this);
mUtils.initTTS();
acquireWakeLock();
// Start detecting
mStepDetector = new StepDetector();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
registerDetector();
// Register our receiver for the ACTION_SCREEN_OFF action. This will make our receiver
// code be called whenever the phone enters standby mode.
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(mReceiver, filter);
mStepDisplayer = new StepDisplayer(mPedometerSettings, mUtils);
mStepDisplayer.setSteps(mSteps = mState.getInt("steps", 0));
mStepDisplayer.addListener(mStepListener);
mStepDetector.addStepListener(mStepDisplayer);
mPaceNotifier = new PaceNotifier(mPedometerSettings, mUtils);
mPaceNotifier.setPace(mPace = mState.getInt("pace", 0));
mPaceNotifier.addListener(mPaceListener);
mStepDetector.addStepListener(mPaceNotifier);
mDistanceNotifier = new DistanceNotifier(mDistanceListener, mPedometerSettings, mUtils);
mDistanceNotifier.setDistance(mDistance = mState.getFloat("distance", 0));
mStepDetector.addStepListener(mDistanceNotifier);
mSpeedNotifier = new SpeedNotifier(mSpeedListener, mPedometerSettings, mUtils);
mSpeedNotifier.setSpeed(mSpeed = mState.getFloat("speed", 0));
mPaceNotifier.addListener(mSpeedNotifier);
mCaloriesNotifier = new CaloriesNotifier(mCaloriesListener, mPedometerSettings, mUtils);
mCaloriesNotifier.setCalories(mCalories = mState.getFloat("calories", 0));
mStepDetector.addStepListener(mCaloriesNotifier);
mSpeakingTimer = new SpeakingTimer(mPedometerSettings, mUtils);
mSpeakingTimer.addListener(mStepDisplayer);
mSpeakingTimer.addListener(mPaceNotifier);
mSpeakingTimer.addListener(mDistanceNotifier);
mSpeakingTimer.addListener(mSpeedNotifier);
mSpeakingTimer.addListener(mCaloriesNotifier);
mStepDetector.addStepListener(mSpeakingTimer);
// Used when debugging:
// mStepBuzzer = new StepBuzzer(this);
// mStepDetector.addStepListener(mStepBuzzer);
// Start voice
reloadSettings();
// Tell the user we started.
Toast.makeText(this, getText(R.string.started), Toast.LENGTH_SHORT).show();
}
@Override
public void onStart(Intent intent, int startId) {
Log.i(TAG, "[SERVICE] onStart");
super.onStart(intent, startId);
}
@Override
public void onDestroy() {
Log.i(TAG, "[SERVICE] onDestroy");
mUtils.shutdownTTS();
// Unregister our receiver.
unregisterReceiver(mReceiver);
unregisterDetector();
mStateEditor = mState.edit();
mStateEditor.putInt("steps", mSteps);
mStateEditor.putInt("pace", mPace);
mStateEditor.putFloat("distance", mDistance);
mStateEditor.putFloat("speed", mSpeed);
mStateEditor.putFloat("calories", mCalories);
mStateEditor.commit();
mNM.cancel(R.string.app_name);
wakeLock.release();
super.onDestroy();
// Stop detecting
mSensorManager.unregisterListener(mStepDetector);
// Tell the user we stopped.
Toast.makeText(this, getText(R.string.stopped), Toast.LENGTH_SHORT).show();
}
private void registerDetector() {
mSensor = mSensorManager.getDefaultSensor(
Sensor.TYPE_ACCELEROMETER /*|
Sensor.TYPE_MAGNETIC_FIELD |
Sensor.TYPE_ORIENTATION*/);
mSensorManager.registerListener(mStepDetector,
mSensor,
SensorManager.SENSOR_DELAY_FASTEST);
}
private void unregisterDetector() {
mSensorManager.unregisterListener(mStepDetector);
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "[SERVICE] onBind");
return mBinder;
}
/**
* Receives messages from activity.
*/
private final IBinder mBinder = new StepBinder();
public interface ICallback {
public void stepsChanged(int value);
public void paceChanged(int value);
public void distanceChanged(float value);
public void speedChanged(float value);
public void caloriesChanged(float value);
}
private ICallback mCallback;
public void registerCallback(ICallback cb) {
mCallback = cb;
//mStepDisplayer.passValue();
//mPaceListener.passValue();
}
private int mDesiredPace;
private float mDesiredSpeed;
/**
* Called by activity to pass the desired pace value,
* whenever it is modified by the user.
* @param desiredPace
*/
public void setDesiredPace(int desiredPace) {
mDesiredPace = desiredPace;
if (mPaceNotifier != null) {
mPaceNotifier.setDesiredPace(mDesiredPace);
}
}
/**
* Called by activity to pass the desired speed value,
* whenever it is modified by the user.
* @param desiredSpeed
*/
public void setDesiredSpeed(float desiredSpeed) {
mDesiredSpeed = desiredSpeed;
if (mSpeedNotifier != null) {
mSpeedNotifier.setDesiredSpeed(mDesiredSpeed);
}
}
public void reloadSettings() {
mSettings = PreferenceManager.getDefaultSharedPreferences(this);
if (mStepDetector != null) {
mStepDetector.setSensitivity(
Float.valueOf(mSettings.getString("sensitivity", "10"))
);
}
if (mStepDisplayer != null) mStepDisplayer.reloadSettings();
if (mPaceNotifier != null) mPaceNotifier.reloadSettings();
if (mDistanceNotifier != null) mDistanceNotifier.reloadSettings();
if (mSpeedNotifier != null) mSpeedNotifier.reloadSettings();
if (mCaloriesNotifier != null) mCaloriesNotifier.reloadSettings();
if (mSpeakingTimer != null) mSpeakingTimer.reloadSettings();
}
public void resetValues() {
mStepDisplayer.setSteps(0);
mPaceNotifier.setPace(0);
mDistanceNotifier.setDistance(0);
mSpeedNotifier.setSpeed(0);
mCaloriesNotifier.setCalories(0);
}
/**
* Forwards pace values from PaceNotifier to the activity.
*/
private StepDisplayer.Listener mStepListener = new StepDisplayer.Listener() {
public void stepsChanged(int value) {
mSteps = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.stepsChanged(mSteps);
}
}
};
/**
* Forwards pace values from PaceNotifier to the activity.
*/
private PaceNotifier.Listener mPaceListener = new PaceNotifier.Listener() {
public void paceChanged(int value) {
mPace = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.paceChanged(mPace);
}
}
};
/**
* Forwards distance values from DistanceNotifier to the activity.
*/
private DistanceNotifier.Listener mDistanceListener = new DistanceNotifier.Listener() {
public void valueChanged(float value) {
mDistance = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.distanceChanged(mDistance);
}
}
};
/**
* Forwards speed values from SpeedNotifier to the activity.
*/
private SpeedNotifier.Listener mSpeedListener = new SpeedNotifier.Listener() {
public void valueChanged(float value) {
mSpeed = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.speedChanged(mSpeed);
}
}
};
/**
* Forwards calories values from CaloriesNotifier to the activity.
*/
private CaloriesNotifier.Listener mCaloriesListener = new CaloriesNotifier.Listener() {
public void valueChanged(float value) {
mCalories = value;
passValue();
}
public void passValue() {
if (mCallback != null) {
mCallback.caloriesChanged(mCalories);
}
}
};
/**
* Show a notification while this service is running.
*/
private void showNotification() {
CharSequence text = getText(R.string.app_name);
Notification notification = new Notification(R.drawable.ic_notification, null,
System.currentTimeMillis());
notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
Intent pedometerIntent = new Intent();
pedometerIntent.setComponent(new ComponentName(this, Pedometer.class));
pedometerIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
pedometerIntent, 0);
notification.setLatestEventInfo(this, text,
getText(R.string.notification_subtitle), contentIntent);
mNM.notify(R.string.app_name, notification);
}
// BroadcastReceiver for handling ACTION_SCREEN_OFF.
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Check action just to be on the safe side.
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// Unregisters the listener and registers it again.
StepService.this.unregisterDetector();
StepService.this.registerDetector();
if (mPedometerSettings.wakeAggressively()) {
wakeLock.release();
acquireWakeLock();
}
}
}
};
private void acquireWakeLock() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
int wakeFlags;
if (mPedometerSettings.wakeAggressively()) {
wakeFlags = PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP;
}
else if (mPedometerSettings.keepScreenOn()) {
wakeFlags = PowerManager.SCREEN_DIM_WAKE_LOCK;
}
else {
wakeFlags = PowerManager.PARTIAL_WAKE_LOCK;
}
wakeLock = pm.newWakeLock(wakeFlags, TAG);
wakeLock.acquire();
}
}
14.Utils
package com.tdc.jibuqi;
import java.util.Locale;
import android.app.Service;
import android.speech.tts.TextToSpeech;
import android.text.format.Time;
import android.util.Log;
public class Utils implements TextToSpeech.OnInitListener {
private static final String TAG = "Utils";
private Service mService;
private static Utils instance = null;
private Utils() {
}
public static Utils getInstance() {
if (instance == null) {
instance = new Utils();
}
return instance;
}
public void setService(Service service) {
mService = service;
}
/********** SPEAKING **********/
private TextToSpeech mTts;
private boolean mSpeak = false;
private boolean mSpeakingEngineAvailable = false;
public void initTTS() {
// Initialize text-to-speech. This is an asynchronous operation.
// The OnInitListener (second argument) is called after initialization completes.
Log.i(TAG, "Initializing TextToSpeech...");
mTts = new TextToSpeech(mService,this);
}
public void shutdownTTS() {
Log.i(TAG, "Shutting Down TextToSpeech...");
mSpeakingEngineAvailable = false;
mTts.shutdown();
Log.i(TAG, "TextToSpeech Shut Down.");
}
public void say(String text) {
if (mSpeak && mSpeakingEngineAvailable) {
mTts.speak(text,
TextToSpeech.QUEUE_ADD, // Drop all pending entries in the playback queue.
null);
}
}
// Implements TextToSpeech.OnInitListener.
public void onInit(int status) {
// status can be either TextToSpeech.SUCCESS or TextToSpeech.ERROR.
if (status == TextToSpeech.SUCCESS) {
int result;
if(Locale.getDefault().getLanguage().equals("zh")&&
Locale.getDefault().getCountry().equals("CN")){
result = mTts.setLanguage(Locale.CHINESE);
}else{
result = mTts.setLanguage(Locale.US);
}
if (result == TextToSpeech.LANG_MISSING_DATA ||
result == TextToSpeech.LANG_NOT_SUPPORTED) {
// Language data is missing or the language is not supported.
Log.e(TAG, "Language is not available.");
} else {
Log.i(TAG, "TextToSpeech Initialized.");
mSpeakingEngineAvailable = true;
}
} else {
// Initialization failed.
Log.e(TAG, "Could not initialize TextToSpeech.");
}
}
public void setSpeak(boolean speak) {
mSpeak = speak;
}
public boolean isSpeakingEnabled() {
return mSpeak;
}
public boolean isSpeakingNow() {
return mTts.isSpeaking();
}
public void ding() {
}
/********** Time **********/
public static long currentTimeInMillis() {
Time time = new Time();
time.setToNow();
return time.toMillis(false);
}
}
15.com.tdc.jibuqi.preferences.BodyWeightPreference
package com.tdc.jibuqi.preferences;
import com.tdc.jibuqi.R;
import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
public class BodyWeightPreference extends EditMeasurementPreference {
public BodyWeightPreference(Context context) {
super(context);
}
public BodyWeightPreference(Context context, AttributeSet attr) {
super(context, attr);
}
public BodyWeightPreference(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
}
protected void initPreferenceDetails() {
mTitleResource = R.string.body_weight_setting_title;
mMetricUnitsResource = R.string.kilograms;
mImperialUnitsResource = R.string.pounds;
}
}
16.com.tdc.jibuqi.preferences.EditMeasurementPreference
package com.tdc.jibuqi.preferences;
import android.content.Context;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
abstract public class EditMeasurementPreference extends EditTextPreference {
boolean mIsMetric;
protected int mTitleResource;
protected int mMetricUnitsResource;
protected int mImperialUnitsResource;
public EditMeasurementPreference(Context context) {
super(context);
initPreferenceDetails();
}
public EditMeasurementPreference(Context context, AttributeSet attr) {
super(context, attr);
initPreferenceDetails();
}
public EditMeasurementPreference(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
initPreferenceDetails();
}
abstract protected void initPreferenceDetails();
protected void showDialog(Bundle state) {
mIsMetric = PreferenceManager.getDefaultSharedPreferences(getContext()).getString("units", "metric").equals("metric");
setDialogTitle(
getContext().getString(mTitleResource) +
" (" +
getContext().getString(
mIsMetric
? mMetricUnitsResource
: mImperialUnitsResource) +
")"
);
try {
Float.valueOf(getText());
}
catch (Exception e) {
setText("70");
}
super.showDialog(state);
}
protected void onAddEditTextToDialogView (View dialogView, EditText editText) {
editText.setRawInputType(
InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
super.onAddEditTextToDialogView(dialogView, editText);
}
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
try {
Float.valueOf(((CharSequence)(getEditText().getText())).toString());
}
catch (NumberFormatException e) {
this.showDialog(null);
return;
}
}
super.onDialogClosed(positiveResult);
}
}
17.com.tdc.jibuqi.preferences.StepLengthPreference
package com.tdc.jibuqi.preferences;
import com.tdc.jibuqi.R;
import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
public class StepLengthPreference extends EditMeasurementPreference {
public StepLengthPreference(Context context) {
super(context);
}
public StepLengthPreference(Context context, AttributeSet attr) {
super(context, attr);
}
public StepLengthPreference(Context context, AttributeSet attr, int defStyle) {
super(context, attr, defStyle);
}
protected void initPreferenceDetails() {
mTitleResource = R.string.step_length_setting_title;
mMetricUnitsResource = R.string.centimeters;
mImperialUnitsResource = R.string.inches;
}
}
18.layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="@dimen/margin"
android:background="@color/screen_background">
<LinearLayout android:id="@+id/row_1"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/row_spacing">
<LinearLayout android:id="@+id/box_steps"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:paddingRight="@dimen/margin"
android:layout_weight="1">
<TextView android:id="@+id/step_value"
android:textSize="@dimen/value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="@color/display_background"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:paddingTop="@dimen/padding"
android:text=""/>
<TextView android:id="@+id/step_units"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/units"
android:text="@string/steps"
android:background="@color/display_background"
android:paddingBottom="@dimen/padding"/>
</LinearLayout>
<LinearLayout android:id="@+id/box_distance"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1">
<TextView android:id="@+id/distance_value"
android:textSize="@dimen/value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="@color/display_background"
android:paddingTop="@dimen/padding"
android:paddingRight="@dimen/padding"
android:paddingLeft="@dimen/padding"
android:text=""/>
<TextView android:id="@+id/distance_units"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/units"
android:text="@string/kilometers"
android:background="@color/display_background"
android:paddingBottom="@dimen/padding"/>
</LinearLayout>
</LinearLayout>
<LinearLayout android:id="@+id/row_2"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/row_spacing">
<LinearLayout android:id="@+id/box_pace"
android:orientation="vertical"
android:layout_height="wrap_content"
android:paddingRight="@dimen/margin"
android:layout_width="fill_parent"
android:layout_weight="1">
<TextView android:id="@+id/pace_value"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/display_background"
android:textSize="@dimen/small_value"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:paddingTop="@dimen/padding"
android:text=""/>
<TextView android:id="@+id/pace_units"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/units"
android:text="@string/steps_per_minute"
android:paddingBottom="@dimen/padding"
android:background="@color/display_background"/>
</LinearLayout>
<LinearLayout android:id="@+id/box_speed"
android:orientation="vertical"
android:paddingRight="@dimen/margin"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1">
<TextView android:id="@+id/speed_value"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/display_background"
android:textSize="@dimen/small_value"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:paddingTop="@dimen/padding"
android:text=""/>
<TextView android:id="@+id/speed_units"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/units"
android:text="@string/kilometers_per_hour"
android:paddingBottom="@dimen/padding"
android:background="@color/display_background"/>
</LinearLayout>
<LinearLayout android:id="@+id/box_calories"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1">
<TextView android:id="@+id/calories_value"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/display_background"
android:textSize="@dimen/small_value"
android:paddingLeft="@dimen/padding"
android:paddingRight="@dimen/padding"
android:paddingTop="@dimen/padding"
android:text=""/>
<TextView android:id="@+id/calories_units"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/units"
android:text="@string/calories_burned"
android:paddingBottom="@dimen/padding"
android:background="@color/display_background"/>
</LinearLayout>
</LinearLayout>
<!-- Desired pace/speed row -->
<LinearLayout
android:id="@+id/desired_pace_control"
android:paddingTop="@dimen/row_spacing"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<!-- Button "-", for decrementing desired pace/speed -->
<Button android:id="@+id/button_desired_pace_lower"
android:text="-"
android:textSize="@dimen/button_sign"
android:layout_width="@dimen/button"
android:layout_height="@dimen/button"/>
<!-- Container for desired pace/speed -->
<LinearLayout
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_width="@dimen/desired_pace_width"
android:layout_height="wrap_content">
<TextView android:id="@+id/desired_pace_label"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/desired_pace"
android:textSize="@dimen/desired_pace_label"/>
<!-- Current desired pace/speed -->
<TextView android:id="@+id/desired_pace_value"
android:gravity="center_horizontal"
android:textSize="@dimen/desired_pace"
android:layout_width="@dimen/desired_pace_width"
android:layout_height="wrap_content"/>
</LinearLayout>
<!-- Button "+", for incrementing desired pace/speed -->
<Button android:id="@+id/button_desired_pace_raise"
android:text="+"
android:textSize="@dimen/button_sign"
android:layout_width="@dimen/button"
android:layout_height="@dimen/button"/>
</LinearLayout>
</LinearLayout>
19.arrays.xml
<resources>
<string-array name="sensitivity_preference">
<item>@string/extra_high</item>
<item>@string/very_high</item>
<item>@string/high</item>
<item>@string/higher</item>
<item>@string/medium</item>
<item>@string/lower</item>
<item>@string/low</item>
<item>@string/very_low</item>
<item>@string/extra_low</item>
</string-array>
<string-array name="sensitivity_preference_values">
<item>1.9753</item>
<item>2.9630</item>
<item>4.4444</item><!-- 10 * 1.5^(-2) -->
<item>6.6667</item><!-- 10 * 1.5^(-1) -->
<item>10</item>
<item>15</item><!-- 10 * 1.5 -->
<item>22.5</item><!-- 10 * 1.5^2 -->
<item>33.75</item>
<item>50.625</item>
</string-array>
<string-array name="operation_level_preference">
<item>@string/run_in_background</item>
<item>@string/keep_screen_on</item>
<item>@string/wake_up</item>
</string-array>
<string-array name="operation_level_preference_values">
<item>run_in_background</item>
<item>keep_screen_on</item>
<item>wake_up</item>
</string-array>
<string-array name="units_preference">
<item>@string/units_metric</item>
<item>@string/units_imperial</item>
</string-array>
<string-array name="units_preference_values">
<item>metric</item>
<item>imperial</item>
</string-array>
<string-array name="exercise_type_preference">
<item>@string/exercise_type_running</item>
<item>@string/exercise_type_walking</item>
</string-array>
<string-array name="exercise_type_preference_values">
<item>running</item>
<item>walking</item>
</string-array>
<string-array name="maintain_preference">
<item>@string/maintain_nothing</item>
<item>@string/maintain_pace</item>
<item>@string/maintain_speed</item>
</string-array>
<string-array name="maintain_preference_values">
<item>none</item>
<item>pace</item>
<item>speed</item>
</string-array>
<string-array name="speaking_interval_preference">
<item>@string/interval_15_seconds</item>
<item>@string/interval_30_seconds</item>
<item>@string/interval_1_minute</item>
<item>@string/interval_2_minutes</item>
<item>@string/interval_5_minutes</item>
<item>@string/interval_10_minutes</item>
</string-array>
<string-array name="speaking_interval_preference_values">
<item>0.25</item>
<item>0.5</item>
<item>1</item>
<item>2</item>
<item>5</item>
<item>10</item>
</string-array>
</resources>
20.colors.xml
<resources>
<color name="screen_background">#333</color>
<color name="display_background">#000</color>
</resources>
21.dimens.xml
<resources>
<dimen name="large_half">240px</dimen>
<dimen name="value">40sp</dimen>
<dimen name="small_value">40sp</dimen>
<!-- <dimen name="step_count">30sp</dimen> -->
<dimen name="units">16sp</dimen>
<dimen name="desired_pace">30sp</dimen>
<dimen name="desired_pace_label">18sp</dimen>
<dimen name="desired_pace_width">150sp</dimen>
<dimen name="button">60sp</dimen>
<dimen name="button_sign">30sp</dimen>
<dimen name="margin">6px</dimen>
<dimen name="padding">5px</dimen>
<dimen name="row_spacing">6px</dimen>
</resources>
22.strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Pedometer</string>
<string name="notification_subtitle">Counting your steps</string>
<string name="started">Pedometer started.</string>
<string name="stopped">Pedometer stopped.</string>
<string name="steps">steps</string>
<string name="kilometers">kilometers</string>
<string name="miles">miles</string>
<string name="steps_per_minute">steps / minute</string>
<string name="kilometers_per_hour">kilometers / hour</string>
<string name="miles_per_hour">miles / hour</string>
<string name="calories_burned">calories burned</string>
<string name="centimeters">centimeters</string>
<string name="inches">inches</string>
<string name="kilograms">kilograms</string>
<string name="pounds">pounds</string>
<string name="desired_pace">Desired pace:</string>
<string name="desired_speed">Desired speed:</string>
<string name="pause">Pause</string>
<string name="resume">Resume</string>
<string name="reset">Reset</string>
<!-- Settings -->
<string name="settings">Settings</string>
<string name="activity_settings">Pedometer Settings</string>
<string name="steps_settings_title">Basic Settings</string>
<string name="sensitivity_setting">Sensitivity</string>
<string name="sensitivity_setting_details">Calibrate the step detector</string>
<string name="sensitivity_setting_title">Select sensitivity level</string>
<string name="extra_high">extra high</string>
<string name="very_high">very high</string>
<string name="high">high</string>
<string name="higher">higher</string>
<string name="medium">medium</string>
<string name="lower">lower</string>
<string name="low">low</string>
<string name="very_low">very low</string>
<string name="extra_low">extra low</string>
<string name="operation_level_setting">Operational level</string>
<string name="operation_level_setting_details">Some OS versions prevent using sensors when the screen is off; set the level that works for you</string>
<string name="operation_level_setting_title">Select operational level</string>
<string name="run_in_background">Normal (just try running in background)</string>
<string name="keep_screen_on">Screen on (keep screen dim)</string>
<string name="wake_up">Aggressive (wake up when I turn screen off)</string>
<string name="units_setting">Units</string>
<string name="units_setting_details">Imperial or metric units</string>
<string name="units_setting_title">Choose your units</string>
<string name="units_metric">metric (kilometers)</string>
<string name="units_imperial">imperial (miles)</string>
<string name="step_length_setting">Step length</string>
<string name="step_length_setting_details">For distance, speed & calorie calculation</string>
<string name="step_length_setting_title">Enter your step length</string>
<string name="body_weight_setting">Body weight</string>
<string name="body_weight_setting_details">For calorie calculation</string>
<string name="body_weight_setting_title">Enter your body weight</string>
<string name="exercise_type_setting">Exercise type</string>
<string name="exercise_type_setting_details">For calorie calculation</string>
<string name="exercise_type_setting_title">Choose exercise type</string>
<string name="exercise_type_running">running</string>
<string name="exercise_type_walking">walking</string>
<string name="maintain_setting">Maintain pace/speed</string>
<string name="maintain_setting_details">Maintaining a desired a pace/speed</string>
<string name="maintain_setting_title">Maintain:</string>
<string name="maintain_nothing">nothing</string>
<string name="maintain_pace">pace</string>
<string name="maintain_speed">speed</string>
<string name="voice_settings_title">Voice</string>
<string name="voice_setting">Speak</string>
<string name="voice_setting_details">Enable/disable voice notification</string>
<string name="speaking_interval_setting">Interval</string>
<string name="speaking_interval_setting_details">Voice notification interval</string>
<string name="interval_15_seconds">15 seconds</string>
<string name="interval_30_seconds">30 seconds</string>
<string name="interval_1_minute">1 minute</string>
<string name="interval_2_minutes">2 minutes</string>
<string name="interval_5_minutes">5 minutes</string>
<string name="interval_10_minutes">10 minutes</string>
<string name="tell_what">What to tell</string>
<string name="tell_steps_setting">Steps</string>
<string name="tell_steps_setting_details"></string>
<string name="tell_pace_setting">Pace</string>
<string name="tell_pace_setting_details">steps / minute</string>
<string name="tell_distance_setting">Distance</string>
<string name="tell_distance_setting_details">kilometers or miles</string>
<string name="tell_speed_setting">Speed</string>
<string name="tell_speed_setting_details">km/h or mph</string>
<string name="tell_calories_setting">Calories burned</string>
<string name="tell_calories_setting_details"></string>
<string name="tell_fasterslower_setting">Faster/slower</string>
<string name="tell_fasterslower_setting_details">Helps you maintain your desired pace or speed (interval doesn\'t apply)</string>
<string name="incorrect_step_length">Incorrect step length format! Go to the Settings to change it!</string>
<string name="quit">Quit</string>
</resources>
23.styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MyStyle" parent="android:Theme.Holo">
<item name="android:windowFullscreen">true</item>
<item name="android:windowNoTitle">false</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowShowWallpaper">false</item>
</style>
</resources>
24.中文strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">计步器</string>
<string name="notification_subtitle">正在计步中</string>
<string name="started">计步器开始</string>
<string name="stopped">计步器停止</string>
<string name="steps">步数</string>
<string name="kilometers">公里</string>
<string name="miles">英里</string>
<string name="steps_per_minute">步/分钟</string>
<string name="kilometers_per_hour">公里/小时</string>
<string name="miles_per_hour">英里/小时</string>
<string name="calories_burned">消耗卡路里</string>
<string name="centimeters">厘米</string>
<string name="inches">英寸</string>
<string name="kilograms">千克</string>
<string name="pounds">英镑</string>
<string name="desired_pace">设置目标步伐</string>
<string name="desired_speed">设置目标速度</string>
<string name="pause">暂停</string>
<string name="resume">继续</string>
<string name="reset">重置</string>
<!-- Settings -->
<string name="settings">设置</string>
<string name="activity_settings">计步器设置</string>
<string name="steps_settings_title">基本设置</string>
<string name="sensitivity_setting">灵敏度</string>
<string name="sensitivity_setting_details">校准步骤检测器</string>
<string name="sensitivity_setting_title">选择敏感水平</string>
<string name="extra_high">超高</string>
<string name="very_high">非常高</string>
<string name="high">很高</string>
<string name="higher">比较高</string>
<string name="medium">一般</string>
<string name="lower">低</string>
<string name="low">比较低</string>
<string name="very_low">非常低</string>
<string name="extra_low">超低</string>
<string name="operation_level_setting">运行模式</string>
<string name="operation_level_setting_details">当屏幕关闭的时候,操作系统可能会关闭一些传感器,为了程序正常运行,需要您设置合适的模式</string>
<string name="operation_level_setting_title">选择运行模式</string>
<string name="run_in_background">CPU保持运行,屏幕和按键背光灯关闭</string>
<string name="keep_screen_on">CPU保持运行,屏幕变暗,按键背光灯关闭</string>
<string name="wake_up">CPU保持运行,屏幕变暗,按键背光灯关闭,并立即生效</string>
<string name="units_setting">单位</string>
<string name="units_setting_details">英制或公制单位</string>
<string name="units_setting_title">选择你的单位</string>
<string name="units_metric">公里</string>
<string name="units_imperial">英寸</string>
<string name="step_length_setting">步长</string>
<string name="step_length_setting_details">用于计算距离</string>
<string name="step_length_setting_title">输入您的步长</string>
<string name="body_weight_setting">体重</string>
<string name="body_weight_setting_details">用于计算热量</string>
<string name="body_weight_setting_title">输入您的体重</string>
<string name="exercise_type_setting">运动方式</string>
<string name="exercise_type_setting_details">用于计算热量</string>
<string name="exercise_type_setting_title">选择运动方式</string>
<string name="exercise_type_running">跑步</string>
<string name="exercise_type_walking">走路</string>
<string name="maintain_setting">目标步伐/速度</string>
<string name="maintain_setting_details">设置运动时目标步伐或者目标速度</string>
<string name="maintain_setting_title">设置</string>
<string name="maintain_nothing">不设置</string>
<string name="maintain_pace">步伐</string>
<string name="maintain_speed">速度</string>
<string name="voice_settings_title">声音</string>
<string name="voice_setting">讲话</string>
<string name="voice_setting_details">启用/禁用语音通知</string>
<string name="speaking_interval_setting">间隔</string>
<string name="speaking_interval_setting_details">语音通知间隔时间</string>
<string name="interval_15_seconds">15秒</string>
<string name="interval_30_seconds">30秒</string>
<string name="interval_1_minute">1分钟</string>
<string name="interval_2_minutes">2分钟</string>
<string name="interval_5_minutes">5分钟</string>
<string name="interval_10_minutes">10分钟</string>
<string name="tell_what">说话内容</string>
<string name="tell_steps_setting">步数</string>
<string name="tell_steps_setting_details"></string>
<string name="tell_pace_setting">步伐</string>
<string name="tell_pace_setting_details">步/分钟</string>
<string name="tell_distance_setting">距离</string>
<string name="tell_distance_setting_details">公里或者英里</string>
<string name="tell_speed_setting">速度</string>
<string name="tell_speed_setting_details">公里每小时或者米每小时</string>
<string name="tell_calories_setting">消耗卡路里</string>
<string name="tell_calories_setting_details"></string>
<string name="tell_fasterslower_setting">快/慢</string>
<string name="tell_fasterslower_setting_details">有助于你保持所需的步伐或速度(间隔不适用)</string>
<string name="incorrect_step_length">不正确的步骤长度格式,请重新设置!</string>
<string name="quit">退出</string>
</resources>