计步ok了

This commit is contained in:
ScorpioMiku
2018-08-28 13:31:46 +08:00
parent 8b1d6a6527
commit c57388dc94
39 changed files with 2495 additions and 32 deletions

View File

@@ -0,0 +1,70 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.today.step.lib">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<!-- 协处理器计步权限 -->
<uses-feature
android:name="android.hardware.sensor.stepcounter"
android:required="true" />
<uses-feature
android:name="android.hardware.sensor.stepdetector"
android:required="true" />
<application>
<!--计步Service-->
<service
android:name=".TodayStepService"
android:enabled="true"
android:exported="false"
android:process=":todaystep"
android:launchMode="singleInstance"
android:priority="1000">
<intent-filter>
<!-- 系统启动完成后会调用 -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DATE_CHANGED" />
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="android.intent.action.ACTION_TIME_TICK" />
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</service>
<!--开机自启动-->
<receiver
android:name=".TodayStepBootCompleteReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- 关机广播 -->
<receiver android:name=".TodayStepShutdownReceiver" >
<intent-filter>
<!-- 关机广播 -->
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
</intent-filter>
</receiver>
<!--0点分隔alertmanager-->
<receiver
android:name=".TodayStepAlertReceive"
android:enabled="true"
android:exported="false"></receiver>
<service android:name=".JobSchedulerService"
android:permission="android.permission.BIND_JOB_SERVICE" />
</application>
</manifest>

View File

@@ -0,0 +1,35 @@
// ISportStepInterface.aidl
package com.today.step.lib;
interface ISportStepInterface {
/**
* 获取当前时间运动步数
*/
int getCurrentTimeSportStep();
/**
* 获取所有步数列表json格式如果数据过多建议在线程中获取否则会阻塞UI线程
*/
String getTodaySportStepArray();
/**
* 根据时间获取步数列表
*
* @param dateString 格式yyyy-MM-dd
* @return
*/
String getTodaySportStepArrayByDate(String date);
/**
* 根据时间和天数获取步数列表
* 例如:
* startDate = 2018-01-15
* days = 3
* 获取 2018-01-15、2018-01-16、2018-01-17三天的步数
*
* @param startDate 格式yyyy-MM-dd
* @param days
* @return
*/
String getTodaySportStepArrayByStartDateAndDays(String date, int days);
}

View File

@@ -0,0 +1,4 @@
# This is a simple Microlog configuration file
microlog.level=DEBUG
microlog.appender=LogCatAppender;FileAppender
microlog.formatter=SimpleFormatter

View File

@@ -0,0 +1,12 @@
package com.today.step.lib;
import android.content.BroadcastReceiver;
/**
* 通知栏点击通知
* Created by jiahongfei on 2017/10/19.
*/
public abstract class BaseClickBroadcast extends BroadcastReceiver {
}

View File

@@ -0,0 +1,72 @@
package com.today.step.lib;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Description: 时间工具类(时间格式转换方便类)
*/
class DateUtils {
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat();
/**
* 返回一定格式的当前时间
*
* @param pattern "yyyy-MM-dd HH:mm:ss E"
* @return
*/
public static String getCurrentDate(String pattern) {
SIMPLE_DATE_FORMAT.applyPattern(pattern);
Date date = new Date(System.currentTimeMillis());
String dateString = SIMPLE_DATE_FORMAT.format(date);
return dateString;
}
public static long getDateMillis(String dateString, String pattern) {
long millionSeconds = 0;
SIMPLE_DATE_FORMAT.applyPattern(pattern);
try {
millionSeconds = SIMPLE_DATE_FORMAT.parse(dateString).getTime();
} catch (ParseException e) {
e.printStackTrace();
}// 毫秒
return millionSeconds;
}
/**
* 格式化输入的millis
*
* @param millis
* @param pattern yyyy-MM-dd HH:mm:ss E
* @return
*/
public static String dateFormat(long millis, String pattern) {
SIMPLE_DATE_FORMAT.applyPattern(pattern);
Date date = new Date(millis);
String dateString = SIMPLE_DATE_FORMAT.format(date);
return dateString;
}
/**
* 将dateString原来old格式转换成new格式
*
* @param dateString
* @param oldPattern yyyy-MM-dd HH:mm:ss E
* @param newPattern
* @return oldPattern和dateString形式不一样直接返回dateString
*/
public static String dateFormat(String dateString, String oldPattern,
String newPattern) {
long millis = getDateMillis(dateString, oldPattern);
if (0 == millis) {
return dateString;
}
String date = dateFormat(millis, newPattern);
return date;
}
}

View File

@@ -0,0 +1,29 @@
package com.today.step.lib;
import java.util.List;
/**
* @author : jiahongfei
* @email : jiahongfeinew@163.com
* @date : 2018/1/22
* @desc :
*/
interface ITodayStepDBHelper {
void createTable();
void deleteTable();
void clearCapacity(String curDate, int limit);
boolean isExist(TodayStepData todayStepData);
void insert(TodayStepData todayStepData);
List<TodayStepData> getQueryAll();
List<TodayStepData> getStepListByDate(String dateString);
List<TodayStepData> getStepListByStartDateAndDays(String startDate, int days);
}

View File

@@ -0,0 +1,35 @@
package com.today.step.lib;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.RequiresApi;
/**
*
* Created by jiahongfei on 2017/10/13.
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class JobSchedulerService extends JobService {
private static final String TAG = "JobSchedulerService";
@Override
public boolean onStartJob(JobParameters params) {
Intent intent = new Intent(getApplication(), TodayStepService.class);
getApplication().startService(intent);
// Toast.makeText(getApplicationContext(), "onStartJob", Toast.LENGTH_SHORT).show();
Logger.e(TAG,"onStartJob");
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}

View File

@@ -0,0 +1,66 @@
package com.today.step.lib;
import android.text.TextUtils;
import android.util.Log;
/**
* 日志打印工具类,封装到一起,是为了调用时方便
*
* @author Administrator
*/
class Logger {
private static final String TAG = "Logger";
public static boolean sIsDebug = BuildConfig.TODAY_STEP_DEBUG;
public static void v(String message) {
if (sIsDebug)
Log.v(TAG, message);
}
public static void v(String tag, String message) {
if (sIsDebug)
Log.v(tag, message);
}
public static void d(String message) {
if (sIsDebug)
Log.d(TAG, message);
}
public static void i(String message) {
if (sIsDebug)
Log.i(TAG, message);
}
public static void i(String tag, String message) {
if (sIsDebug)
Log.i(tag, message);
}
public static void w(String message) {
if (sIsDebug)
Log.w(TAG, message);
}
public static void w(String tag, String message) {
if (sIsDebug)
Log.w(tag, message);
}
public static void e(String message) {
if (sIsDebug)
Log.e(TAG, message);
}
public static void e(String tag, String message) {
if (sIsDebug)
Log.e(tag, message);
}
public static void d(String tag, String message) {
if (!TextUtils.isEmpty(message) && sIsDebug) {
Log.d(TextUtils.isEmpty(tag) ? TAG : tag, message);
}
}
}

View File

@@ -0,0 +1,35 @@
package com.today.step.lib;
import android.content.Context;
import com.google.code.microlog4android.LoggerFactory;
import com.google.code.microlog4android.appender.FileAppender;
import com.google.code.microlog4android.config.PropertyConfigurator;
/**
* @author : jiahongfei
* @email : jiahongfeinew@163.com
* @date : 2018/2/6
* @desc :
*/
public class Microlog4Android {
private static final com.google.code.microlog4android.Logger logger = LoggerFactory.getLogger();
public void configure(Context context){
if(null != logger) {
PropertyConfigurator.getConfigurator(context).configure();
FileAppender appender = (FileAppender) logger.getAppender(1);
appender.setAppend(true);
logger.addAppender(appender);
}
}
public void error(Object message){
if(null != logger) {
logger.error(message);
}
}
}

View File

@@ -0,0 +1,20 @@
package com.today.step.lib;
/**
* Created by jiahongfei on 2017/6/30.
*/
interface OnStepCounterListener {
/**
* 用于显示步数
* @param step
*/
void onChangeStepCounter(int step);
/**
* 步数清零监听由于跨越0点需要重新计步
*/
void onStepCounterClean();
}

View File

@@ -0,0 +1,119 @@
package com.today.step.lib;
import android.content.Context;
import android.content.SharedPreferences;
/**
* @ClassName: PreferencesHelper
* @Description: (公用类用于缓存一些key——value类型的数据)
*/
class PreferencesHelper {
private static final String TAG = "PreferencesHelper";
public static final String APP_SHARD = "today_step_share_prefs";
// 上一次计步器的步数
public static final String LAST_SENSOR_TIME = "last_sensor_time";
// 步数补偿数值,每次传感器返回的步数-offset=当前步数
public static final String STEP_OFFSET = "step_offset";
// 当天,用来判断是否跨天
public static final String STEP_TODAY = "step_today";
// 清除步数
public static final String CLEAN_STEP = "clean_step";
// 当前步数
public static final String CURR_STEP = "curr_step";
//手机关机监听
public static final String SHUTDOWN = "shutdown";
//系统运行时间
public static final String ELAPSED_REALTIMEl = "elapsed_realtime";
/**
* Get SharedPreferences
*/
private static SharedPreferences getSharedPreferences(Context context) {
return context.getSharedPreferences(APP_SHARD, Context.MODE_PRIVATE);
}
public static void setLastSensorStep(Context context, float lastSensorStep){
Logger.e(TAG, "setLastSensorStep");
getSharedPreferences(context).edit().putFloat(LAST_SENSOR_TIME,lastSensorStep).commit();
}
public static float getLastSensorStep(Context context){
Logger.e(TAG, "getLastSensorStep");
return getSharedPreferences(context).getFloat(LAST_SENSOR_TIME,0.0f);
}
public static void setStepOffset(Context context, float stepOffset){
Logger.e(TAG, "setStepOffset");
getSharedPreferences(context).edit().putFloat(STEP_OFFSET,stepOffset).commit();
}
public static float getStepOffset(Context context){
Logger.e(TAG, "getStepOffset");
return getSharedPreferences(context).getFloat(STEP_OFFSET,0.0f);
}
public static void setStepToday(Context context, String stepToday){
Logger.e(TAG, "setStepToday");
getSharedPreferences(context).edit().putString(STEP_TODAY,stepToday).commit();
}
public static String getStepToday(Context context){
Logger.e(TAG, "getStepToday");
return getSharedPreferences(context).getString(STEP_TODAY,"");
}
/**
* true清除步数从0开始false否
* @param context
* @param cleanStep
*/
public static void setCleanStep(Context context, boolean cleanStep){
Logger.e(TAG, "setCleanStep");
getSharedPreferences(context).edit().putBoolean(CLEAN_STEP,cleanStep).commit();
}
/**
* true 清除步数false否
* @param context
* @return
*/
public static boolean getCleanStep(Context context){
Logger.e(TAG, "getCleanStep");
return getSharedPreferences(context).getBoolean(CLEAN_STEP,true);
}
public static void setCurrentStep(Context context, float currStep){
Logger.e(TAG, "setCurrentStep");
getSharedPreferences(context).edit().putFloat(CURR_STEP,currStep).commit();
}
public static float getCurrentStep(Context context){
Logger.e(TAG, "getCurrentStep");
return getSharedPreferences(context).getFloat(CURR_STEP,0.0f);
}
public static void setShutdown(Context context, boolean shutdown){
Logger.e(TAG, "setShutdown");
getSharedPreferences(context).edit().putBoolean(SHUTDOWN,shutdown).commit();
}
public static boolean getShutdown(Context context){
Logger.e(TAG, "getShutdown");
return getSharedPreferences(context).getBoolean(SHUTDOWN, false);
}
public static void setElapsedRealtime(Context context, long elapsedRealtime){
Logger.e(TAG, "setElapsedRealtime");
getSharedPreferences(context).edit().putLong(ELAPSED_REALTIMEl,elapsedRealtime).commit();
}
public static long getElapsedRealtime(Context context){
Logger.e(TAG, "getElapsedRealtime");
return getSharedPreferences(context).getLong(ELAPSED_REALTIMEl, 0L);
}
}

View File

@@ -0,0 +1,57 @@
package com.today.step.lib;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
/**
* @author : jiahongfei
* @email : jiahongfeinew@163.com
* @date : 2018/1/31
* @desc : 用于解析和生成运动步数Json字符串
*/
public class SportStepJsonUtils {
public static final String SPORT_DATE = "sportDate";
public static final String STEP_NUM = "stepNum";
public static final String DISTANCE = "km";
public static final String CALORIE = "kaluli";
public static final String TODAY = TodayStepDBHelper.TODAY;
static JSONArray getSportStepJsonArray(List<TodayStepData> todayStepDataArrayList) {
JSONArray jsonArray = new JSONArray();
if (null == todayStepDataArrayList || 0 == todayStepDataArrayList.size()) {
return jsonArray;
}
for (int i = 0; i < todayStepDataArrayList.size(); i++) {
TodayStepData todayStepData = todayStepDataArrayList.get(i);
try {
JSONObject subObject = new JSONObject();
subObject.put(TODAY, todayStepData.getToday());
subObject.put(SPORT_DATE, todayStepData.getDate());
subObject.put(STEP_NUM, todayStepData.getStep());
subObject.put(DISTANCE, getDistanceByStep(todayStepData.getStep()));
subObject.put(CALORIE, getCalorieByStep(todayStepData.getStep()));
jsonArray.put(subObject);
} catch (JSONException e) {
e.printStackTrace();
}
}
return jsonArray;
}
// 公里计算公式
static String getDistanceByStep(long steps) {
return String.format("%.2f", steps * 0.6f / 1000);
}
// 千卡路里计算公式
static String getCalorieByStep(long steps) {
return String.format("%.1f", steps * 0.6f * 60 * 1.036f / 1000);
}
}

View File

@@ -0,0 +1,50 @@
package com.today.step.lib;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import java.util.Calendar;
import static android.content.Context.ALARM_SERVICE;
/**
* Created by jiahongfei on 2017/6/18.
*/
class StepAlertManagerUtils {
private static final String TAG = "StepAlertManagerUtils";
/**
* 设置0点分隔Alert当前天+1天的0点启动
*
* @param application
*/
public static void set0SeparateAlertManager(Context application) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.DAY_OF_YEAR,1);
String tomorrow = DateUtils.dateFormat(calendar.getTimeInMillis(),"yyyy-MM-dd");
long timeInMillis = DateUtils.getDateMillis(tomorrow+ " 00:00:00","yyyy-MM-dd HH:mm:ss");
Logger.e(TAG, DateUtils.dateFormat(timeInMillis,"yyyy-MM-dd HH:mm:ss"));
AlarmManager alarmManager = (AlarmManager) application.getSystemService(ALARM_SERVICE);
Intent i1 = new Intent(application, TodayStepAlertReceive.class);
i1.putExtra(TodayStepService.INTENT_NAME_0_SEPARATE, true);
i1.setAction(TodayStepAlertReceive.ACTION_STEP_ALERT);
PendingIntent operation = PendingIntent.getBroadcast(application, 0, i1, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, operation);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, operation);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, operation);
}
}
}

View File

@@ -0,0 +1,30 @@
package com.today.step.lib;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* 0点启动app处理步数
* Created by jiahongfei on 2017/6/18.
*/
public class TodayStepAlertReceive extends BroadcastReceiver {
public static final String ACTION_STEP_ALERT = "action_step_alert";
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_STEP_ALERT.equals(intent.getAction())) {
boolean separate = intent.getBooleanExtra(TodayStepService.INTENT_NAME_0_SEPARATE, false);
Intent stepInent = new Intent(context, TodayStepService.class);
stepInent.putExtra(TodayStepService.INTENT_NAME_0_SEPARATE, separate);
context.startService(stepInent);
StepAlertManagerUtils.set0SeparateAlertManager(context.getApplicationContext());
Logger.e("TodayStepAlertReceive","TodayStepAlertReceive");
}
}
}

View File

@@ -0,0 +1,25 @@
package com.today.step.lib;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* 开机完成广播
*
*/
public class TodayStepBootCompleteReceiver extends BroadcastReceiver {
private static final String TAG = "TodayStepBootCompleteReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Intent todayStepIntent = new Intent(context, TodayStepService.class);
todayStepIntent.putExtra(TodayStepService.INTENT_NAME_BOOT,true);
context.startService(todayStepIntent);
Logger.e(TAG,"TodayStepBootCompleteReceiver");
}
}

View File

@@ -0,0 +1,226 @@
package com.today.step.lib;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.PowerManager;
import android.os.SystemClock;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* android4.4 Sensor.TYPE_STEP_COUNTER
* 计步传感器计算当天步数不需要后台Service
* Created by jiahongfei on 2017/6/30.
*/
class TodayStepCounter implements SensorEventListener {
private static final String TAG = "TodayStepCounter";
private int sOffsetStep = 0;
private int sCurrStep = 0;
private String mTodayDate;
private boolean mCleanStep = true;
private boolean mShutdown = false;
/**
* 用来标识对象第一次创建,
*/
private boolean mCounterStepReset = true;
private Context mContext;
private OnStepCounterListener mOnStepCounterListener;
private boolean mSeparate = false;
private boolean mBoot = false;
public TodayStepCounter(Context context, OnStepCounterListener onStepCounterListener, boolean separate, boolean boot) {
this.mContext = context;
this.mSeparate = separate;
this.mBoot = boot;
this.mOnStepCounterListener = onStepCounterListener;
WakeLockUtils.getLock(mContext);
sCurrStep = (int) PreferencesHelper.getCurrentStep(mContext);
mCleanStep = PreferencesHelper.getCleanStep(mContext);
mTodayDate = PreferencesHelper.getStepToday(mContext);
sOffsetStep = (int) PreferencesHelper.getStepOffset(mContext);
mShutdown = PreferencesHelper.getShutdown(mContext);
Logger.e(TAG, "mShutdown : " + mShutdown);
//开机启动监听到,一定是关机开机了
if (mBoot || shutdownBySystemRunningTime()) {
mShutdown = true;
PreferencesHelper.setShutdown(mContext, mShutdown);
Logger.e(TAG, "开机启动监听到");
}
dateChangeCleanStep();
initBroadcastReceiver();
updateStepCounter();
}
private void initBroadcastReceiver() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_DATE_CHANGED);
BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (Intent.ACTION_TIME_TICK.equals(intent.getAction())
|| Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
Logger.e(TAG, "ACTION_TIME_TICK");
//service存活做0点分隔
dateChangeCleanStep();
}
}
};
mContext.registerReceiver(mBatInfoReceiver, filter);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
int counterStep = (int) event.values[0];
if (mCleanStep) {
//TODO:只有传感器回调才会记录当前传感器步数然后对当天步数进行清零所以步数会少少的步数等于传感器启动需要的步数假如传感器需要10步进行启动那么就少10步
cleanStep(counterStep);
} else {
//处理关机启动
if (mShutdown || shutdownByCounterStep(counterStep)) {
Logger.e(TAG, "onSensorChanged shutdown");
shutdown(counterStep);
}
}
sCurrStep = counterStep - sOffsetStep;
if (sCurrStep < 0) {
//容错处理无论任何原因步数不能小于0如果小于0直接清零
Logger.e(TAG, "容错处理无论任何原因步数不能小于0如果小于0直接清零");
cleanStep(counterStep);
}
PreferencesHelper.setCurrentStep(mContext, sCurrStep);
PreferencesHelper.setElapsedRealtime(mContext, SystemClock.elapsedRealtime());
PreferencesHelper.setLastSensorStep(mContext, counterStep);
Logger.e(TAG, "counterStep : " + counterStep + " --- " + "sOffsetStep : " + sOffsetStep + " --- " + "sCurrStep : " + sCurrStep);
updateStepCounter();
}
}
private void cleanStep(int counterStep) {
//清除步数,步数归零,优先级最高
sCurrStep = 0;
sOffsetStep = counterStep;
PreferencesHelper.setStepOffset(mContext, sOffsetStep);
mCleanStep = false;
PreferencesHelper.setCleanStep(mContext, mCleanStep);
Logger.e(TAG, "mCleanStep : " + "清除步数,步数归零");
}
private void shutdown(int counterStep) {
int tmpCurrStep = (int) PreferencesHelper.getCurrentStep(mContext);
//重新设置offset
sOffsetStep = counterStep - tmpCurrStep;
PreferencesHelper.setStepOffset(mContext, sOffsetStep);
mShutdown = false;
PreferencesHelper.setShutdown(mContext, mShutdown);
}
private boolean shutdownByCounterStep(int counterStep) {
if (mCounterStepReset) {
//只判断一次
if (counterStep < PreferencesHelper.getLastSensorStep(mContext)) {
//当前传感器步数小于上次传感器步数肯定是重新启动了,只是用来增加精度不是绝对的
Logger.e(TAG, "当前传感器步数小于上次传感器步数肯定是重新启动了,只是用来增加精度不是绝对的");
return true;
}
mCounterStepReset = false;
}
return false;
}
private boolean shutdownBySystemRunningTime() {
if (PreferencesHelper.getElapsedRealtime(mContext) > SystemClock.elapsedRealtime()) {
//上次运行的时间大于当前运行时间判断为重启,只是增加精度,极端情况下连续重启,会判断不出来
Logger.e(TAG, "上次运行的时间大于当前运行时间判断为重启,只是增加精度,极端情况下连续重启,会判断不出来");
return true;
}
return false;
}
private synchronized void dateChangeCleanStep() {
//时间改变了清零或者0点分隔回调
if (!getTodayDate().equals(mTodayDate) || mSeparate) {
WakeLockUtils.getLock(mContext);
mCleanStep = true;
PreferencesHelper.setCleanStep(mContext, mCleanStep);
mTodayDate = getTodayDate();
PreferencesHelper.setStepToday(mContext, mTodayDate);
mShutdown = false;
PreferencesHelper.setShutdown(mContext, mShutdown);
mBoot = false;
mSeparate = false;
sCurrStep = 0;
PreferencesHelper.setCurrentStep(mContext, sCurrStep);
if (null != mOnStepCounterListener) {
mOnStepCounterListener.onStepCounterClean();
}
}
}
private String getTodayDate() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
private void updateStepCounter() {
//每次回调都判断一下是否跨天
dateChangeCleanStep();
if (null != mOnStepCounterListener) {
mOnStepCounterListener.onChangeStepCounter(sCurrStep);
}
}
public int getCurrentStep() {
sCurrStep = (int) PreferencesHelper.getCurrentStep(mContext);
return sCurrStep;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}

View File

@@ -0,0 +1,201 @@
package com.today.step.lib;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 用来记录当天步数列表传感器回调30次记录一条数据
* Created by jiahongfei on 2017/10/9.
*/
class TodayStepDBHelper extends SQLiteOpenHelper implements ITodayStepDBHelper{
private static final String TAG = "TodayStepDBHelper";
private static final String DATE_PATTERN_YYYY_MM_DD = "yyyy-MM-dd";
private static final int VERSION = 1;
private static final String DATABASE_NAME = "TodayStepDB.db";
private static final String TABLE_NAME = "TodayStepData";
private static final String PRIMARY_KEY = "_id";
public static final String TODAY = "today";
public static final String DATE = "date";
public static final String STEP = "step";
private static final String SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+ PRIMARY_KEY + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ TODAY + " TEXT, "
+ DATE + " long, "
+ STEP + " long);";
private static final String SQL_DELETE_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;
private static final String SQL_QUERY_ALL = "SELECT * FROM " + TABLE_NAME;
private static final String SQL_QUERY_STEP = "SELECT * FROM " + TABLE_NAME + " WHERE " + TODAY + " = ? AND " + STEP + " = ?";
private static final String SQL_QUERY_STEP_BY_DATE = "SELECT * FROM " + TABLE_NAME + " WHERE " + TODAY + " = ?";
private static final String SQL_DELETE_TODAY = "DELETE FROM " + TABLE_NAME + " WHERE " + TODAY + " = ?";
//只保留mLimit天的数据
private int mLimit = -1;
public static ITodayStepDBHelper factory(Context context){
return new TodayStepDBHelper(context);
}
private TodayStepDBHelper(Context context) {
super(context, DATABASE_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Logger.e(TAG, SQL_CREATE_TABLE);
db.execSQL(SQL_CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
deleteTable();
onCreate(db);
}
@Override
public synchronized boolean isExist(TodayStepData todayStepData) {
Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_STEP, new String[]{todayStepData.getToday(), todayStepData.getStep() + ""});
boolean exist = cursor.getCount() > 0 ? true : false;
cursor.close();
return exist;
}
@Override
public synchronized void createTable() {
getWritableDatabase().execSQL(SQL_CREATE_TABLE);
}
@Override
public synchronized void insert(TodayStepData todayStepData) {
ContentValues contentValues = new ContentValues();
contentValues.put(TODAY, todayStepData.getToday());
contentValues.put(DATE, todayStepData.getDate());
contentValues.put(STEP, todayStepData.getStep());
getWritableDatabase().insert(TABLE_NAME, null, contentValues);
}
@Override
public synchronized List<TodayStepData> getQueryAll() {
Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_ALL, new String[]{});
List<TodayStepData> todayStepDatas = getTodayStepDataList(cursor);
cursor.close();
return todayStepDatas;
}
/**
* 根据时间获取步数列表
*
* @param dateString 格式yyyy-MM-dd
* @return
*/
@Override
public synchronized List<TodayStepData> getStepListByDate(String dateString) {
Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_STEP_BY_DATE, new String[]{dateString});
List<TodayStepData> todayStepDatas = getTodayStepDataList(cursor);
cursor.close();
return todayStepDatas;
}
/**
* 根据时间和天数获取步数列表
* 例如:
* startDate = 2018-01-15
* days = 3
* 获取 2018-01-15、2018-01-16、2018-01-17三天的步数
*
* @param startDate 格式yyyy-MM-dd
* @param days
* @return
*/
@Override
public synchronized List<TodayStepData> getStepListByStartDateAndDays(String startDate, int days) {
List<TodayStepData> todayStepDatas = new ArrayList<>();
for (int i = 0; i < days; i++) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(DateUtils.getDateMillis(startDate, DATE_PATTERN_YYYY_MM_DD));
calendar.add(Calendar.DAY_OF_YEAR, i);
Cursor cursor = getReadableDatabase().rawQuery(SQL_QUERY_STEP_BY_DATE,
new String[]{DateUtils.dateFormat(calendar.getTimeInMillis(), DATE_PATTERN_YYYY_MM_DD)});
todayStepDatas.addAll(getTodayStepDataList(cursor));
cursor.close();
}
return todayStepDatas;
}
private List<TodayStepData> getTodayStepDataList(Cursor cursor) {
List<TodayStepData> todayStepDatas = new ArrayList<>();
while (cursor.moveToNext()) {
String today = cursor.getString(cursor.getColumnIndex(TODAY));
long date = cursor.getLong(cursor.getColumnIndex(DATE));
long step = cursor.getLong(cursor.getColumnIndex(STEP));
TodayStepData todayStepData = new TodayStepData();
todayStepData.setToday(today);
todayStepData.setDate(date);
todayStepData.setStep(step);
todayStepDatas.add(todayStepData);
}
return todayStepDatas;
}
/**
* 根据limit来清除数据库
* 例如:
* curDate = 2018-01-10 limit=0;表示只保留2018-01-10
* curDate = 2018-01-10 limit=1;表示保留2018-01-10、2018-01-09等
* @param curDate
* @param limit -1失效
*/
@Override
public synchronized void clearCapacity(String curDate, int limit) {
mLimit = limit;
if (mLimit <= 0) {
return;
}
try {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(DateUtils.getDateMillis(curDate, DATE_PATTERN_YYYY_MM_DD));
calendar.add(Calendar.DAY_OF_YEAR, -(mLimit));
String date = DateUtils.dateFormat(calendar.getTimeInMillis(), DATE_PATTERN_YYYY_MM_DD);
Log.e(TAG, date);
List<TodayStepData> todayStepDataList = getQueryAll();
Set<String> delDateSet = new HashSet<>();
for (TodayStepData tmpTodayStepData : todayStepDataList) {
long dbTodayDate = DateUtils.getDateMillis(tmpTodayStepData.getToday(), DATE_PATTERN_YYYY_MM_DD);
if (calendar.getTimeInMillis() >= dbTodayDate) {
delDateSet.add(tmpTodayStepData.getToday());
}
}
for (String delDate : delDateSet) {
getWritableDatabase().execSQL(SQL_DELETE_TODAY, new String[]{delDate});
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public synchronized void deleteTable() {
getWritableDatabase().execSQL(SQL_DELETE_TABLE);
}
}

View File

@@ -0,0 +1,84 @@
package com.today.step.lib;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
public class TodayStepData implements Serializable, Parcelable {
//当天时间,只显示到天 yyyy-MM-dd
private String today;
//步数时间,显示到毫秒
private long date;
//对应date时间的步数
private long step;
public TodayStepData() {
}
protected TodayStepData(Parcel in) {
today = in.readString();
date = in.readLong();
step = in.readLong();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(today);
dest.writeLong(date);
dest.writeLong(step);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<TodayStepData> CREATOR = new Creator<TodayStepData>() {
@Override
public TodayStepData createFromParcel(Parcel in) {
return new TodayStepData(in);
}
@Override
public TodayStepData[] newArray(int size) {
return new TodayStepData[size];
}
};
public long getDate() {
return date;
}
public void setDate(long date) {
this.date = date;
}
public long getStep() {
return step;
}
public void setStep(long step) {
this.step = step;
}
public String getToday() {
return today;
}
public void setToday(String today) {
this.today = today;
}
@Override
public String toString() {
return "TodayStepData{" +
", today=" + today +
", date=" + date +
", step=" + step +
'}';
}
}

View File

@@ -0,0 +1,309 @@
package com.today.step.lib;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.PowerManager;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* Sensor.TYPE_ACCELEROMETER
* 加速度传感器计算当天步数需要保持后台Service
*/
class TodayStepDetector implements SensorEventListener{
private final String TAG = "TodayStepDetector";
//存放三轴数据
float[] oriValues = new float[3];
final int ValueNum = 4;
//用于存放计算阈值的波峰波谷差值
float[] tempValue = new float[ValueNum];
int tempCount = 0;
//是否上升的标志位
boolean isDirectionUp = false;
//持续上升次数
int continueUpCount = 0;
//上一点的持续上升的次数,为了记录波峰的上升次数
int continueUpFormerCount = 0;
//上一点的状态,上升还是下降
boolean lastStatus = false;
//波峰值
float peakOfWave = 0;
//波谷值
float valleyOfWave = 0;
//此次波峰的时间
long timeOfThisPeak = 0;
//上次波峰的时间
long timeOfLastPeak = 0;
//当前的时间
long timeOfNow = 0;
//当前传感器的值
float gravityNew = 0;
//上次传感器的值
float gravityOld = 0;
//动态阈值需要动态的数据,这个值用于这些动态数据的阈值
final float InitialValue = (float) 1.3;
//初始阈值
float ThreadValue = (float) 2.0;
//波峰波谷时间差
int TimeInterval = 400;
private int count = 0;
private int mCount = 0;
private OnStepCounterListener mOnStepCounterListener;
private Context mContext;
private long timeOfLastPeak1 = 0;
private long timeOfThisPeak1 = 0;
private String mTodayDate;
public TodayStepDetector(Context context, OnStepCounterListener onStepCounterListener){
super();
mContext = context;
this.mOnStepCounterListener = onStepCounterListener;
WakeLockUtils.getLock(mContext);
mCount = (int) PreferencesHelper.getCurrentStep(mContext);
mTodayDate = PreferencesHelper.getStepToday(mContext);
dateChangeCleanStep();
initBroadcastReceiver();
updateStepCounter();
}
private void initBroadcastReceiver() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_DATE_CHANGED);
BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (Intent.ACTION_TIME_TICK.equals(intent.getAction())
|| Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
Logger.e(TAG, "ACTION_TIME_TICK");
//service存活做0点分隔
dateChangeCleanStep();
}
}
};
mContext.registerReceiver(mBatInfoReceiver, filter);
}
private synchronized void dateChangeCleanStep() {
//时间改变了清零或者0点分隔回调
if (!getTodayDate().equals(mTodayDate)) {
WakeLockUtils.getLock(mContext);
mCount = 0;
PreferencesHelper.setCurrentStep(mContext, mCount);
mTodayDate = getTodayDate();
PreferencesHelper.setStepToday(mContext, mTodayDate);
setSteps(0);
if(null != mOnStepCounterListener){
mOnStepCounterListener.onStepCounterClean();
}
}
}
private String getTodayDate() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
private void updateStepCounter(){
//每次回调都判断一下是否跨天
dateChangeCleanStep();
if (null != mOnStepCounterListener) {
mOnStepCounterListener.onChangeStepCounter(mCount);
}
}
@Override
public void onSensorChanged(SensorEvent event) {
for (int i = 0; i < 3; i++) {
oriValues[i] = event.values[i];
}
gravityNew = (float) Math.sqrt(oriValues[0] * oriValues[0]
+ oriValues[1] * oriValues[1] + oriValues[2] * oriValues[2]);
detectorNewStep(gravityNew);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//
}
/*
* 检测步子,并开始计步
* 1.传入sersor中的数据
* 2.如果检测到了波峰并且符合时间差以及阈值的条件则判定为1步
* 3.符合时间差条件波峰波谷差值大于initialValue则将该差值纳入阈值的计算中
* */
private void detectorNewStep(float values) {
if (gravityOld == 0) {
gravityOld = values;
} else {
if (detectorPeak(values, gravityOld)) {
timeOfLastPeak = timeOfThisPeak;
timeOfNow = System.currentTimeMillis();
if (timeOfNow - timeOfLastPeak >= TimeInterval
&& (peakOfWave - valleyOfWave >= ThreadValue)) {
timeOfThisPeak = timeOfNow;
/*
* 更新界面的处理,不涉及到算法
* 一般在通知更新界面之前,增加下面处理,为了处理无效运动:
* 1.连续记录10才开始计步
* 2.例如记录的9步用户停住超过3秒则前面的记录失效下次从头开始
* 3.连续记录了9步用户还在运动之前的数据才有效
* */
countStep();
}
if (timeOfNow - timeOfLastPeak >= TimeInterval
&& (peakOfWave - valleyOfWave >= InitialValue)) {
timeOfThisPeak = timeOfNow;
ThreadValue = peakValleyThread(peakOfWave - valleyOfWave);
}
}
}
gravityOld = values;
}
/*
* 检测波峰
* 以下四个条件判断为波峰:
* 1.目前点为下降的趋势isDirectionUp为false
* 2.之前的点为上升的趋势lastStatus为true
* 3.到波峰为止持续上升大于等于2次
* 4.波峰值大于20
* 记录波谷值
* 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值
* 2.所以要记录每次的波谷值,为了和下次的波峰做对比
* */
private boolean detectorPeak(float newValue, float oldValue) {
lastStatus = isDirectionUp;
if (newValue >= oldValue) {
isDirectionUp = true;
continueUpCount++;
} else {
continueUpFormerCount = continueUpCount;
continueUpCount = 0;
isDirectionUp = false;
}
if (!isDirectionUp && lastStatus
&& (continueUpFormerCount >= 2 || oldValue >= 20)) {
peakOfWave = oldValue;
return true;
} else if (!lastStatus && isDirectionUp) {
valleyOfWave = oldValue;
return false;
} else {
return false;
}
}
/*
* 阈值的计算
* 1.通过波峰波谷的差值计算阈值
* 2.记录4个值存入tempValue[]数组中
* 3.在将数组传入函数averageValue中计算阈值
* */
private float peakValleyThread(float value) {
float tempThread = ThreadValue;
if (tempCount < ValueNum) {
tempValue[tempCount] = value;
tempCount++;
} else {
tempThread = averageValue(tempValue, ValueNum);
for (int i = 1; i < ValueNum; i++) {
tempValue[i - 1] = tempValue[i];
}
tempValue[ValueNum - 1] = value;
}
return tempThread;
}
/*
* 梯度化阈值
* 1.计算数组的均值
* 2.通过均值将阈值梯度化在一个范围里
* */
private float averageValue(float value[], int n) {
float ave = 0;
for (int i = 0; i < n; i++) {
ave += value[i];
}
ave = ave / ValueNum;
if (ave >= 8)
ave = (float) 4.3;
else if (ave >= 7 && ave < 8)
ave = (float) 3.3;
else if (ave >= 4 && ave < 7)
ave = (float) 2.3;
else if (ave >= 3 && ave < 4)
ave = (float) 2.0;
else {
ave = (float) 1.7;
}
return ave;
}
/*
* 连续走十步才会开始计步
* 连续走了9步以下,停留超过3秒,则计数清空
* */
private void countStep() {
this.timeOfLastPeak1 = this.timeOfThisPeak1;
this.timeOfThisPeak1 = System.currentTimeMillis();
if (this.timeOfThisPeak1 - this.timeOfLastPeak1 <= 3000L){
if(this.count<9){
this.count++;
}else if(this.count == 9){
this.count++;
this.mCount += this.count;
PreferencesHelper.setCurrentStep(mContext, mCount);
updateStepCounter();
}else{
this.mCount++;
PreferencesHelper.setCurrentStep(mContext, mCount);
updateStepCounter();
}
}else{//超时
this.count = 1;//为1,不是0
}
}
private void setSteps(int initValue) {
this.mCount = initValue;
this.count = 0;
timeOfLastPeak1 = 0;
timeOfThisPeak1 = 0;
}
public int getCurrentStep() {
return mCount;
}
}

View File

@@ -0,0 +1,63 @@
package com.today.step.lib;
import android.app.Application;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.annotation.RequiresApi;
/**
* 计步SDK初始化方法
* Created by jiahongfei on 2017/10/9.
*/
public class TodayStepManager {
private static final String TAG = "TodayStepManager";
private static final int JOB_ID = 100;
/**
* 在程序的最开始调用最好在自定义的application oncreate中调用
*
* @param application
*/
public static void init(Application application) {
StepAlertManagerUtils.set0SeparateAlertManager(application);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// initJobScheduler(application);
}
startTodayStepService(application);
}
public static void startTodayStepService(Application application) {
Intent intent = new Intent(application, TodayStepService.class);
application.startService(intent);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private static void initJobScheduler(Application application) {
Logger.e(TAG, "initJobScheduler");
JobScheduler jobScheduler = (JobScheduler)
application.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(
JOB_ID,
new ComponentName(application.getPackageName(), JobSchedulerService.class.getName()));
builder.setMinimumLatency(5000)// 设置任务运行最少延迟时间
.setOverrideDeadline(60000)// 设置deadline若到期还没有达到规定的条件则会开始执行
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)// 设置网络条件
.setRequiresCharging(true)// 设置是否充电的条件
.setRequiresDeviceIdle(false);// 设置手机是否空闲的条件
int resultCode = jobScheduler.schedule(builder.build());
if (JobScheduler.RESULT_FAILURE == resultCode) {
Logger.e(TAG, "jobScheduler 失败");
}
}
}

View File

@@ -0,0 +1,467 @@
package com.today.step.lib;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import org.json.JSONArray;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import static com.today.step.lib.SportStepJsonUtils.getCalorieByStep;
import static com.today.step.lib.SportStepJsonUtils.getDistanceByStep;
public class TodayStepService extends Service implements Handler.Callback {
private static final String TAG = "TodayStepService";
/**
* 数据库中保存多少天的运动数据
*/
private static final int DB_LIMIT = 2;
//保存数据库频率
private static final int DB_SAVE_COUNTER = 50;
//传感器的采样周期这里使用SensorManager.SENSOR_DELAY_FASTEST如果使用SENSOR_DELAY_UI会导致部分手机后台清理内存之后传感器不记步
private static final int SAMPLING_PERIOD_US = SensorManager.SENSOR_DELAY_FASTEST;
private static final int HANDLER_WHAT_SAVE_STEP = 0;
//如果走路如果停止10秒钟后保存数据库
private static final int LAST_SAVE_STEP_DURATION = 10*1000;
private static final int BROADCAST_REQUEST_CODE = 100;
public static final String INTENT_NAME_0_SEPARATE = "intent_name_0_separate";
public static final String INTENT_NAME_BOOT = "intent_name_boot";
public static final String INTENT_JOB_SCHEDULER = "intent_job_scheduler";
public static int CURRENT_SETP = 0;
private SensorManager sensorManager;
// private TodayStepDcretor stepDetector;
private TodayStepDetector mStepDetector;
private TodayStepCounter stepCounter;
private NotificationManager nm;
Notification notification;
private NotificationCompat.Builder builder;
private boolean mSeparate = false;
private boolean mBoot = false;
private int mDbSaveCount = 0;
private ITodayStepDBHelper mTodayStepDBHelper;
private final Handler sHandler = new Handler(this);
private Microlog4Android mMicrolog4Android = new Microlog4Android();
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case HANDLER_WHAT_SAVE_STEP: {
Logger.e(TAG, "HANDLER_WHAT_SAVE_STEP");
microlog4AndroidError("HANDLER_WHAT_SAVE_STEP");
mDbSaveCount = 0;
saveDb(true, CURRENT_SETP);
break;
}
default:
break;
}
return false;
}
@Override
public void onCreate() {
Logger.e(TAG, "onCreate:" + CURRENT_SETP);
super.onCreate();
mTodayStepDBHelper = TodayStepDBHelper.factory(getApplicationContext());
sensorManager = (SensorManager) this
.getSystemService(SENSOR_SERVICE);
initNotification(CURRENT_SETP);
if(null != mMicrolog4Android) {
mMicrolog4Android.configure(this);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Logger.e(TAG, "onStartCommand:" + CURRENT_SETP);
if (null != intent) {
mSeparate = intent.getBooleanExtra(INTENT_NAME_0_SEPARATE, false);
mBoot = intent.getBooleanExtra(INTENT_NAME_BOOT, false);
}
mDbSaveCount = 0;
updateNotification(CURRENT_SETP);
//注册传感器
startStepDetector();
//TODO:测试数据Start
// if(Logger.sIsDebug) {
// if (!isStepCounter()) {
// Toast.makeText(getApplicationContext(), "Lib 当前手机没有计步传感器", Toast.LENGTH_LONG).show();
// } else {
// Toast.makeText(getApplicationContext(), "Lib 当前手机使用计步传感器", Toast.LENGTH_LONG).show();
//
// }
// }
//TODO:测试数据End
microlog4AndroidError("onStartCommand");
return START_STICKY;
}
private void initNotification(int currentStep) {
builder = new NotificationCompat.Builder(this);
builder.setPriority(Notification.PRIORITY_MIN);
String receiverName = getReceiver(getApplicationContext());
PendingIntent contentIntent = PendingIntent.getBroadcast(this, BROADCAST_REQUEST_CODE, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
if (!TextUtils.isEmpty(receiverName)) {
try {
contentIntent = PendingIntent.getBroadcast(this, BROADCAST_REQUEST_CODE, new Intent(this, Class.forName(receiverName)), PendingIntent.FLAG_UPDATE_CURRENT);
} catch (Exception e) {
e.printStackTrace();
contentIntent = PendingIntent.getBroadcast(this, BROADCAST_REQUEST_CODE, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
}
}
builder.setContentIntent(contentIntent);
int smallIcon = getResources().getIdentifier("icon_step_small", "mipmap", getPackageName());
if (0 != smallIcon) {
Logger.e(TAG, "smallIcon");
builder.setSmallIcon(smallIcon);
} else {
builder.setSmallIcon(R.mipmap.ic_notification_default);// 设置通知小ICON
}
int largeIcon = getResources().getIdentifier("icon_step_large", "mipmap", getPackageName());
if (0 != largeIcon) {
Logger.e(TAG, "largeIcon");
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), largeIcon));
} else {
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_notification_default));
}
builder.setTicker(getString(R.string.app_name));
builder.setContentTitle(getString(R.string.title_notification_bar, String.valueOf(currentStep)));
String km = getDistanceByStep(currentStep);
String calorie = getCalorieByStep(currentStep);
builder.setContentText(calorie + " 千卡 " + km + " 公里");
//设置不可清除
builder.setOngoing(true);
notification = builder.build();
//将Service设置前台这里的id和notify的id一定要相同否则会出现后台清理内存Service被杀死通知还存在的bug
startForeground(R.string.app_name, notification);
nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify(R.string.app_name, notification);
}
@Override
public IBinder onBind(Intent intent) {
Logger.e(TAG, "onBind:" + CURRENT_SETP);
return mIBinder.asBinder();
}
private void startStepDetector() {
// getLock(this);
//android4.4以后如果有stepcounter可以使用计步传感器
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isStepCounter()) {
addStepCounterListener();
} else {
addBasePedoListener();
}
}
private void addStepCounterListener() {
Logger.e(TAG, "addStepCounterListener");
if (null != stepCounter) {
Logger.e(TAG, "已经注册TYPE_STEP_COUNTER");
WakeLockUtils.getLock(this);
CURRENT_SETP = stepCounter.getCurrentStep();
updateNotification(CURRENT_SETP);
return;
}
Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
if (null == countSensor) {
return;
}
stepCounter = new TodayStepCounter(getApplicationContext(), mOnStepCounterListener, mSeparate, mBoot);
Logger.e(TAG, "countSensor");
sensorManager.registerListener(stepCounter, countSensor, SAMPLING_PERIOD_US);
}
private void addBasePedoListener() {
Logger.e(TAG, "addBasePedoListener");
if (null != mStepDetector) {
WakeLockUtils.getLock(this);
Logger.e(TAG, "已经注册TYPE_ACCELEROMETER");
CURRENT_SETP = mStepDetector.getCurrentStep();
updateNotification(CURRENT_SETP);
return;
}
//没有计步器的时候开启定时器保存数据
Sensor sensor = sensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (null == sensor) {
return;
}
mStepDetector = new TodayStepDetector(this, mOnStepCounterListener);
Log.e(TAG, "TodayStepDcretor");
// 获得传感器的类型,这里获得的类型是加速度传感器
// 此方法用来注册只有注册过才会生效参数SensorEventListener的实例Sensor的实例更新速率
sensorManager.registerListener(mStepDetector, sensor, SAMPLING_PERIOD_US);
}
@Override
public void onDestroy() {
Logger.e(TAG, "onDestroy:" + CURRENT_SETP);
Intent intent = new Intent(this, TodayStepService.class);
startService(intent);
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Logger.e(TAG, "onUnbind:" + CURRENT_SETP);
return super.onUnbind(intent);
}
/**
* 步数每次回调的方法
*
* @param currentStep
*/
private void updateTodayStep(int currentStep) {
microlog4AndroidError(" currentStep : " + currentStep);
CURRENT_SETP = currentStep;
updateNotification(CURRENT_SETP);
saveStep(currentStep);
}
private void saveStep(int currentStep) {
sHandler.removeMessages(HANDLER_WHAT_SAVE_STEP);
sHandler.sendEmptyMessageDelayed(HANDLER_WHAT_SAVE_STEP, LAST_SAVE_STEP_DURATION);
microlog4AndroidError(" mDbSaveCount : " + mDbSaveCount);
if (DB_SAVE_COUNTER > mDbSaveCount) {
mDbSaveCount++;
return;
}
mDbSaveCount = 0;
saveDb(false, currentStep);
}
/**
* @param handler true handler回调保存步数否false
* @param currentStep
*/
private void saveDb(boolean handler, int currentStep) {
TodayStepData todayStepData = new TodayStepData();
todayStepData.setToday(getTodayDate());
todayStepData.setDate(System.currentTimeMillis());
todayStepData.setStep(currentStep);
if (null != mTodayStepDBHelper) {
Logger.e(TAG, "saveDb handler : " + handler);
if (!handler || !mTodayStepDBHelper.isExist(todayStepData)) {
Logger.e(TAG, "saveDb currentStep : " + currentStep);
microlog4AndroidError("saveDb currentStep : " + currentStep);
mTodayStepDBHelper.insert(todayStepData);
}
}
}
private void cleanDb() {
Logger.e(TAG, "cleanDb");
mDbSaveCount = 0;
if (null != mTodayStepDBHelper) {
mTodayStepDBHelper.clearCapacity(DateUtils.dateFormat(System.currentTimeMillis(), "yyyy-MM-dd"), DB_LIMIT);
}
// if (null != mTodayStepDBHelper) {
//保存多天的步数
// mTodayStepDBHelper.deleteTable();
// mTodayStepDBHelper.createTable();
// }
}
private String getTodayDate() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(date);
}
/**
* 更新通知
*/
private void updateNotification(int stepCount) {
if (null == builder || null == nm) {
return;
}
builder.setContentTitle(getString(R.string.title_notification_bar, String.valueOf(stepCount)));
String km = getDistanceByStep(stepCount);
String calorie = getCalorieByStep(stepCount);
builder.setContentText(calorie + " 千卡 " + km + " 公里");
notification = builder.build();
nm.notify(R.string.app_name, notification);
}
private boolean isStepCounter() {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_COUNTER);
}
private boolean isStepDetector() {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR);
}
private OnStepCounterListener mOnStepCounterListener = new OnStepCounterListener() {
@Override
public void onChangeStepCounter(int step) {
updateTodayStep(step);
}
@Override
public void onStepCounterClean() {
CURRENT_SETP = 0;
updateNotification(CURRENT_SETP);
cleanDb();
}
};
private final ISportStepInterface.Stub mIBinder = new ISportStepInterface.Stub() {
@Override
public int getCurrentTimeSportStep() throws RemoteException {
return CURRENT_SETP;
}
private JSONArray getSportStepJsonArray(List<TodayStepData> todayStepDataArrayList) {
return SportStepJsonUtils.getSportStepJsonArray(todayStepDataArrayList);
}
@Override
public String getTodaySportStepArray() throws RemoteException {
if (null != mTodayStepDBHelper) {
List<TodayStepData> todayStepDataArrayList = mTodayStepDBHelper.getQueryAll();
JSONArray jsonArray = getSportStepJsonArray(todayStepDataArrayList);
Logger.e(TAG, jsonArray.toString());
return jsonArray.toString();
}
return null;
}
@Override
public String getTodaySportStepArrayByDate(String date) throws RemoteException {
if (null != mTodayStepDBHelper) {
List<TodayStepData> todayStepDataArrayList = mTodayStepDBHelper.getStepListByDate(date);
JSONArray jsonArray = getSportStepJsonArray(todayStepDataArrayList);
Logger.e(TAG, jsonArray.toString());
return jsonArray.toString();
}
return null;
}
@Override
public String getTodaySportStepArrayByStartDateAndDays(String date, int days) throws RemoteException {
if (null != mTodayStepDBHelper) {
List<TodayStepData> todayStepDataArrayList = mTodayStepDBHelper.getStepListByStartDateAndDays(date, days);
JSONArray jsonArray = getSportStepJsonArray(todayStepDataArrayList);
Logger.e(TAG, jsonArray.toString());
return jsonArray.toString();
}
return null;
}
};
public static String getReceiver(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_RECEIVERS);
ActivityInfo[] activityInfos = packageInfo.receivers;
if (null != activityInfos && activityInfos.length > 0) {
for (int i = 0; i < activityInfos.length; i++) {
String receiverName = activityInfos[i].name;
Class superClazz = Class.forName(receiverName).getSuperclass();
int count = 1;
while (null != superClazz) {
if (superClazz.getName().equals("java.lang.Object")) {
break;
}
if (superClazz.getName().equals(BaseClickBroadcast.class.getName())) {
Log.e(TAG, "receiverName : " + receiverName);
return receiverName;
}
if (count > 20) {
//用来做容错如果20个基类还不到Object直接跳出防止while死循环
break;
}
count++;
superClazz = superClazz.getSuperclass();
Log.e(TAG, "superClazz : " + superClazz);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private void microlog4AndroidError(String msg){
if (null != mMicrolog4Android) {
mMicrolog4Android.error(DateUtils.getCurrentDate("yyyy-MM-dd HH:mm:ss") + " " + msg);
}
}
}

View File

@@ -0,0 +1,23 @@
package com.today.step.lib;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* Created by jiahongfei on 2017/9/27.
*/
public class TodayStepShutdownReceiver extends BroadcastReceiver {
private static final String TAG = "TodayStepShutdownReceiver";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
Logger.e(TAG,"TodayStepShutdownReceiver");
PreferencesHelper.setShutdown(context,true);
}
}
}

View File

@@ -0,0 +1,39 @@
package com.today.step.lib;
import android.content.Context;
import android.os.PowerManager;
import java.util.Calendar;
/**
* @author : jiahongfei
* @email : jiahongfeinew@163.com
* @date : 2018/2/12
* @desc :
*/
class WakeLockUtils {
private static PowerManager.WakeLock mWakeLock;
synchronized static PowerManager.WakeLock getLock(Context context) {
if (mWakeLock != null) {
if (mWakeLock.isHeld())
mWakeLock.release();
mWakeLock = null;
}
if (mWakeLock == null) {
PowerManager mgr = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
TodayStepService.class.getName());
mWakeLock.setReferenceCounted(true);
Calendar c = Calendar.getInstance();
c.setTimeInMillis(System.currentTimeMillis());
mWakeLock.acquire();
}
return (mWakeLock);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,4 @@
<resources>
<string name="app_name">TodayStepCounterLib</string>
<string name="title_notification_bar">当前步数:%1$s</string>
</resources>

View File

@@ -0,0 +1,53 @@
package com.today.step.lib;
import org.junit.runners.model.InitializationError;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.res.Fs;
/**
* @author : jiahongfei
* @email : jiahongfeinew@163.com
* @date : 2018/1/22
* @desc :
*/
public class MyRobolectricTestRunner extends RobolectricTestRunner {
/**
* Creates a runner to run {@code testClass}. Looks in your working directory for your AndroidManifest.xml file
* and res directory by default. Use the {@link Config} annotation to configure.
*
* @param testClass the test class to be run
* @throws InitializationError if junit says so
*/
public MyRobolectricTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected AndroidManifest getAppManifest(Config config) {
String projectName = "TodayStepCounter";
int nameLength = projectName.length();
String rootPath = System.getProperty("user.dir", "./");
int index = rootPath.indexOf(projectName);
if (index == -1) {
throw new RuntimeException("project name not found in user.dir");
}
//获取项目的根目录
rootPath = rootPath.substring(0, index + nameLength);
String manifestProperty = rootPath + "/todaystepcounterlib/src/main/AndroidManifest.xml";
String resProperty = rootPath + "/todaystepcounterlib/src/main/res";
String assetsProperty = rootPath + "/todaystepcounterlib/src/main/assets";
return new AndroidManifest(
Fs.fileFromPath(manifestProperty),
Fs.fileFromPath(resProperty),
Fs.fileFromPath(assetsProperty)) {
@Override
public int getTargetSdkVersion() {
return 21;
}
};
}
}

View File

@@ -0,0 +1,110 @@
package com.today.step.lib;
import android.app.Application;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Calendar;
import java.util.List;
/**
* @author : jiahongfei
* @email : jiahongfeinew@163.com
* @date : 2018/1/22
* @desc :
*/
@RunWith(MyRobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class TodayStepDBHelperTest {
private static final String SPORT_DATE = "sportDate";
private static final String STEP_NUM = "stepNum";
private ITodayStepDBHelper mTodayStepDBHelper;
@Before
public void before(){
Application application = RuntimeEnvironment.application;
System.out.println(application);
mTodayStepDBHelper = TodayStepDBHelper.factory(application);
}
@Test
public void insert(){
//10天
for (int i = 0; i<10; i++){
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(DateUtils.getDateMillis("2018-01-01","yyyy-MM-dd"));
if(4 == i || 5 == i || 7 == i || 8 == i){
continue;
}
calendar.add(Calendar.DAY_OF_YEAR,i);
//每天存储5次步数
for (int j = 0; j<5; j++){
TodayStepData todayStepData = new TodayStepData();
todayStepData.setToday(DateUtils.dateFormat(calendar.getTimeInMillis(),"yyyy-MM-dd"));
todayStepData.setStep(100*j);
Calendar hourCalendar = Calendar.getInstance();
hourCalendar.setTimeInMillis(DateUtils.getDateMillis("2018-01-01 08:00","yyyy-MM-dd HH:mm"));
hourCalendar.add(Calendar.HOUR_OF_DAY,j);
todayStepData.setDate(hourCalendar.getTimeInMillis());
mTodayStepDBHelper.insert(todayStepData);
}
}
List<TodayStepData> todayStepDataList = mTodayStepDBHelper.getQueryAll();
JSONArray jsonArray = getSportStepJsonArray(todayStepDataList);
System.out.println(jsonArray.toString());
todayStepDataList = mTodayStepDBHelper.getStepListByDate("2018-01-01");
jsonArray = getSportStepJsonArray(todayStepDataList);
System.out.println(jsonArray.toString());
todayStepDataList = mTodayStepDBHelper.getStepListByStartDateAndDays("2018-01-03",3);
jsonArray = getSportStepJsonArray(todayStepDataList);
System.out.println(jsonArray.toString());
// mTodayStepDBHelper.clearCapacity("2018-01-10",7);
//
// todayStepDataList = mTodayStepDBHelper.getQueryAll();
// jsonArray = getSportStepJsonArray(todayStepDataList);
// Systemhttp://pa.mokous.com/share/renewal/esb_plus_award_notice.html.out.println(jsonArray.toString());
}
private JSONArray getSportStepJsonArray(List<TodayStepData> todayStepDataArrayList){
JSONArray jsonArray = new JSONArray();
if (null == todayStepDataArrayList || 0 == todayStepDataArrayList.size()) {
return jsonArray;
}
for (int i = 0; i < todayStepDataArrayList.size(); i++) {
TodayStepData todayStepData = todayStepDataArrayList.get(i);
try {
JSONObject subObject = new JSONObject();
subObject.put(TodayStepDBHelper.TODAY, todayStepData.getToday());
subObject.put(SPORT_DATE, DateUtils.dateFormat(todayStepData.getDate(),"yyyy-MM-dd HH:mm"));
subObject.put(STEP_NUM, todayStepData.getStep());
jsonArray.put(subObject);
} catch (JSONException e) {
e.printStackTrace();
}
}
return jsonArray;
}
}