Commit 2ccd5d32b67a5f5003d2566112be182a2b2add06
1 parent
ad8287cf
1、进程保活
Showing
31 changed files
with
2106 additions
and
10 deletions
app/build.gradle
| 1 | apply plugin: 'com.android.application' | 1 | apply plugin: 'com.android.application' |
| 2 | +apply plugin: 'com.alibaba.arouter' | ||
| 3 | + | ||
| 4 | +static def stringValue(def value) { | ||
| 5 | + return "\"${value}\"" | ||
| 6 | +} | ||
| 2 | 7 | ||
| 3 | android { | 8 | android { |
| 4 | compileSdkVersion 28 | 9 | compileSdkVersion 28 |
| 5 | defaultConfig { | 10 | defaultConfig { |
| 6 | - applicationId "com.example.modulepedometer" | ||
| 7 | - minSdkVersion 15 | ||
| 8 | - targetSdkVersion 28 | 11 | + applicationId "com.cnlive.strike" |
| 12 | + minSdkVersion 16 | ||
| 13 | + targetSdkVersion 27 | ||
| 9 | versionCode 1 | 14 | versionCode 1 |
| 10 | versionName "1.0" | 15 | versionName "1.0" |
| 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" |
| 17 | + multiDexEnabled true | ||
| 18 | + javaCompileOptions { | ||
| 19 | + annotationProcessorOptions { | ||
| 20 | + arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"] | ||
| 21 | + } | ||
| 22 | + } | ||
| 12 | } | 23 | } |
| 13 | buildTypes { | 24 | buildTypes { |
| 25 | + debug { | ||
| 26 | + applicationIdSuffix ".debug" | ||
| 27 | + resValue("string", "app_name", "网++ 内测") | ||
| 28 | + buildConfigField("String", "APP_ID", stringValue("802_jhbqccxw08")) | ||
| 29 | + buildConfigField("String", "APP_KEY", stringValue("9c5619e9be747bb3b925dd215ca5923d86f12c09b9394f")) | ||
| 30 | + buildConfigField("String", "APP_SCERET", stringValue("1e6380cb347255e777f72b5aa60f41daa68443a132aaea")) | ||
| 31 | + } | ||
| 14 | release { | 32 | release { |
| 15 | - minifyEnabled false | ||
| 16 | - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | 33 | + resValue("string", "app_name", "网++") |
| 34 | + buildConfigField("String", "APP_ID", stringValue("769_jetj7bq525")) | ||
| 35 | + buildConfigField("String", "APP_KEY", stringValue("0d57b045ec0c3988a682d94c644e66b9b8e0d2b8ef937d")) | ||
| 36 | + buildConfigField("String", "APP_SCERET", stringValue("275e8dc29274a1186d82f65bc607c231d02bcb3bfd8976")) | ||
| 17 | } | 37 | } |
| 18 | } | 38 | } |
| 39 | + | ||
| 40 | + compileOptions { | ||
| 41 | + sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 42 | + targetCompatibility JavaVersion.VERSION_1_8 | ||
| 43 | + } | ||
| 44 | + dataBinding { | ||
| 45 | + enabled true | ||
| 46 | + } | ||
| 47 | + dexOptions { | ||
| 48 | + javaMaxHeapSize '4g' | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + compileOptions { | ||
| 52 | + sourceCompatibility 1.8 | ||
| 53 | + targetCompatibility 1.8 | ||
| 54 | + } | ||
| 19 | } | 55 | } |
| 20 | 56 | ||
| 21 | dependencies { | 57 | dependencies { |
| @@ -25,4 +61,12 @@ dependencies { | @@ -25,4 +61,12 @@ dependencies { | ||
| 25 | testImplementation 'junit:junit:4.12' | 61 | testImplementation 'junit:junit:4.12' |
| 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' | 62 | androidTestImplementation 'com.android.support.test:runner:1.0.2' |
| 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' | 63 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' |
| 64 | + implementation project(':moudle_pedometer') | ||
| 65 | + implementation "com.cnlive.libs:base:$rootProject.libBase" | ||
| 66 | + implementation "com.qmuiteam:qmui:$rootProject.qmui" | ||
| 67 | + implementation "com.cnlive:app_arch:$rootProject.appArch" | ||
| 68 | + | ||
| 69 | + //ARouter | ||
| 70 | + implementation "com.alibaba:arouter-api:$rootProject.arouterApiVersion" | ||
| 71 | + annotationProcessor "com.alibaba:arouter-compiler:$rootProject.arouterCompilerVersion" | ||
| 28 | } | 72 | } |
app/src/main/AndroidManifest.xml
| @@ -2,7 +2,11 @@ | @@ -2,7 +2,11 @@ | ||
| 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 3 | package="com.example.modulepedometer"> | 3 | package="com.example.modulepedometer"> |
| 4 | 4 | ||
| 5 | + | ||
| 6 | + | ||
| 7 | + | ||
| 5 | <application | 8 | <application |
| 9 | + android:name=".MyApp" | ||
| 6 | android:allowBackup="true" | 10 | android:allowBackup="true" |
| 7 | android:icon="@mipmap/ic_launcher" | 11 | android:icon="@mipmap/ic_launcher" |
| 8 | android:label="@string/app_name" | 12 | android:label="@string/app_name" |
app/src/main/java/com/example/modulepedometer/MainActivity.java
| 1 | package com.example.modulepedometer; | 1 | package com.example.modulepedometer; |
| 2 | 2 | ||
| 3 | +import android.content.Intent; | ||
| 3 | import android.support.v7.app.AppCompatActivity; | 4 | import android.support.v7.app.AppCompatActivity; |
| 4 | import android.os.Bundle; | 5 | import android.os.Bundle; |
| 5 | 6 | ||
| 7 | +import com.alibaba.android.arouter.launcher.ARouter; | ||
| 8 | +import com.cnlive.moudle.pedometer.ui.activity.PedometerMainActivity; | ||
| 9 | + | ||
| 6 | public class MainActivity extends AppCompatActivity { | 10 | public class MainActivity extends AppCompatActivity { |
| 7 | 11 | ||
| 8 | @Override | 12 | @Override |
| 9 | protected void onCreate(Bundle savedInstanceState) { | 13 | protected void onCreate(Bundle savedInstanceState) { |
| 10 | super.onCreate(savedInstanceState); | 14 | super.onCreate(savedInstanceState); |
| 11 | setContentView(R.layout.activity_main); | 15 | setContentView(R.layout.activity_main); |
| 16 | + ARouter.getInstance() | ||
| 17 | + .build("/moudle_pedometer/InterestActivity") | ||
| 18 | + .navigation(this); | ||
| 19 | + finish(); | ||
| 12 | } | 20 | } |
| 13 | } | 21 | } |
app/src/main/java/com/example/modulepedometer/MyApp.java
0 → 100644
| 1 | +package com.example.modulepedometer; | ||
| 2 | + | ||
| 3 | +import android.app.Application; | ||
| 4 | + | ||
| 5 | +import com.alibaba.android.arouter.launcher.ARouter; | ||
| 6 | +import com.cnlive.libs.base.application.AppConfig; | ||
| 7 | + | ||
| 8 | +public class MyApp extends Application { | ||
| 9 | + @Override | ||
| 10 | + public void onCreate() { | ||
| 11 | + super.onCreate(); | ||
| 12 | + String userId = ""; | ||
| 13 | + AppConfig.init(getApplicationContext(), BuildConfig.APP_ID, BuildConfig.APP_KEY, BuildConfig.APP_SCERET, | ||
| 14 | + 0, BuildConfig.DEBUG, "", "", | ||
| 15 | + userId, "", "", "", ""); | ||
| 16 | + initARouter(); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 初始化阿里的路由 | ||
| 21 | + */ | ||
| 22 | + private void initARouter() { | ||
| 23 | + if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 | ||
| 24 | + ARouter.openLog(); // 打印日志 | ||
| 25 | + ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) | ||
| 26 | + ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈 | ||
| 27 | + } | ||
| 28 | + ARouter.init(this); // 尽可能早,推荐在Application中初始化 | ||
| 29 | + } | ||
| 30 | +} |
app/src/main/res/values/strings.xml
moudle_pedometer/build.gradle
| @@ -42,7 +42,7 @@ android { | @@ -42,7 +42,7 @@ android { | ||
| 42 | } | 42 | } |
| 43 | //greendao配置 | 43 | //greendao配置 |
| 44 | greendao { | 44 | greendao { |
| 45 | - schemaVersion 1 //版本号,升级时可配置 | 45 | + schemaVersion 1 //版本号,升级时可配置 |
| 46 | daoPackage 'com.cnlive.moudle.pedometer.sql.dao' //包名 | 46 | daoPackage 'com.cnlive.moudle.pedometer.sql.dao' //包名 |
| 47 | targetGenDir 'src/main/java' //生成目录 | 47 | targetGenDir 'src/main/java' //生成目录 |
| 48 | } | 48 | } |
| @@ -68,6 +68,7 @@ dependencies { | @@ -68,6 +68,7 @@ dependencies { | ||
| 68 | implementation "com.github.bumptech.glide:glide:$rootProject.glide" | 68 | implementation "com.github.bumptech.glide:glide:$rootProject.glide" |
| 69 | implementation "jp.wasabeef:glide-transformations:$rootProject.glideTransformations" | 69 | implementation "jp.wasabeef:glide-transformations:$rootProject.glideTransformations" |
| 70 | implementation 'com.android.support:support-v4:28.0.0' | 70 | implementation 'com.android.support:support-v4:28.0.0' |
| 71 | + implementation 'com.android.support.constraint:constraint-layout:1.1.3' | ||
| 71 | annotationProcessor "com.github.bumptech.glide:compiler:$rootProject.compiler" | 72 | annotationProcessor "com.github.bumptech.glide:compiler:$rootProject.compiler" |
| 72 | 73 | ||
| 73 | //-------------------------------------------------------- | 74 | //-------------------------------------------------------- |
moudle_pedometer/src/debug/res/mipmap-hdpi/ic_launcher.png
0 → 100644
4.41 KB
moudle_pedometer/src/debug/res/mipmap-ldpi/ic_launcher.png
0 → 100644
2.17 KB
moudle_pedometer/src/debug/res/mipmap-xhdpi/ic_launcher.png
0 → 100644
5.93 KB
moudle_pedometer/src/debug/res/mipmap-xxhdpi/ic_launcher.png
0 → 100644
9.25 KB
moudle_pedometer/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
0 → 100644
13.2 KB
moudle_pedometer/src/main/AndroidManifest.xml
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | 2 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 2 | - package="com.example.moudle_pedometer" /> | 3 | + xmlns:tools="http://schemas.android.com/tools" |
| 4 | + package="com.example.moudle_pedometer"> | ||
| 5 | + | ||
| 6 | + <!-- 申请权限 --> | ||
| 7 | + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> | ||
| 8 | + <uses-permission android:name="android.permission.WAKE_LOCK" /> | ||
| 9 | + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||
| 10 | + <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> | ||
| 11 | + <!--计歩需要的权限--> | ||
| 12 | + <uses-permission android:name="android.permission.VIBRATE" /> | ||
| 13 | + <uses-permission | ||
| 14 | + android:name="android.permission.WRITE_SETTINGS" | ||
| 15 | + tools:ignore="ProtectedPermissions" /> | ||
| 16 | + | ||
| 17 | + <uses-feature android:name="android.hardware.sensor.accelerometer" /> | ||
| 18 | + | ||
| 19 | + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||
| 20 | + <uses-permission | ||
| 21 | + android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" | ||
| 22 | + tools:ignore="ProtectedPermissions" /> | ||
| 23 | + | ||
| 24 | + <uses-feature | ||
| 25 | + android:name="android.hardware.sensor.stepcounter" | ||
| 26 | + android:required="true" /> | ||
| 27 | + <uses-feature | ||
| 28 | + android:name="android.hardware.sensor.stepdetector" | ||
| 29 | + android:required="true" /> | ||
| 30 | + | ||
| 31 | + <application> | ||
| 32 | + <service | ||
| 33 | + android:name="com.cnlive.moudle.pedometer.service.KeepLiveService" | ||
| 34 | + android:enabled="true" | ||
| 35 | + android:exported="true" | ||
| 36 | + android:priority="1000" | ||
| 37 | + android:process=":remote" /> | ||
| 38 | + <service | ||
| 39 | + android:name="com.cnlive.moudle.pedometer.service.StepService" | ||
| 40 | + android:enabled="true" | ||
| 41 | + android:exported="true" | ||
| 42 | + android:priority="1000" | ||
| 43 | + android:process=":step"> | ||
| 44 | + <intent-filter> | ||
| 45 | + <!-- 系统启动完成后会调用--> | ||
| 46 | + <action android:name="android.intent.action.BOOT_COMPLETED" /> | ||
| 47 | + <action android:name="android.intent.action.DATE_CHANGED" /> | ||
| 48 | + <action android:name="android.intent.action.MEDIA_MOUNTED" /> | ||
| 49 | + <action android:name="android.intent.action.USER_PRESENT" /> | ||
| 50 | + <action android:name="android.intent.action.ACTION_TIME_TICK" /> | ||
| 51 | + <action android:name="android.intent.action.ACTION_POWER_CONNECTED" /> | ||
| 52 | + <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" /> | ||
| 53 | + </intent-filter> | ||
| 54 | + </service> | ||
| 55 | + | ||
| 56 | + <activity android:name="com.cnlive.moudle.pedometer.ui.activity.PedometerMainActivity" /> | ||
| 57 | + | ||
| 58 | + <receiver | ||
| 59 | + android:name="com.cnlive.moudle.pedometer.receivers.MyReceiver" | ||
| 60 | + android:enabled="true" | ||
| 61 | + android:exported="true"> | ||
| 62 | + <intent-filter android:priority="1000"> | ||
| 63 | + <action android:name="com.cnlive.moudle.pedometer.receivers.MyReceiver" /> | ||
| 64 | + <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> | ||
| 65 | + <action android:name="android.net.wifi.STATE_CHANGE" /> | ||
| 66 | + <action android:name="com.example.myservice.MyReceiver" /> | ||
| 67 | + </intent-filter> | ||
| 68 | + </receiver> | ||
| 69 | + </application> | ||
| 70 | + | ||
| 71 | +</manifest> | ||
| 3 | \ No newline at end of file | 72 | \ No newline at end of file |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/model/step/StepCount.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.model.step; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * Created by dylan on 16/9/27. | ||
| 5 | + */ | ||
| 6 | + | ||
| 7 | +/* | ||
| 8 | +* 根据StepDetector传入的步点"数"步子 | ||
| 9 | +* */ | ||
| 10 | +public class StepCount implements StepCountListener { | ||
| 11 | + | ||
| 12 | + private int count = 0; | ||
| 13 | + private int mCount = 0; | ||
| 14 | + private StepValuePassListener mStepValuePassListener; | ||
| 15 | + private long timeOfLastPeak = 0; | ||
| 16 | + private long timeOfThisPeak = 0; | ||
| 17 | + private StepDetector stepDetector; | ||
| 18 | + | ||
| 19 | + public StepCount() { | ||
| 20 | + stepDetector = new StepDetector(); | ||
| 21 | + stepDetector.initListener(this); | ||
| 22 | + } | ||
| 23 | + public StepDetector getStepDetector(){ | ||
| 24 | + return stepDetector; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /* | ||
| 28 | + * 连续走十步才会开始计步 | ||
| 29 | + * 连续走了9步以下,停留超过3秒,则计数清空 | ||
| 30 | + * */ | ||
| 31 | + @Override | ||
| 32 | + public void countStep() { | ||
| 33 | + this.timeOfLastPeak = this.timeOfThisPeak; | ||
| 34 | + this.timeOfThisPeak = System.currentTimeMillis(); | ||
| 35 | + if (this.timeOfThisPeak - this.timeOfLastPeak <= 3000L) { | ||
| 36 | + if (this.count < 9) { | ||
| 37 | + this.count++; | ||
| 38 | + } else if (this.count == 9) { | ||
| 39 | + this.count++; | ||
| 40 | + this.mCount += this.count; | ||
| 41 | + notifyListener(); | ||
| 42 | + } else { | ||
| 43 | + this.mCount++; | ||
| 44 | + notifyListener(); | ||
| 45 | + } | ||
| 46 | + } else {//超时 | ||
| 47 | + this.count = 1;//为1,不是0 | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + public void initListener(StepValuePassListener listener) { | ||
| 53 | + this.mStepValuePassListener = listener; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public void notifyListener() { | ||
| 57 | + if (this.mStepValuePassListener != null) | ||
| 58 | + this.mStepValuePassListener.stepChanged(this.mCount); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + | ||
| 62 | + public void setSteps(int initValue) { | ||
| 63 | + this.mCount = initValue; | ||
| 64 | + this.count = 0; | ||
| 65 | + timeOfLastPeak = 0; | ||
| 66 | + timeOfThisPeak = 0; | ||
| 67 | + notifyListener(); | ||
| 68 | + } | ||
| 69 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/model/step/StepCountListener.java
0 → 100644
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/model/step/StepDetector.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.model.step; | ||
| 2 | + | ||
| 3 | + | ||
| 4 | +import android.hardware.Sensor; | ||
| 5 | +import android.hardware.SensorEvent; | ||
| 6 | +import android.hardware.SensorEventListener; | ||
| 7 | +/* | ||
| 8 | + * 算法的主要部分,检测是否是步点 | ||
| 9 | + * */ | ||
| 10 | + | ||
| 11 | +public class StepDetector implements SensorEventListener { | ||
| 12 | + | ||
| 13 | + //存放三轴数据 | ||
| 14 | + float[] oriValues = new float[3]; | ||
| 15 | + final int ValueNum = 4; | ||
| 16 | + //用于存放计算阈值的波峰波谷差值 | ||
| 17 | + float[] tempValue = new float[ValueNum]; | ||
| 18 | + int tempCount = 0; | ||
| 19 | + //是否上升的标志位 | ||
| 20 | + boolean isDirectionUp = false; | ||
| 21 | + //持续上升次数 | ||
| 22 | + int continueUpCount = 0; | ||
| 23 | + //上一点的持续上升的次数,为了记录波峰的上升次数 | ||
| 24 | + int continueUpFormerCount = 0; | ||
| 25 | + //上一点的状态,上升还是下降 | ||
| 26 | + boolean lastStatus = false; | ||
| 27 | + //波峰值 | ||
| 28 | + float peakOfWave = 0; | ||
| 29 | + //波谷值 | ||
| 30 | + float valleyOfWave = 0; | ||
| 31 | + //此次波峰的时间 | ||
| 32 | + long timeOfThisPeak = 0; | ||
| 33 | + //上次波峰的时间 | ||
| 34 | + long timeOfLastPeak = 0; | ||
| 35 | + //当前的时间 | ||
| 36 | + long timeOfNow = 0; | ||
| 37 | + //当前传感器的值 | ||
| 38 | + float gravityNew = 0; | ||
| 39 | + //上次传感器的值 | ||
| 40 | + float gravityOld = 0; | ||
| 41 | + //动态阈值需要动态的数据,这个值用于这些动态数据的阈值 | ||
| 42 | + final float InitialValue = (float) 1.3; | ||
| 43 | + //初始阈值 | ||
| 44 | + float ThreadValue = (float) 2.0; | ||
| 45 | + //波峰波谷时间差 | ||
| 46 | + int TimeInterval = 250; | ||
| 47 | + private StepCountListener mStepListeners; | ||
| 48 | + | ||
| 49 | + @Override | ||
| 50 | + public void onSensorChanged(SensorEvent event) { | ||
| 51 | + for (int i = 0; i < 3; i++) { | ||
| 52 | + oriValues[i] = event.values[i]; | ||
| 53 | + } | ||
| 54 | + gravityNew = (float) Math.sqrt(oriValues[0] * oriValues[0] | ||
| 55 | + + oriValues[1] * oriValues[1] + oriValues[2] * oriValues[2]); | ||
| 56 | + detectorNewStep(gravityNew); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + @Override | ||
| 60 | + public void onAccuracyChanged(Sensor sensor, int accuracy) { | ||
| 61 | + // | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + public void initListener(StepCountListener listener) { | ||
| 65 | + this.mStepListeners = listener; | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + /* | ||
| 69 | + * 检测步子,并开始计步 | ||
| 70 | + * 1.传入sersor中的数据 | ||
| 71 | + * 2.如果检测到了波峰,并且符合时间差以及阈值的条件,则判定为1步 | ||
| 72 | + * 3.符合时间差条件,波峰波谷差值大于initialValue,则将该差值纳入阈值的计算中 | ||
| 73 | + * */ | ||
| 74 | + public void detectorNewStep(float values) { | ||
| 75 | + if (gravityOld == 0) { | ||
| 76 | + gravityOld = values; | ||
| 77 | + } else { | ||
| 78 | + if (detectorPeak(values, gravityOld)) { | ||
| 79 | + timeOfLastPeak = timeOfThisPeak; | ||
| 80 | + timeOfNow = System.currentTimeMillis(); | ||
| 81 | + if (timeOfNow - timeOfLastPeak >= TimeInterval | ||
| 82 | + && (peakOfWave - valleyOfWave >= ThreadValue)) { | ||
| 83 | + timeOfThisPeak = timeOfNow; | ||
| 84 | + /* | ||
| 85 | + * 更新界面的处理,不涉及到算法 | ||
| 86 | + * 一般在通知更新界面之前,增加下面处理,为了处理无效运动: | ||
| 87 | + * 1.连续记录10才开始计步 | ||
| 88 | + * 2.例如记录的9步用户停住超过3秒,则前面的记录失效,下次从头开始 | ||
| 89 | + * 3.连续记录了9步用户还在运动,之前的数据才有效 | ||
| 90 | + * */ | ||
| 91 | + mStepListeners.countStep(); | ||
| 92 | + } | ||
| 93 | + if (timeOfNow - timeOfLastPeak >= TimeInterval | ||
| 94 | + && (peakOfWave - valleyOfWave >= InitialValue)) { | ||
| 95 | + timeOfThisPeak = timeOfNow; | ||
| 96 | + ThreadValue = peakValleyThread(peakOfWave - valleyOfWave); | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + gravityOld = values; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + /* | ||
| 104 | + * 检测波峰 | ||
| 105 | + * 以下四个条件判断为波峰: | ||
| 106 | + * 1.目前点为下降的趋势:isDirectionUp为false | ||
| 107 | + * 2.之前的点为上升的趋势:lastStatus为true | ||
| 108 | + * 3.到波峰为止,持续上升大于等于2次 | ||
| 109 | + * 4.波峰值大于20 | ||
| 110 | + * 记录波谷值 | ||
| 111 | + * 1.观察波形图,可以发现在出现步子的地方,波谷的下一个就是波峰,有比较明显的特征以及差值 | ||
| 112 | + * 2.所以要记录每次的波谷值,为了和下次的波峰做对比 | ||
| 113 | + * */ | ||
| 114 | + public boolean detectorPeak(float newValue, float oldValue) { | ||
| 115 | + lastStatus = isDirectionUp; | ||
| 116 | + if (newValue >= oldValue) { | ||
| 117 | + isDirectionUp = true; | ||
| 118 | + continueUpCount++; | ||
| 119 | + } else { | ||
| 120 | + continueUpFormerCount = continueUpCount; | ||
| 121 | + continueUpCount = 0; | ||
| 122 | + isDirectionUp = false; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + if (!isDirectionUp && lastStatus | ||
| 126 | + && (continueUpFormerCount >= 2 || oldValue >= 20)) { | ||
| 127 | + peakOfWave = oldValue; | ||
| 128 | + return true; | ||
| 129 | + } else if (!lastStatus && isDirectionUp) { | ||
| 130 | + valleyOfWave = oldValue; | ||
| 131 | + return false; | ||
| 132 | + } else { | ||
| 133 | + return false; | ||
| 134 | + } | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + /* | ||
| 138 | + * 阈值的计算 | ||
| 139 | + * 1.通过波峰波谷的差值计算阈值 | ||
| 140 | + * 2.记录4个值,存入tempValue[]数组中 | ||
| 141 | + * 3.在将数组传入函数averageValue中计算阈值 | ||
| 142 | + * */ | ||
| 143 | + public float peakValleyThread(float value) { | ||
| 144 | + float tempThread = ThreadValue; | ||
| 145 | + if (tempCount < ValueNum) { | ||
| 146 | + tempValue[tempCount] = value; | ||
| 147 | + tempCount++; | ||
| 148 | + } else { | ||
| 149 | + tempThread = averageValue(tempValue, ValueNum); | ||
| 150 | + for (int i = 1; i < ValueNum; i++) { | ||
| 151 | + tempValue[i - 1] = tempValue[i]; | ||
| 152 | + } | ||
| 153 | + tempValue[ValueNum - 1] = value; | ||
| 154 | + } | ||
| 155 | + return tempThread; | ||
| 156 | + | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + /* | ||
| 160 | + * 梯度化阈值 | ||
| 161 | + * 1.计算数组的均值 | ||
| 162 | + * 2.通过均值将阈值梯度化在一个范围里 | ||
| 163 | + * */ | ||
| 164 | + public float averageValue(float value[], int n) { | ||
| 165 | + float ave = 0; | ||
| 166 | + for (int i = 0; i < n; i++) { | ||
| 167 | + ave += value[i]; | ||
| 168 | + } | ||
| 169 | + ave = ave / ValueNum; | ||
| 170 | + if (ave >= 8) { | ||
| 171 | + ave = (float) 4.3; | ||
| 172 | + } else if (ave >= 7 && ave < 8) { | ||
| 173 | + ave = (float) 3.3; | ||
| 174 | + } else if (ave >= 4 && ave < 7) { | ||
| 175 | + ave = (float) 2.3; | ||
| 176 | + } else if (ave >= 3 && ave < 4) { | ||
| 177 | + ave = (float) 2.0; | ||
| 178 | + } else { | ||
| 179 | + ave = (float) 1.3; | ||
| 180 | + } | ||
| 181 | + return ave; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/model/step/StepValuePassListener.java
0 → 100644
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/receivers/MyReceiver.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.receivers; | ||
| 2 | + | ||
| 3 | +import android.annotation.SuppressLint; | ||
| 4 | +import android.content.BroadcastReceiver; | ||
| 5 | +import android.content.Context; | ||
| 6 | +import android.content.Intent; | ||
| 7 | +import android.os.PowerManager; | ||
| 8 | +import android.util.Log; | ||
| 9 | + | ||
| 10 | +import com.cnlive.moudle.pedometer.service.KeepLiveService; | ||
| 11 | +import com.cnlive.moudle.pedometer.service.StepService; | ||
| 12 | +import com.cnlive.moudle.pedometer.utils.ServiceUtils; | ||
| 13 | + | ||
| 14 | +public class MyReceiver extends BroadcastReceiver { | ||
| 15 | + | ||
| 16 | + private PowerManager.WakeLock wakeLock = null; | ||
| 17 | + | ||
| 18 | + public MyReceiver() { | ||
| 19 | + | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + public MyReceiver(PowerManager.WakeLock wakeLock) { | ||
| 23 | + super(); | ||
| 24 | + this.wakeLock = wakeLock; | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + @SuppressLint("Wakelock") | ||
| 28 | + @Override | ||
| 29 | + public void onReceive(Context context, Intent intent) { | ||
| 30 | + Log.e("广播", "接收到广播:" + intent.getAction()); | ||
| 31 | + if (!ServiceUtils.isServiceRunning(context, "com.cnlive.moudle.pedometer.service.KeepLiveService")) { | ||
| 32 | + context.startService(new Intent(context, KeepLiveService.class)); | ||
| 33 | + Log.e("启动服务", "启动MyIntentService"); | ||
| 34 | + } | ||
| 35 | + if (!ServiceUtils.isServiceRunning(context, "com.cnlive.moudle.pedometer.service.StepService")) { | ||
| 36 | + context.startService(new Intent(context, StepService.class)); | ||
| 37 | + Log.e("启动服务", "启动MyTestService"); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + String action = intent.getAction(); | ||
| 41 | + | ||
| 42 | + if (Intent.ACTION_SCREEN_OFF.equals(action)) { | ||
| 43 | + if (null != wakeLock && !(wakeLock.isHeld())) { | ||
| 44 | + wakeLock.acquire(); | ||
| 45 | + Log.e("启动电量锁", "启动"); | ||
| 46 | + } | ||
| 47 | + //启动保活服务 | ||
| 48 | +// KeepLiveService.getInstance().startPlaySong(); | ||
| 49 | + | ||
| 50 | +// Log.e("启动保活", "启动"); | ||
| 51 | + } else if (Intent.ACTION_SCREEN_ON.equals(action) || Intent.ACTION_USER_PRESENT.equals(action)) { | ||
| 52 | + if (null != wakeLock && wakeLock.isHeld()) { | ||
| 53 | + wakeLock.release(); | ||
| 54 | + Log.e("重置电量锁", "释放"); | ||
| 55 | + } | ||
| 56 | + //暂停保活服务 | ||
| 57 | +// KeepLiveService.getInstance().stopPlaySong(); | ||
| 58 | +// Log.e("停止保活", "停止"); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + } | ||
| 62 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/service/KeepLiveService.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.service; | ||
| 2 | + | ||
| 3 | +import android.annotation.SuppressLint; | ||
| 4 | +import android.app.Activity; | ||
| 5 | +import android.app.Notification; | ||
| 6 | +import android.app.NotificationChannel; | ||
| 7 | +import android.app.NotificationManager; | ||
| 8 | +import android.app.PendingIntent; | ||
| 9 | +import android.app.Service; | ||
| 10 | +import android.content.ComponentName; | ||
| 11 | +import android.content.Context; | ||
| 12 | +import android.content.Intent; | ||
| 13 | +import android.content.IntentFilter; | ||
| 14 | +import android.media.MediaPlayer; | ||
| 15 | +import android.os.Build; | ||
| 16 | +import android.os.IBinder; | ||
| 17 | +import android.os.PowerManager; | ||
| 18 | +import android.support.v4.app.NotificationCompat; | ||
| 19 | +import android.util.Log; | ||
| 20 | +import android.widget.RemoteViews; | ||
| 21 | + | ||
| 22 | +import com.cnlive.libs.base.util.SharedPreferencesHelper; | ||
| 23 | +import com.cnlive.moudle.pedometer.receivers.MyReceiver; | ||
| 24 | +import com.cnlive.moudle.pedometer.utils.ServiceUtils; | ||
| 25 | +import com.example.moudle_pedometer.R; | ||
| 26 | + | ||
| 27 | +import java.io.IOException; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * @author ShinnyYang | ||
| 31 | + * 维持后台运行的服务 | ||
| 32 | + */ | ||
| 33 | +public class KeepLiveService extends Service implements MediaPlayer.OnCompletionListener { | ||
| 34 | + //唤醒锁 | ||
| 35 | + private PowerManager.WakeLock wakeLock = null; | ||
| 36 | + //电源管理器 | ||
| 37 | + private PowerManager powerManager = null; | ||
| 38 | + //音频播放器 | ||
| 39 | + public static MediaPlayer mMediaPlayer; | ||
| 40 | + //广播 | ||
| 41 | + private static MyReceiver myReceiver; | ||
| 42 | + private String CHANNEL_ONE_ID = "com.cnlive"; | ||
| 43 | + private String CHANNEL_ONE_NAME = "Channel One"; | ||
| 44 | + private NotificationManager mNM; | ||
| 45 | + private Notification notification = null; | ||
| 46 | + //是否在后台运行 | ||
| 47 | + public static boolean isKeepService = true; | ||
| 48 | + private static KeepLiveService instance; | ||
| 49 | + private int startId; | ||
| 50 | + | ||
| 51 | + public KeepLiveService() { | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + public static KeepLiveService getInstance() { | ||
| 55 | + if (instance == null) { | ||
| 56 | + instance = new KeepLiveService(); | ||
| 57 | + return instance; | ||
| 58 | + } else { | ||
| 59 | + return instance; | ||
| 60 | + } | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 64 | + public void onCreate() { | ||
| 65 | + instance = this; | ||
| 66 | +// if (!ServiceUtils.isServiceRunning(getApplicationContext(), "com.cnlive.moudle.pedometer.service.StepService")) { | ||
| 67 | +// startService(new Intent(getApplicationContext(), StepService.class)); | ||
| 68 | +// } | ||
| 69 | + init(); | ||
| 70 | + initBroadCastReceiver(); | ||
| 71 | + super.onCreate(); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 初始化广播 | ||
| 76 | + */ | ||
| 77 | + private void initBroadCastReceiver() { | ||
| 78 | + if (wakeLock != null) { | ||
| 79 | + myReceiver = new MyReceiver(wakeLock); | ||
| 80 | + } else { | ||
| 81 | + myReceiver = new MyReceiver(); | ||
| 82 | + } | ||
| 83 | + IntentFilter intentFilter = new IntentFilter(); | ||
| 84 | + intentFilter.addAction(Intent.ACTION_SCREEN_OFF); | ||
| 85 | + intentFilter.addAction(Intent.ACTION_SCREEN_ON); | ||
| 86 | +// intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); | ||
| 87 | +// intentFilter.addAction(Intent.ACTION_TIME_CHANGED); | ||
| 88 | +// intentFilter.addAction(Intent.ACTION_POWER_CONNECTED); | ||
| 89 | +// intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED); | ||
| 90 | + intentFilter.addAction("com.cnlive.moudle.pedometer.receivers.MyReceiver"); | ||
| 91 | + registerReceiver(myReceiver, intentFilter); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + /** | ||
| 95 | + * 初始化相关参数 | ||
| 96 | + */ | ||
| 97 | + @SuppressLint("InvalidWakeLockTag") | ||
| 98 | + private void init() { | ||
| 99 | + if (null == powerManager) { | ||
| 100 | + powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); | ||
| 101 | + } | ||
| 102 | + if (null == wakeLock) { | ||
| 103 | + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "track upload"); | ||
| 104 | + } | ||
| 105 | + if (null == mNM) { | ||
| 106 | + mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + @SuppressLint("WrongConstant") | ||
| 112 | + @Override | ||
| 113 | + public int onStartCommand(Intent intent, int flags, int startId) { | ||
| 114 | + this.startId = startId; | ||
| 115 | + showHideForegroundNotification(); | ||
| 116 | + startPlaySong(); | ||
| 117 | + flags = START_STICKY; | ||
| 118 | + return super.onStartCommand(intent, flags, startId); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + //开始、暂停播放 | ||
| 122 | + public void startPlaySong() { | ||
| 123 | + if (mMediaPlayer == null) { | ||
| 124 | + mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.no_kill); | ||
| 125 | + Log.e("音乐启动播放,播放对象为: ", "" + mMediaPlayer.hashCode()); | ||
| 126 | + } else { | ||
| 127 | + Log.e("音乐启动播放,播放对象为: ", "" + mMediaPlayer.hashCode()); | ||
| 128 | + } | ||
| 129 | + mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); | ||
| 130 | + if (!mMediaPlayer.isPlaying()) { | ||
| 131 | + mMediaPlayer.start(); | ||
| 132 | + } | ||
| 133 | + Log.e("是否播放:", mMediaPlayer.isPlaying() + ""); | ||
| 134 | + mMediaPlayer.setOnCompletionListener(this); | ||
| 135 | + mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { | ||
| 136 | + @Override | ||
| 137 | + public void onPrepared(MediaPlayer mediaPlayer) { | ||
| 138 | + Log.e("加载完成:", ""); | ||
| 139 | + | ||
| 140 | + } | ||
| 141 | + }); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + //停止播放销毁对象 | ||
| 145 | + public void stopPlaySong() { | ||
| 146 | + if (mMediaPlayer != null) { | ||
| 147 | + mMediaPlayer.stop(); | ||
| 148 | + Log.e("音乐停止播放,播放对象为:", "" + mMediaPlayer.hashCode()); | ||
| 149 | + Log.e("音乐播放器是否在循环:", "" + mMediaPlayer.isLooping()); | ||
| 150 | + Log.e("音乐播放器是否还在播放:", "" + mMediaPlayer.isPlaying()); | ||
| 151 | + mMediaPlayer.release(); | ||
| 152 | + Log.e("播放对象销毁,播放对象为:", "" + mMediaPlayer.hashCode()); | ||
| 153 | + mMediaPlayer = null; | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + | ||
| 158 | + private void showHideForegroundNotification() { | ||
| 159 | + NotificationChannel channel = null; | ||
| 160 | + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { | ||
| 161 | + channel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, | ||
| 162 | + NotificationManager.IMPORTANCE_HIGH); | ||
| 163 | + channel.enableLights(false); | ||
| 164 | + channel.enableVibration(false); | ||
| 165 | + channel.setVibrationPattern(new long[]{0}); | ||
| 166 | + channel.setSound(null, null); | ||
| 167 | + mNM.createNotificationChannel(channel); | ||
| 168 | + } | ||
| 169 | + notification = new NotificationCompat.Builder(this, CHANNEL_ONE_ID) | ||
| 170 | + .setWhen(System.currentTimeMillis()) | ||
| 171 | + .setSound(null) | ||
| 172 | + .build(); | ||
| 173 | + // 0 隐藏前台服务通知 | ||
| 174 | + startForeground(0, notification); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + @Override | ||
| 178 | + public void onDestroy() { | ||
| 179 | + super.onDestroy(); | ||
| 180 | + Log.e("keep", "停止后"); | ||
| 181 | + if (myReceiver != null) { | ||
| 182 | + unregisterReceiver(myReceiver); | ||
| 183 | + } | ||
| 184 | + stopForeground(true); | ||
| 185 | + stopPlaySong(); | ||
| 186 | + boolean result = SharedPreferencesHelper.getInstance(getApplicationContext()).getBoolean("isKeepAlive", false); | ||
| 187 | + Log.e("状态", result + ""); | ||
| 188 | + //判断是否保持服务 | ||
| 189 | + if (result) { | ||
| 190 | +// // 重启自己 | ||
| 191 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
| 192 | + startForegroundService(new Intent(getApplicationContext(), KeepLiveService.class)); | ||
| 193 | + } else { | ||
| 194 | + startService(new Intent(getApplicationContext(), KeepLiveService.class)); | ||
| 195 | + } | ||
| 196 | + //发送重启广播 | ||
| 197 | + sendRestartBroadCast(); | ||
| 198 | + } | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + | ||
| 202 | + /** | ||
| 203 | + * 发送重启广播 | ||
| 204 | + */ | ||
| 205 | + private void sendRestartBroadCast() { | ||
| 206 | + Intent intent = new Intent(" com.cnlive.moudle.pedometer.receivers.MyReceiver"); | ||
| 207 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
| 208 | + intent.setComponent(new ComponentName(getPackageName(), " com.cnlive.moudle.pedometer.receivers.MyReceiver"));//高版本Android发送广播需要加这句 | ||
| 209 | + } | ||
| 210 | + sendBroadcast(intent); | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + @Override | ||
| 214 | + public IBinder onBind(Intent intent) { | ||
| 215 | + // TODO: Return the communication channel to the service. | ||
| 216 | + throw new UnsupportedOperationException("Not yet implemented"); | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + | ||
| 220 | + /** | ||
| 221 | + * 歌曲播放完成 | ||
| 222 | + * | ||
| 223 | + * @param mediaPlayer | ||
| 224 | + */ | ||
| 225 | + @Override | ||
| 226 | + public void onCompletion(MediaPlayer mediaPlayer) { | ||
| 227 | + if (isKeepService) { | ||
| 228 | + mediaPlayer.start(); | ||
| 229 | + } | ||
| 230 | + Intent intent = new Intent(" com.cnlive.moudle.pedometer.receivers.MyReceiver"); | ||
| 231 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
| 232 | + intent.setComponent(new ComponentName(getPackageName(), " com.cnlive.moudle.pedometer.receivers.MyReceiver"));//高版本Android发送广播需要加这句 | ||
| 233 | + } | ||
| 234 | + sendBroadcast(intent); | ||
| 235 | + Log.e("重新播放", "开始"); | ||
| 236 | + } | ||
| 237 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/service/StepService.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.service; | ||
| 2 | + | ||
| 3 | +import android.annotation.SuppressLint; | ||
| 4 | +import android.app.Notification; | ||
| 5 | +import android.app.NotificationChannel; | ||
| 6 | +import android.app.NotificationManager; | ||
| 7 | +import android.app.PendingIntent; | ||
| 8 | +import android.app.Service; | ||
| 9 | +import android.content.BroadcastReceiver; | ||
| 10 | +import android.content.ComponentName; | ||
| 11 | +import android.content.Context; | ||
| 12 | +import android.content.Intent; | ||
| 13 | +import android.content.IntentFilter; | ||
| 14 | +import android.hardware.Sensor; | ||
| 15 | +import android.hardware.SensorEvent; | ||
| 16 | +import android.hardware.SensorEventListener; | ||
| 17 | +import android.hardware.SensorManager; | ||
| 18 | +import android.os.Binder; | ||
| 19 | +import android.os.Build; | ||
| 20 | +import android.os.CountDownTimer; | ||
| 21 | +import android.os.IBinder; | ||
| 22 | +import android.support.v4.app.NotificationCompat; | ||
| 23 | +import android.util.Log; | ||
| 24 | +import android.widget.RemoteViews; | ||
| 25 | + | ||
| 26 | +import com.cnlive.moudle.pedometer.model.step.StepCount; | ||
| 27 | +import com.cnlive.moudle.pedometer.model.step.StepValuePassListener; | ||
| 28 | +import com.cnlive.moudle.pedometer.sql.dao.UserStepEntityDao; | ||
| 29 | +import com.cnlive.moudle.pedometer.sql.db.DbCore; | ||
| 30 | +import com.cnlive.moudle.pedometer.sql.entity.UserStepEntity; | ||
| 31 | +import com.cnlive.moudle.pedometer.ui.activity.PedometerMainActivity; | ||
| 32 | +import com.cnlive.moudle.pedometer.utils.ServiceUtils; | ||
| 33 | +import com.example.moudle_pedometer.R; | ||
| 34 | +import com.orhanobut.logger.Logger; | ||
| 35 | + | ||
| 36 | +import org.greenrobot.greendao.DbUtils; | ||
| 37 | + | ||
| 38 | +import java.text.SimpleDateFormat; | ||
| 39 | +import java.util.Date; | ||
| 40 | +import java.util.List; | ||
| 41 | +import java.util.UUID; | ||
| 42 | + | ||
| 43 | +public class StepService extends Service implements SensorEventListener { | ||
| 44 | + private String TAG = "StepService"; | ||
| 45 | + public static boolean isKeep = true; | ||
| 46 | + /** | ||
| 47 | + * 通知栏相关 | ||
| 48 | + */ | ||
| 49 | + private String CHANNEL_ONE_ID = "com.cnlive"; | ||
| 50 | + private String CHANNEL_ONE_NAME = "Channel Two"; | ||
| 51 | + private NotificationManager mNM; | ||
| 52 | + private Notification notification = null; | ||
| 53 | + private RemoteViews remoteViews; | ||
| 54 | + /** | ||
| 55 | + * 默认为30秒进行一次存储 | ||
| 56 | + */ | ||
| 57 | + private static int duration = 30 * 1000; | ||
| 58 | + /** | ||
| 59 | + * 当前的日期 | ||
| 60 | + */ | ||
| 61 | + private static String CURRENT_DATE = ""; | ||
| 62 | + /** | ||
| 63 | + * 传感器管理对象 | ||
| 64 | + */ | ||
| 65 | + private SensorManager sensorManager; | ||
| 66 | + /** | ||
| 67 | + * 广播接受者 | ||
| 68 | + */ | ||
| 69 | + private BroadcastReceiver mBatInfoReceiver; | ||
| 70 | + /** | ||
| 71 | + * 保存记步计时器 | ||
| 72 | + */ | ||
| 73 | + private TimeCount time; | ||
| 74 | + /** | ||
| 75 | + * 当前所走的步数 | ||
| 76 | + */ | ||
| 77 | + private int CURRENT_STEP; | ||
| 78 | + /** | ||
| 79 | + * 计步传感器类型 Sensor.TYPE_STEP_COUNTER或者Sensor.TYPE_STEP_DETECTOR | ||
| 80 | + */ | ||
| 81 | + private static int stepSensorType = -1; | ||
| 82 | + /** | ||
| 83 | + * 每次第一次启动记步服务时是否从系统中获取了已有的步数记录 | ||
| 84 | + */ | ||
| 85 | + private boolean hasRecord = false; | ||
| 86 | + /** | ||
| 87 | + * 系统中获取到的已有的步数 | ||
| 88 | + */ | ||
| 89 | + private int hasStepCount = 0; | ||
| 90 | + /** | ||
| 91 | + * 上一次的步数 | ||
| 92 | + */ | ||
| 93 | + private int previousStepCount = 0; | ||
| 94 | + /** | ||
| 95 | + * 加速度传感器中获取的步数 | ||
| 96 | + */ | ||
| 97 | + private StepCount mStepCount; | ||
| 98 | + /** | ||
| 99 | + * IBinder对象,向Activity传递数据的桥梁 | ||
| 100 | + */ | ||
| 101 | + private StepBinder stepBinder = new StepBinder(); | ||
| 102 | + private String userId; | ||
| 103 | + | ||
| 104 | + public StepService() { | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + @Override | ||
| 108 | + public void onCreate() { | ||
| 109 | + super.onCreate(); | ||
| 110 | + init(); | ||
| 111 | + //显示计步通知 | ||
| 112 | + showStartForegroundNotification(); | ||
| 113 | + userId = "123"; | ||
| 114 | + initTodayData(userId); | ||
| 115 | + initBroadcastReceiver(); | ||
| 116 | + new Thread(new Runnable() { | ||
| 117 | + @Override | ||
| 118 | + public void run() { | ||
| 119 | + startStepDetector(); | ||
| 120 | + } | ||
| 121 | + }).start(); | ||
| 122 | + startTimeCount(); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + private void init() { | ||
| 126 | + if (remoteViews == null) { | ||
| 127 | + remoteViews = new RemoteViews(getPackageName(), R.layout.custom_step_notification); | ||
| 128 | + } | ||
| 129 | + if (null == mNM) { | ||
| 130 | + mNM = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + @SuppressLint("WrongConstant") | ||
| 135 | + @Override | ||
| 136 | + public int onStartCommand(Intent intent, int flags, int startId) { | ||
| 137 | + if (!ServiceUtils.isServiceRunning(getApplicationContext(), "com.cnlive.moudle.pedometer.service.KeepLiveService")) { | ||
| 138 | + startService(new Intent(getApplicationContext(), KeepLiveService.class)); | ||
| 139 | + } | ||
| 140 | + flags = START_STICKY; | ||
| 141 | + return super.onStartCommand(intent, flags, startId); | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + private void showStartForegroundNotification() { | ||
| 145 | + NotificationChannel channel = null; | ||
| 146 | + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { | ||
| 147 | + channel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, | ||
| 148 | + NotificationManager.IMPORTANCE_HIGH); | ||
| 149 | + channel.enableLights(false); | ||
| 150 | + channel.setSound(null, null); | ||
| 151 | +// channel.setLightColor(getColor(R.color.colorPrimary)); | ||
| 152 | + channel.enableVibration(false); | ||
| 153 | + channel.setVibrationPattern(new long[]{0}); | ||
| 154 | + channel.setSound(null, null); | ||
| 155 | + mNM.createNotificationChannel(channel); | ||
| 156 | + } | ||
| 157 | + Intent notificationIntent = new Intent(this, PedometerMainActivity.class); | ||
| 158 | + notification = new NotificationCompat.Builder(this, CHANNEL_ONE_ID) | ||
| 159 | + .setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0)) | ||
| 160 | + .setSmallIcon(R.drawable.ic_icon) | ||
| 161 | +// .setContentText("前台服务正在运行") | ||
| 162 | +// .setContentTitle("标题") | ||
| 163 | + .setCustomContentView(remoteViews) | ||
| 164 | + .setSound(null) | ||
| 165 | + .setWhen(System.currentTimeMillis()) | ||
| 166 | + .build(); | ||
| 167 | + startForeground(1, notification); | ||
| 168 | + | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + @Override | ||
| 172 | + public IBinder onBind(Intent intent) { | ||
| 173 | + return stepBinder; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + | ||
| 177 | + @Override | ||
| 178 | + public void onDestroy() { | ||
| 179 | + super.onDestroy(); | ||
| 180 | + //取消前台进程 | ||
| 181 | + stopForeground(true); | ||
| 182 | + unregisterReceiver(mBatInfoReceiver); | ||
| 183 | + stopSelf(); | ||
| 184 | + if (isKeep) { | ||
| 185 | + sendRestartBroadCast(); | ||
| 186 | + } | ||
| 187 | + Log.e("杀死", "MyTestService"); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + /** | ||
| 191 | + * 发送重启广播 | ||
| 192 | + */ | ||
| 193 | + private void sendRestartBroadCast() { | ||
| 194 | + Intent intent = new Intent(" com.cnlive.moudle.pedometer.receivers.MyReceiver"); | ||
| 195 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
| 196 | + intent.setComponent(new ComponentName(getPackageName(), " com.cnlive.moudle.pedometer.receivers.MyReceiver"));//高版本Android发送广播需要加这句 | ||
| 197 | + } | ||
| 198 | + sendBroadcast(intent); | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + | ||
| 202 | + @Override | ||
| 203 | + public void onAccuracyChanged(Sensor sensor, int i) { | ||
| 204 | + | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + /** | ||
| 208 | + * @获取默认的pendingIntent,为了防止2.3及以下版本报错 | ||
| 209 | + * @flags属性: 在顶部常驻:Notification.FLAG_ONGOING_EVENT | ||
| 210 | + * 点击去除: Notification.FLAG_AUTO_CANCEL | ||
| 211 | + */ | ||
| 212 | + public PendingIntent getDefalutIntent(int flags) { | ||
| 213 | + PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, new Intent(), flags); | ||
| 214 | + return pendingIntent; | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + /** | ||
| 218 | + * 获取当天日期 | ||
| 219 | + * | ||
| 220 | + * @return | ||
| 221 | + */ | ||
| 222 | + private String getTodayDate() { | ||
| 223 | + Date date = new Date(System.currentTimeMillis()); | ||
| 224 | + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); | ||
| 225 | + return sdf.format(date); | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + /** | ||
| 229 | + * 初始化当天的步数 | ||
| 230 | + */ | ||
| 231 | + private void initTodayData(String userId) { | ||
| 232 | + CURRENT_DATE = getTodayDate(); | ||
| 233 | + DbCore.init(getApplicationContext(), "Step.db"); | ||
| 234 | +// //获取当天的数据,用于展示 CURRENT_DATE | ||
| 235 | + List<UserStepEntity> list = DbCore.getDaoSession().getUserStepEntityDao().queryBuilder().where(UserStepEntityDao.Properties.UserId.eq(userId), UserStepEntityDao.Properties.Date.eq(CURRENT_DATE)).list(); | ||
| 236 | + if (list.size() == 0 || list.isEmpty()) { | ||
| 237 | + CURRENT_STEP = 0; | ||
| 238 | + } else if (list.size() == 1) { | ||
| 239 | + Log.v(TAG, "StepData=" + list.get(0).toString()); | ||
| 240 | + CURRENT_STEP = Integer.parseInt(list.get(0).getStepCount()); | ||
| 241 | + } else { | ||
| 242 | + Log.v(TAG, "出错了!"); | ||
| 243 | + } | ||
| 244 | + if (mStepCount != null) { | ||
| 245 | + mStepCount.setSteps(CURRENT_STEP); | ||
| 246 | + } | ||
| 247 | + updateNotification(); | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + private void updateNotification() { | ||
| 251 | + remoteViews.setTextViewText(R.id.tv_step_count, CURRENT_STEP + ""); | ||
| 252 | + mNM.notify(1, notification); | ||
| 253 | + | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + /** | ||
| 257 | + * 注册广播 | ||
| 258 | + */ | ||
| 259 | + private void initBroadcastReceiver() { | ||
| 260 | + final IntentFilter filter = new IntentFilter(); | ||
| 261 | + // 屏幕灭屏广播 | ||
| 262 | + filter.addAction(Intent.ACTION_SCREEN_OFF); | ||
| 263 | + //关机广播 | ||
| 264 | + filter.addAction(Intent.ACTION_SHUTDOWN); | ||
| 265 | + // 屏幕亮屏广播 | ||
| 266 | + filter.addAction(Intent.ACTION_SCREEN_ON); | ||
| 267 | + // 屏幕解锁广播 | ||
| 268 | +// filter.addAction(Intent.ACTION_USER_PRESENT); | ||
| 269 | + // 当长按电源键弹出“关机”对话或者锁屏时系统会发出这个广播 | ||
| 270 | + // example:有时候会用到系统对话框,权限可能很高,会覆盖在锁屏界面或者“关机”对话框之上, | ||
| 271 | + // 所以监听这个广播,当收到时就隐藏自己的对话,如点击pad右下角部分弹出的对话框 | ||
| 272 | + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); | ||
| 273 | + //监听日期变化 | ||
| 274 | + filter.addAction(Intent.ACTION_DATE_CHANGED); | ||
| 275 | + filter.addAction(Intent.ACTION_TIME_CHANGED); | ||
| 276 | + filter.addAction(Intent.ACTION_TIME_TICK); | ||
| 277 | + | ||
| 278 | + mBatInfoReceiver = new BroadcastReceiver() { | ||
| 279 | + @Override | ||
| 280 | + public void onReceive(final Context context, final Intent intent) { | ||
| 281 | + String action = intent.getAction(); | ||
| 282 | + if (Intent.ACTION_SCREEN_ON.equals(action)) { | ||
| 283 | + Log.d(TAG, "screen on"); | ||
| 284 | + } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { | ||
| 285 | + Log.d(TAG, "screen off"); | ||
| 286 | + //改为60秒一存储 | ||
| 287 | + duration = 60000; | ||
| 288 | + } else if (Intent.ACTION_USER_PRESENT.equals(action)) { | ||
| 289 | + Log.d(TAG, "screen unlock"); | ||
| 290 | + save(); | ||
| 291 | + //改为30秒一存储 | ||
| 292 | + duration = 30000; | ||
| 293 | + } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { | ||
| 294 | + Log.i(TAG, " receive Intent.ACTION_CLOSE_SYSTEM_DIALOGS"); | ||
| 295 | + //保存一次 | ||
| 296 | + save(); | ||
| 297 | + } else if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { | ||
| 298 | + Log.i(TAG, " receive ACTION_SHUTDOWN"); | ||
| 299 | + save(); | ||
| 300 | + } else if (Intent.ACTION_DATE_CHANGED.equals(action)) {//日期变化步数重置为0 | ||
| 301 | +// Logger.d("重置步数" + StepDcretor.CURRENT_STEP); | ||
| 302 | + save(); | ||
| 303 | + isNewDay(); | ||
| 304 | + } else if (Intent.ACTION_TIME_CHANGED.equals(action)) { | ||
| 305 | + //时间变化步数重置为0 | ||
| 306 | +// isCall(); | ||
| 307 | + save(); | ||
| 308 | + isNewDay(); | ||
| 309 | + } else if (Intent.ACTION_TIME_TICK.equals(action)) {//日期变化步数重置为0 | ||
| 310 | +// isCall(); | ||
| 311 | +// Logger.d("重置步数" + StepDcretor.CURRENT_STEP); | ||
| 312 | + save(); | ||
| 313 | + isNewDay(); | ||
| 314 | + } | ||
| 315 | + } | ||
| 316 | + }; | ||
| 317 | + registerReceiver(mBatInfoReceiver, filter); | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + /** | ||
| 321 | + * 获取当前步数 | ||
| 322 | + * | ||
| 323 | + * @return | ||
| 324 | + */ | ||
| 325 | + public int getStepCount() { | ||
| 326 | + return CURRENT_STEP; | ||
| 327 | + } | ||
| 328 | + | ||
| 329 | + /** | ||
| 330 | + * 监听晚上0点变化初始化数据 | ||
| 331 | + */ | ||
| 332 | + private void isNewDay() { | ||
| 333 | + String time = "00:00"; | ||
| 334 | + if (time.equals(new SimpleDateFormat("HH:mm").format(new Date())) || !CURRENT_DATE.equals(getTodayDate())) { | ||
| 335 | + initTodayData(userId); | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | +// /** | ||
| 340 | +// * 监听时间变化提醒用户锻炼 | ||
| 341 | +// */ | ||
| 342 | +// private void isCall() { | ||
| 343 | +// String time = this.getSharedPreferences("share_date", Context.MODE_MULTI_PROCESS).getString("achieveTime", "21:00"); | ||
| 344 | +// String plan = this.getSharedPreferences("share_date", Context.MODE_MULTI_PROCESS).getString("planWalk_QTY", "7000"); | ||
| 345 | +// String remind = this.getSharedPreferences("share_date", Context.MODE_MULTI_PROCESS).getString("remind", "1"); | ||
| 346 | +// Logger.d("time=" + time + "\n" + | ||
| 347 | +// "new SimpleDateFormat(\"HH: mm\").format(new Date()))=" + new SimpleDateFormat("HH:mm").format(new Date())); | ||
| 348 | +// if (("1".equals(remind)) && | ||
| 349 | +// (CURRENT_STEP < Integer.parseInt(plan)) && | ||
| 350 | +// (time.equals(new SimpleDateFormat("HH:mm").format(new Date()))) | ||
| 351 | +// ) { | ||
| 352 | +// remindNotify(); | ||
| 353 | +// } | ||
| 354 | +// | ||
| 355 | +// } | ||
| 356 | + /** | ||
| 357 | + * 提醒锻炼通知栏 | ||
| 358 | + */ | ||
| 359 | +// private void remindNotify() { | ||
| 360 | +// | ||
| 361 | +// //设置点击跳转 | ||
| 362 | +// Intent hangIntent = new Intent(this, MainActivity.class); | ||
| 363 | +// PendingIntent hangPendingIntent = PendingIntent.getActivity(this, 0, hangIntent, PendingIntent.FLAG_CANCEL_CURRENT); | ||
| 364 | +// | ||
| 365 | +// String plan = this.getSharedPreferences("share_date", Context.MODE_MULTI_PROCESS).getString("planWalk_QTY", "7000"); | ||
| 366 | +// NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this); | ||
| 367 | +// mBuilder.setContentTitle("今日步数" + CURRENT_STEP + " 步") | ||
| 368 | +// .setContentText("距离目标还差" + (Integer.valueOf(plan) - CURRENT_STEP) + "步,加油!") | ||
| 369 | +// .setContentIntent(hangPendingIntent) | ||
| 370 | +// .setTicker(getResources().getString(R.string.app_name) + "提醒您开始锻炼了")//通知首次出现在通知栏,带上升动画效果的 | ||
| 371 | +// .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示 | ||
| 372 | +// .setPriority(Notification.PRIORITY_DEFAULT)//设置该通知优先级 | ||
| 373 | +// .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消 | ||
| 374 | +// .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接) | ||
| 375 | +// .setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合: | ||
| 376 | +// //Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission | ||
| 377 | +// .setSmallIcon(R.mipmap.logo); | ||
| 378 | +// NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); | ||
| 379 | +// mNotificationManager.notify(notify_remind_id, mBuilder.build()); | ||
| 380 | +// } | ||
| 381 | + | ||
| 382 | + /** | ||
| 383 | + * 获取传感器实例 | ||
| 384 | + */ | ||
| 385 | + private void startStepDetector() { | ||
| 386 | + if (sensorManager != null) { | ||
| 387 | + sensorManager = null; | ||
| 388 | + } | ||
| 389 | + // 获取传感器管理器的实例 | ||
| 390 | + sensorManager = (SensorManager) this | ||
| 391 | + .getSystemService(SENSOR_SERVICE); | ||
| 392 | + //android4.4以后可以使用计步传感器 | ||
| 393 | + int VERSION_CODES = Build.VERSION.SDK_INT; | ||
| 394 | + if (VERSION_CODES >= 19) { | ||
| 395 | + addCountStepListener(); | ||
| 396 | + } else { | ||
| 397 | + addBasePedometerListener(); | ||
| 398 | + } | ||
| 399 | + } | ||
| 400 | + | ||
| 401 | + /** | ||
| 402 | + * 添加传感器监听 | ||
| 403 | + * 1. TYPE_STEP_COUNTER API的解释说返回从开机被激活后统计的步数,当重启手机后该数据归零, | ||
| 404 | + * 该传感器是一个硬件传感器所以它是低功耗的。 | ||
| 405 | + * 为了能持续的计步,请不要反注册事件,就算手机处于休眠状态它依然会计步。 | ||
| 406 | + * 当激活的时候依然会上报步数。该sensor适合在长时间的计步需求。 | ||
| 407 | + * <p> | ||
| 408 | + * 2.TYPE_STEP_DETECTOR翻译过来就是走路检测, | ||
| 409 | + * API文档也确实是这样说的,该sensor只用来监监测走步,每次返回数字1.0。 | ||
| 410 | + * 如果需要长事件的计步请使用TYPE_STEP_COUNTER。 | ||
| 411 | + */ | ||
| 412 | + private void addCountStepListener() { | ||
| 413 | + Sensor countSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER); | ||
| 414 | + Sensor detectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR); | ||
| 415 | + if (countSensor != null) { | ||
| 416 | + stepSensorType = Sensor.TYPE_STEP_COUNTER; | ||
| 417 | + Log.v(TAG, "Sensor.TYPE_STEP_COUNTER"); | ||
| 418 | + sensorManager.registerListener(StepService.this, countSensor, SensorManager.SENSOR_DELAY_NORMAL); | ||
| 419 | + } else if (detectorSensor != null) { | ||
| 420 | + stepSensorType = Sensor.TYPE_STEP_DETECTOR; | ||
| 421 | + Log.v(TAG, "Sensor.TYPE_STEP_DETECTOR"); | ||
| 422 | + sensorManager.registerListener(StepService.this, detectorSensor, SensorManager.SENSOR_DELAY_NORMAL); | ||
| 423 | + } else { | ||
| 424 | + Log.v(TAG, "Count sensor not available!"); | ||
| 425 | + addBasePedometerListener(); | ||
| 426 | + } | ||
| 427 | + } | ||
| 428 | + | ||
| 429 | + /** | ||
| 430 | + * 通过加速度传感器来记步 | ||
| 431 | + */ | ||
| 432 | + private void addBasePedometerListener() { | ||
| 433 | + mStepCount = new StepCount(); | ||
| 434 | + mStepCount.setSteps(CURRENT_STEP); | ||
| 435 | + // 获得传感器的类型,这里获得的类型是加速度传感器 | ||
| 436 | + // 此方法用来注册,只有注册过才会生效,参数:SensorEventListener的实例,Sensor的实例,更新速率 | ||
| 437 | + Sensor sensor = sensorManager | ||
| 438 | + .getDefaultSensor(Sensor.TYPE_ACCELEROMETER); | ||
| 439 | + boolean isAvailable = sensorManager.registerListener(mStepCount.getStepDetector(), sensor, | ||
| 440 | + SensorManager.SENSOR_DELAY_UI); | ||
| 441 | + mStepCount.initListener(new StepValuePassListener() { | ||
| 442 | + @Override | ||
| 443 | + public void stepChanged(int steps) { | ||
| 444 | + CURRENT_STEP = steps; | ||
| 445 | + updateNotification(); | ||
| 446 | + } | ||
| 447 | + }); | ||
| 448 | + if (isAvailable) { | ||
| 449 | + Log.v(TAG, "加速度传感器可以使用"); | ||
| 450 | + } else { | ||
| 451 | + Log.v(TAG, "加速度传感器无法使用"); | ||
| 452 | + } | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + /** | ||
| 456 | + * 传感器监听回调 | ||
| 457 | + * 记步的关键代码 | ||
| 458 | + * 1. TYPE_STEP_COUNTER API的解释说返回从开机被激活后统计的步数,当重启手机后该数据归零, | ||
| 459 | + * 该传感器是一个硬件传感器所以它是低功耗的。 | ||
| 460 | + * 为了能持续的计步,请不要反注册事件,就算手机处于休眠状态它依然会计步。 | ||
| 461 | + * 当激活的时候依然会上报步数。该sensor适合在长时间的计步需求。 | ||
| 462 | + * <p> | ||
| 463 | + * 2.TYPE_STEP_DETECTOR翻译过来就是走路检测, | ||
| 464 | + * API文档也确实是这样说的,该sensor只用来监监测走步,每次返回数字1.0。 | ||
| 465 | + * 如果需要长事件的计步请使用TYPE_STEP_COUNTER。 | ||
| 466 | + * | ||
| 467 | + * @param event | ||
| 468 | + */ | ||
| 469 | + @Override | ||
| 470 | + public void onSensorChanged(SensorEvent event) { | ||
| 471 | + if (stepSensorType == Sensor.TYPE_STEP_COUNTER) { | ||
| 472 | + //获取当前传感器返回的临时步数 | ||
| 473 | + int tempStep = (int) event.values[0]; | ||
| 474 | + //首次如果没有获取手机系统中已有的步数则获取一次系统中APP还未开始记步的步数 | ||
| 475 | + if (!hasRecord) { | ||
| 476 | + hasRecord = true; | ||
| 477 | + hasStepCount = tempStep; | ||
| 478 | + } else { | ||
| 479 | + //获取APP打开到现在的总步数=本次系统回调的总步数-APP打开之前已有的步数 | ||
| 480 | + int thisStepCount = tempStep - hasStepCount; | ||
| 481 | + //本次有效步数=(APP打开后所记录的总步数-上一次APP打开后所记录的总步数) | ||
| 482 | + int thisStep = thisStepCount - previousStepCount; | ||
| 483 | + //总步数=现有的步数+本次有效步数 | ||
| 484 | + CURRENT_STEP += (thisStep); | ||
| 485 | + //记录最后一次APP打开到现在的总步数 | ||
| 486 | + previousStepCount = thisStepCount; | ||
| 487 | + } | ||
| 488 | + Logger.d("tempStep" + tempStep); | ||
| 489 | + } else if (stepSensorType == Sensor.TYPE_STEP_DETECTOR) { | ||
| 490 | + if (event.values[0] == 1.0) { | ||
| 491 | + CURRENT_STEP++; | ||
| 492 | + } | ||
| 493 | + } | ||
| 494 | + updateNotification(); | ||
| 495 | + } | ||
| 496 | + | ||
| 497 | + /** | ||
| 498 | + * 保存记步数据 | ||
| 499 | + */ | ||
| 500 | + private void save() { | ||
| 501 | + int tempStep = CURRENT_STEP; | ||
| 502 | + | ||
| 503 | + List<UserStepEntity> list = DbCore.getDaoSession().getUserStepEntityDao().queryBuilder().where(UserStepEntityDao.Properties.UserId.eq(userId), UserStepEntityDao.Properties.Date.eq(CURRENT_DATE)).list(); | ||
| 504 | + if (list.size() == 0 || list.isEmpty()) { | ||
| 505 | + UserStepEntity data = new UserStepEntity(); | ||
| 506 | + data.setUuid(UUID.randomUUID().toString()); | ||
| 507 | + data.setUserId(userId); | ||
| 508 | + data.setDate(CURRENT_DATE); | ||
| 509 | + data.setStepCount(tempStep + ""); | ||
| 510 | + DbCore.getDaoSession().getUserStepEntityDao().insert(data); | ||
| 511 | + } else if (list.size() == 1) { | ||
| 512 | + UserStepEntity data = list.get(0); | ||
| 513 | + data.setStepCount(tempStep + ""); | ||
| 514 | + DbCore.getDaoSession().getUserStepEntityDao().update(data); | ||
| 515 | + } else { | ||
| 516 | + } | ||
| 517 | + } | ||
| 518 | + | ||
| 519 | + /** | ||
| 520 | + * 保存记步数据 | ||
| 521 | + */ | ||
| 522 | + class TimeCount extends CountDownTimer { | ||
| 523 | + public TimeCount(long millisInFuture, long countDownInterval) { | ||
| 524 | + super(millisInFuture, countDownInterval); | ||
| 525 | + } | ||
| 526 | + | ||
| 527 | + @Override | ||
| 528 | + public void onFinish() { | ||
| 529 | + // 如果计时器正常结束,则开始计步 | ||
| 530 | + time.cancel(); | ||
| 531 | + save(); | ||
| 532 | + startTimeCount(); | ||
| 533 | + } | ||
| 534 | + | ||
| 535 | + @Override | ||
| 536 | + public void onTick(long millisUntilFinished) { | ||
| 537 | + | ||
| 538 | + } | ||
| 539 | + | ||
| 540 | + } | ||
| 541 | + | ||
| 542 | + /** | ||
| 543 | + * 开始保存记步数据 | ||
| 544 | + */ | ||
| 545 | + private void startTimeCount() { | ||
| 546 | + if (time == null) { | ||
| 547 | + time = new TimeCount(duration, 1000); | ||
| 548 | + } | ||
| 549 | + time.start(); | ||
| 550 | + } | ||
| 551 | + | ||
| 552 | + /** | ||
| 553 | + * 向Activity传递数据的纽带 | ||
| 554 | + */ | ||
| 555 | + public class StepBinder extends Binder { | ||
| 556 | + | ||
| 557 | + /** | ||
| 558 | + * 获取当前service对象 | ||
| 559 | + * | ||
| 560 | + * @return StepService | ||
| 561 | + */ | ||
| 562 | + public StepService getService() { | ||
| 563 | + return StepService.this; | ||
| 564 | + } | ||
| 565 | + } | ||
| 566 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/sql/db/BaseDbHelper.java
0 → 100644
| 1 | +/* | ||
| 2 | +******************************* Copyright (c)*********************************\ | ||
| 3 | +** | ||
| 4 | +** (c) Copyright 2015, 蒋朋, china, qd. sd | ||
| 5 | +** All Rights Reserved | ||
| 6 | +** | ||
| 7 | +** By() | ||
| 8 | +** | ||
| 9 | +**-----------------------------------版本信息------------------------------------ | ||
| 10 | +** 版 本: V0.1 | ||
| 11 | +** | ||
| 12 | +**------------------------------------------------------------------------------ | ||
| 13 | +********************************End of Head************************************\ | ||
| 14 | +*/ | ||
| 15 | + | ||
| 16 | +package com.cnlive.moudle.pedometer.sql.db; | ||
| 17 | + | ||
| 18 | + | ||
| 19 | +import org.greenrobot.greendao.AbstractDao; | ||
| 20 | +import org.greenrobot.greendao.query.QueryBuilder; | ||
| 21 | + | ||
| 22 | +import java.util.List; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * 文 件 名: BaseDbHelper | ||
| 26 | + * 说 明: greedDAO 基础辅助类 | ||
| 27 | + * 创 建 人: 蒋朋 | ||
| 28 | + * 创建日期: 16-7-19 10:19 | ||
| 29 | + * 邮 箱: jp19891017@gmail.com | ||
| 30 | + * 博 客: http://jp1017.github.io | ||
| 31 | + * 修改时间: | ||
| 32 | + * 修改备注: | ||
| 33 | + */ | ||
| 34 | +public class BaseDbHelper<T, K> { | ||
| 35 | + private AbstractDao<T, K> mDao; | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + public BaseDbHelper(AbstractDao dao) { | ||
| 39 | + mDao = dao; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + | ||
| 43 | + public void save(T item) { | ||
| 44 | + mDao.insert(item); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + public void save(T... items) { | ||
| 48 | + mDao.insertInTx(items); | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + public void save(List<T> items) { | ||
| 52 | + mDao.insertInTx(items); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + public void saveOrUpdate(T item) { | ||
| 56 | + mDao.insertOrReplace(item); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public void saveOrUpdate(T... items) { | ||
| 60 | + mDao.insertOrReplaceInTx(items); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + public void saveOrUpdate(List<T> items) { | ||
| 64 | + mDao.insertOrReplaceInTx(items); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + public void deleteByKey(K key) { | ||
| 68 | + mDao.deleteByKey(key); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + public void delete(T item) { | ||
| 72 | + mDao.delete(item); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + public void delete(T... items) { | ||
| 76 | + mDao.deleteInTx(items); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public void delete(List<T> items) { | ||
| 80 | + mDao.deleteInTx(items); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + public void deleteAll() { | ||
| 84 | + mDao.deleteAll(); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + | ||
| 88 | + public void update(T item) { | ||
| 89 | + mDao.update(item); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + public void update(T... items) { | ||
| 93 | + mDao.updateInTx(items); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + public void update(List<T> items) { | ||
| 97 | + mDao.updateInTx(items); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + public T query(K key) { | ||
| 101 | + return mDao.load(key); | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + public List<T> queryAll() { | ||
| 105 | + return mDao.loadAll(); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + public List<T> query(String where, String... params) { | ||
| 109 | + | ||
| 110 | + return mDao.queryRaw(where, params); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + public QueryBuilder<T> queryBuilder() { | ||
| 114 | + | ||
| 115 | + return mDao.queryBuilder(); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + public long count() { | ||
| 119 | + return mDao.count(); | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + public void refresh(T item) { | ||
| 123 | + mDao.refresh(item); | ||
| 124 | + | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + public void detach(T item) { | ||
| 128 | + mDao.detach(item); | ||
| 129 | + } | ||
| 130 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/sql/db/DBMigrationHelper.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.sql.db; | ||
| 2 | + | ||
| 3 | +import android.database.Cursor; | ||
| 4 | +import android.database.SQLException; | ||
| 5 | +import android.database.sqlite.SQLiteDatabase; | ||
| 6 | +import android.support.annotation.NonNull; | ||
| 7 | +import android.text.TextUtils; | ||
| 8 | +import android.util.Log; | ||
| 9 | + | ||
| 10 | +import org.greenrobot.greendao.AbstractDao; | ||
| 11 | +import org.greenrobot.greendao.database.Database; | ||
| 12 | +import org.greenrobot.greendao.database.StandardDatabase; | ||
| 13 | +import org.greenrobot.greendao.internal.DaoConfig; | ||
| 14 | + | ||
| 15 | +import java.lang.reflect.InvocationTargetException; | ||
| 16 | +import java.lang.reflect.Method; | ||
| 17 | +import java.util.ArrayList; | ||
| 18 | +import java.util.Arrays; | ||
| 19 | +import java.util.List; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * greenDao升级类 | ||
| 23 | + */ | ||
| 24 | +public class DBMigrationHelper { | ||
| 25 | + public static boolean DEBUG = false; | ||
| 26 | + private static String TAG = "MigrationHelper2"; | ||
| 27 | + private static final String SQLITE_MASTER = "sqlite_master"; | ||
| 28 | + private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master"; | ||
| 29 | + | ||
| 30 | + public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 31 | + printLog("【The Old Database Version】" + db.getVersion()); | ||
| 32 | + Database database = new StandardDatabase(db); | ||
| 33 | + migrate(database, daoClasses); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 37 | + printLog("【Generate temp table】start"); | ||
| 38 | + generateTempTables(database, daoClasses); | ||
| 39 | + printLog("【Generate temp table】complete"); | ||
| 40 | + | ||
| 41 | + dropAllTables(database, true, daoClasses); | ||
| 42 | + createAllTables(database, false, daoClasses); | ||
| 43 | + | ||
| 44 | + printLog("【Restore data】start"); | ||
| 45 | + restoreData(database, daoClasses); | ||
| 46 | + printLog("【Restore data】complete"); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 50 | + for (int i = 0; i < daoClasses.length; i++) { | ||
| 51 | + String tempTableName = null; | ||
| 52 | + | ||
| 53 | + DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]); | ||
| 54 | + String tableName = daoConfig.tablename; | ||
| 55 | + if (!isTableExists(db, false, tableName)) { | ||
| 56 | + printLog("【New Table】" + tableName); | ||
| 57 | + continue; | ||
| 58 | + } | ||
| 59 | + try { | ||
| 60 | + tempTableName = daoConfig.tablename.concat("_TEMP"); | ||
| 61 | + StringBuilder dropTableStringBuilder = new StringBuilder(); | ||
| 62 | + dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";"); | ||
| 63 | + db.execSQL(dropTableStringBuilder.toString()); | ||
| 64 | + | ||
| 65 | + StringBuilder insertTableStringBuilder = new StringBuilder(); | ||
| 66 | + insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName); | ||
| 67 | + insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";"); | ||
| 68 | + db.execSQL(insertTableStringBuilder.toString()); | ||
| 69 | + printLog("【Table】" + tableName + "\n ---Columns-->" + getColumnsStr(daoConfig)); | ||
| 70 | + printLog("【Generate temp table】" + tempTableName); | ||
| 71 | + } catch (SQLException e) { | ||
| 72 | + Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e); | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + private static boolean isTableExists(Database db, boolean isTemp, String tableName) { | ||
| 78 | + if (db == null || TextUtils.isEmpty(tableName)) { | ||
| 79 | + return false; | ||
| 80 | + } | ||
| 81 | + String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER; | ||
| 82 | + String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?"; | ||
| 83 | + Cursor cursor = null; | ||
| 84 | + int count = 0; | ||
| 85 | + try { | ||
| 86 | + cursor = db.rawQuery(sql, new String[]{"table", tableName}); | ||
| 87 | + if (cursor == null || !cursor.moveToFirst()) { | ||
| 88 | + return false; | ||
| 89 | + } | ||
| 90 | + count = cursor.getInt(0); | ||
| 91 | + } catch (Exception e) { | ||
| 92 | + e.printStackTrace(); | ||
| 93 | + } finally { | ||
| 94 | + if (cursor != null) { | ||
| 95 | + cursor.close(); | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | + return count > 0; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + | ||
| 102 | + private static String getColumnsStr(DaoConfig daoConfig) { | ||
| 103 | + if (daoConfig == null) { | ||
| 104 | + return "no columns"; | ||
| 105 | + } | ||
| 106 | + StringBuilder builder = new StringBuilder(); | ||
| 107 | + for (int i = 0; i < daoConfig.allColumns.length; i++) { | ||
| 108 | + builder.append(daoConfig.allColumns[i]); | ||
| 109 | + builder.append(","); | ||
| 110 | + } | ||
| 111 | + if (builder.length() > 0) { | ||
| 112 | + builder.deleteCharAt(builder.length() - 1); | ||
| 113 | + } | ||
| 114 | + return builder.toString(); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + | ||
| 118 | + private static void dropAllTables(Database db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 119 | + reflectMethod(db, "dropTable", ifExists, daoClasses); | ||
| 120 | + printLog("【Drop all table】"); | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 124 | + reflectMethod(db, "createTable", ifNotExists, daoClasses); | ||
| 125 | + printLog("【Create all table】"); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + /** | ||
| 129 | + * dao class already define the sql exec method, so just invoke it | ||
| 130 | + */ | ||
| 131 | + private static void reflectMethod(Database db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 132 | + if (daoClasses.length < 1) { | ||
| 133 | + return; | ||
| 134 | + } | ||
| 135 | + try { | ||
| 136 | + for (Class cls : daoClasses) { | ||
| 137 | + Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class); | ||
| 138 | + method.invoke(null, db, isExists); | ||
| 139 | + } | ||
| 140 | + } catch (NoSuchMethodException e) { | ||
| 141 | + e.printStackTrace(); | ||
| 142 | + } catch (InvocationTargetException e) { | ||
| 143 | + e.printStackTrace(); | ||
| 144 | + } catch (IllegalAccessException e) { | ||
| 145 | + e.printStackTrace(); | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + private static void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) { | ||
| 150 | + for (int i = 0; i < daoClasses.length; i++) { | ||
| 151 | + DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]); | ||
| 152 | + String tableName = daoConfig.tablename; | ||
| 153 | + String tempTableName = daoConfig.tablename.concat("_TEMP"); | ||
| 154 | + | ||
| 155 | + if (!isTableExists(db, true, tempTableName)) { | ||
| 156 | + continue; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + try { | ||
| 160 | + // get all columns from tempTable, take careful to use the columns list | ||
| 161 | + List<String> columns = getColumns(db, tempTableName); | ||
| 162 | + ArrayList<String> properties = new ArrayList<>(columns.size()); | ||
| 163 | + for (int j = 0; j < daoConfig.properties.length; j++) { | ||
| 164 | + String columnName = daoConfig.properties[j].columnName; | ||
| 165 | + if (columns.contains(columnName)) { | ||
| 166 | + properties.add(columnName); | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + if (properties.size() > 0) { | ||
| 170 | + final String columnSQL = TextUtils.join(",", properties); | ||
| 171 | + | ||
| 172 | + StringBuilder insertTableStringBuilder = new StringBuilder(); | ||
| 173 | + insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" ("); | ||
| 174 | + insertTableStringBuilder.append(columnSQL); | ||
| 175 | + insertTableStringBuilder.append(") SELECT "); | ||
| 176 | + insertTableStringBuilder.append(columnSQL); | ||
| 177 | + insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";"); | ||
| 178 | + db.execSQL(insertTableStringBuilder.toString()); | ||
| 179 | + printLog("【Restore data】 to " + tableName); | ||
| 180 | + } | ||
| 181 | + StringBuilder dropTableStringBuilder = new StringBuilder(); | ||
| 182 | + dropTableStringBuilder.append("DROP TABLE ").append(tempTableName); | ||
| 183 | + db.execSQL(dropTableStringBuilder.toString()); | ||
| 184 | + printLog("【Drop temp table】" + tempTableName); | ||
| 185 | + } catch (SQLException e) { | ||
| 186 | + Log.e(TAG, "【Failed to restore data from temp table 】" + tempTableName, e); | ||
| 187 | + } | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + private static List<String> getColumns(Database db, String tableName) { | ||
| 192 | + List<String> columns = null; | ||
| 193 | + Cursor cursor = null; | ||
| 194 | + try { | ||
| 195 | + cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null); | ||
| 196 | + if (null != cursor && cursor.getColumnCount() > 0) { | ||
| 197 | + columns = Arrays.asList(cursor.getColumnNames()); | ||
| 198 | + } | ||
| 199 | + } catch (Exception e) { | ||
| 200 | + e.printStackTrace(); | ||
| 201 | + } finally { | ||
| 202 | + if (cursor != null) { | ||
| 203 | + cursor.close(); | ||
| 204 | + } | ||
| 205 | + if (null == columns) { | ||
| 206 | + columns = new ArrayList<>(); | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + return columns; | ||
| 210 | + } | ||
| 211 | + | ||
| 212 | + private static void printLog(String info) { | ||
| 213 | + if (DEBUG) { | ||
| 214 | + Log.d(TAG, info); | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + | ||
| 219 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/sql/db/DbCore.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.sql.db; | ||
| 2 | + | ||
| 3 | +import android.content.Context; | ||
| 4 | +import android.text.TextUtils; | ||
| 5 | + | ||
| 6 | + | ||
| 7 | +import com.cnlive.moudle.pedometer.sql.dao.DaoMaster; | ||
| 8 | +import com.cnlive.moudle.pedometer.sql.dao.DaoSession; | ||
| 9 | + | ||
| 10 | +import org.greenrobot.greendao.query.QueryBuilder; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 文 件 名: DbCore | ||
| 14 | + * 说 明: 核心辅助类,用于获取DaoMaster和DaoSession | ||
| 15 | + * 参 考:http://blog.inet198.cn/?sbsujjbcy/article/details/48156683 | ||
| 16 | + * 创 建 人: 蒋朋 | ||
| 17 | + * 创建日期: 16-7-19 10:12 | ||
| 18 | + * 邮 箱: jp19891017@gmail.com | ||
| 19 | + * 博 客: http://jp1017.github.io | ||
| 20 | + * 修改时间: | ||
| 21 | + * 修改备注: | ||
| 22 | + */ | ||
| 23 | +public class DbCore { | ||
| 24 | + private static final String DEFAULT_DB_NAME = "-strike-sp-db"; | ||
| 25 | + private static DaoMaster daoMaster; | ||
| 26 | + private static DaoSession daoSession; | ||
| 27 | + | ||
| 28 | + private static Context mContext; | ||
| 29 | + private static String DB_NAME; | ||
| 30 | + private static char[] databasePassword = "com.cnlive.strike_Passw0rd".toCharArray(); | ||
| 31 | + | ||
| 32 | + public static void init(Context context) { | ||
| 33 | + init(context, DEFAULT_DB_NAME); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + public static void init(Context context, String dbName) { | ||
| 37 | + if (context == null) { | ||
| 38 | + throw new IllegalArgumentException("context can't be null"); | ||
| 39 | + } | ||
| 40 | + if (TextUtils.isEmpty(dbName)) { | ||
| 41 | + DB_NAME = DEFAULT_DB_NAME; | ||
| 42 | + } else { | ||
| 43 | + DB_NAME = dbName; | ||
| 44 | + } | ||
| 45 | + mContext = context.getApplicationContext(); | ||
| 46 | + String pathDatabase = mContext.getDatabasePath(DB_NAME).getAbsolutePath(); | ||
| 47 | + | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + public static DaoMaster getDaoMaster() { | ||
| 51 | + if (daoMaster == null) { | ||
| 52 | + //此处不可用 DaoMaster.DevOpenHelper, 那是开发辅助类,我们要自定义一个,方便升级 | ||
| 53 | + DaoMaster.OpenHelper helper = new MyOpenHelper(mContext, DB_NAME); | ||
| 54 | + daoMaster = new DaoMaster(helper.getWritableDb()); | ||
| 55 | + } | ||
| 56 | + return daoMaster; | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + public static DaoSession getDaoSession() { | ||
| 60 | + if (daoSession == null) { | ||
| 61 | + if (daoMaster == null) { | ||
| 62 | + daoMaster = getDaoMaster(); | ||
| 63 | + } | ||
| 64 | + daoSession = daoMaster.newSession(); | ||
| 65 | + } | ||
| 66 | + return daoSession; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + public static void enableQueryBuilderLog() { | ||
| 70 | + QueryBuilder.LOG_SQL = true; | ||
| 71 | + QueryBuilder.LOG_VALUES = true; | ||
| 72 | + } | ||
| 73 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/sql/db/MyOpenHelper.java
0 → 100644
| 1 | +/* | ||
| 2 | + ******************* Copyright (c) ***********************\ | ||
| 3 | + ** | ||
| 4 | + ** (c) Copyright 2016, 蒋朋, china, sxkj. sd | ||
| 5 | + ** All Rights Reserved | ||
| 6 | + ** | ||
| 7 | + ** By(青岛世新科技有限公司) | ||
| 8 | + ** www.qdsxkj.com | ||
| 9 | + ** | ||
| 10 | + ** _oo0oo_ | ||
| 11 | + ** o8888888o | ||
| 12 | + ** 88" . "88 | ||
| 13 | + ** (| -_- |) | ||
| 14 | + ** 0\ = /0 | ||
| 15 | + ** ___/`---'\___ | ||
| 16 | + ** .' \\| |// '. | ||
| 17 | + ** / \\||| : |||// \ | ||
| 18 | + ** / _||||| -:- |||||- \ | ||
| 19 | + ** | | \\\ - /// | | | ||
| 20 | + ** | \_| ''\---/'' |_/ | | ||
| 21 | + ** \ .-\__ '-' ___/-. / | ||
| 22 | + ** ___'. .' /--.--\ `. .'___ | ||
| 23 | + ** ."" '< `.___\_<|>_/___.' >' "". | ||
| 24 | + ** | | : `- \`.;`\ _ /`;.`/ - ` : | | | ||
| 25 | + ** \ \ `_. \_ __\ /__ _/ .-` / / | ||
| 26 | + ** =====`-.____`.___ \_____/___.-`___.-'===== | ||
| 27 | + ** `=---=' | ||
| 28 | + ** | ||
| 29 | + ** | ||
| 30 | + ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 31 | + ** | ||
| 32 | + ** 佛祖保佑 永无BUG | ||
| 33 | + ** | ||
| 34 | + ** | ||
| 35 | + ** 南无本师释迦牟尼佛 | ||
| 36 | + ** | ||
| 37 | + | ||
| 38 | + **----------------------版本信息------------------------ | ||
| 39 | + ** 版 本: V0.1 | ||
| 40 | + ** | ||
| 41 | + ******************* End of Head **********************\ | ||
| 42 | + */ | ||
| 43 | + | ||
| 44 | +package com.cnlive.moudle.pedometer.sql.db; | ||
| 45 | + | ||
| 46 | +import android.content.Context; | ||
| 47 | + | ||
| 48 | +import com.cnlive.moudle.pedometer.sql.dao.DaoMaster; | ||
| 49 | +import com.cnlive.moudle.pedometer.sql.dao.UserStepEntityDao; | ||
| 50 | +import com.cnlive.moudle.pedometer.sql.entity.UserStepEntity; | ||
| 51 | + | ||
| 52 | +import org.greenrobot.greendao.database.Database; | ||
| 53 | + | ||
| 54 | +/** | ||
| 55 | + * 文 件 名: MyOpenHelper | ||
| 56 | + * 创 建 人: 蒋朋 | ||
| 57 | + * 创建日期: 16-10-11 08:28 | ||
| 58 | + * 邮 箱: jp19891017@gmail.com | ||
| 59 | + * 博 客: https://jp1017.github.io/ | ||
| 60 | + * 描 述: | ||
| 61 | + * 修 改 人: | ||
| 62 | + * 修改时间: | ||
| 63 | + * 修改备注: | ||
| 64 | + */ | ||
| 65 | + | ||
| 66 | +public class MyOpenHelper extends DaoMaster.OpenHelper { | ||
| 67 | + public MyOpenHelper(Context context, String name) { | ||
| 68 | + super(context, name); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + @Override | ||
| 72 | + public void onUpgrade(Database db, int oldVersion, int newVersion) { | ||
| 73 | + try { | ||
| 74 | + //判断版本, 设置需要修改得表 我这边设置一个 FileInfo | ||
| 75 | + DBMigrationHelper.migrate(db, UserStepEntityDao.class); | ||
| 76 | + } catch (ClassCastException e) { | ||
| 77 | + } | ||
| 78 | +// KLog.w("db version update from " + oldVersion + " to " + newVersion); | ||
| 79 | +// MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() { | ||
| 80 | +// | ||
| 81 | +// @Override | ||
| 82 | +// public void onCreateAllTables(Database db, boolean ifNotExists) { | ||
| 83 | +// DaoMaster.createAllTables(db, ifNotExists); | ||
| 84 | +// } | ||
| 85 | +// | ||
| 86 | +// @Override | ||
| 87 | +// public void onDropAllTables(Database db, boolean ifExists) { | ||
| 88 | +// DaoMaster.dropAllTables(db, ifExists); | ||
| 89 | +// } | ||
| 90 | +// },G106InfoDao.class);//, GovernanceTechnologyDao.class, TestData3Dao.class | ||
| 91 | +// } | ||
| 92 | + switch (oldVersion) { | ||
| 93 | + case 1: | ||
| 94 | + | ||
| 95 | + //不能先删除表,否则数据都木了 | ||
| 96 | +// StudentDao.dropTable(db, true); | ||
| 97 | + | ||
| 98 | +// StudentDao.createTable(db, true); | ||
| 99 | + | ||
| 100 | + // 加入新字段 score | ||
| 101 | +// db.execSQL("ALTER TABLE 'STUDENT' ADD 'SCORE' TEXT;"); | ||
| 102 | + | ||
| 103 | + break; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + } | ||
| 107 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/sql/entity/UserStepEntity.java
| @@ -2,37 +2,60 @@ package com.cnlive.moudle.pedometer.sql.entity; | @@ -2,37 +2,60 @@ package com.cnlive.moudle.pedometer.sql.entity; | ||
| 2 | 2 | ||
| 3 | import org.greenrobot.greendao.annotation.Entity; | 3 | import org.greenrobot.greendao.annotation.Entity; |
| 4 | import org.greenrobot.greendao.annotation.Generated; | 4 | import org.greenrobot.greendao.annotation.Generated; |
| 5 | +import org.greenrobot.greendao.annotation.Id; | ||
| 5 | 6 | ||
| 6 | @Entity | 7 | @Entity |
| 7 | public class UserStepEntity { | 8 | public class UserStepEntity { |
| 9 | + @Id | ||
| 10 | + private String uuid; | ||
| 8 | private String userId; | 11 | private String userId; |
| 9 | private String date; | 12 | private String date; |
| 10 | private String stepCount; | 13 | private String stepCount; |
| 11 | - @Generated(hash = 1638365954) | ||
| 12 | - public UserStepEntity(String userId, String date, String stepCount) { | 14 | + |
| 15 | + @Generated(hash = 1772281523) | ||
| 16 | + public UserStepEntity(String uuid, String userId, String date, | ||
| 17 | + String stepCount) { | ||
| 18 | + this.uuid = uuid; | ||
| 13 | this.userId = userId; | 19 | this.userId = userId; |
| 14 | this.date = date; | 20 | this.date = date; |
| 15 | this.stepCount = stepCount; | 21 | this.stepCount = stepCount; |
| 16 | } | 22 | } |
| 23 | + | ||
| 17 | @Generated(hash = 273857141) | 24 | @Generated(hash = 273857141) |
| 18 | public UserStepEntity() { | 25 | public UserStepEntity() { |
| 19 | } | 26 | } |
| 27 | + | ||
| 20 | public String getUserId() { | 28 | public String getUserId() { |
| 21 | return this.userId; | 29 | return this.userId; |
| 22 | } | 30 | } |
| 31 | + | ||
| 23 | public void setUserId(String userId) { | 32 | public void setUserId(String userId) { |
| 24 | this.userId = userId; | 33 | this.userId = userId; |
| 25 | } | 34 | } |
| 35 | + | ||
| 26 | public String getDate() { | 36 | public String getDate() { |
| 27 | return this.date; | 37 | return this.date; |
| 28 | } | 38 | } |
| 39 | + | ||
| 29 | public void setDate(String date) { | 40 | public void setDate(String date) { |
| 30 | this.date = date; | 41 | this.date = date; |
| 31 | } | 42 | } |
| 43 | + | ||
| 32 | public String getStepCount() { | 44 | public String getStepCount() { |
| 33 | return this.stepCount; | 45 | return this.stepCount; |
| 34 | } | 46 | } |
| 47 | + | ||
| 35 | public void setStepCount(String stepCount) { | 48 | public void setStepCount(String stepCount) { |
| 36 | this.stepCount = stepCount; | 49 | this.stepCount = stepCount; |
| 37 | } | 50 | } |
| 51 | + | ||
| 52 | + public String getUuid() { | ||
| 53 | + return this.uuid; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + public void setUuid(String uuid) { | ||
| 57 | + this.uuid = uuid; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + | ||
| 38 | } | 61 | } |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/ui/activity/PedometerMainActivity.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.ui.activity; | ||
| 2 | + | ||
| 3 | +import android.content.ComponentName; | ||
| 4 | +import android.content.Intent; | ||
| 5 | +import android.content.pm.PackageManager; | ||
| 6 | +import android.net.Uri; | ||
| 7 | +import android.os.Build; | ||
| 8 | +import android.os.Handler; | ||
| 9 | +import android.os.PowerManager; | ||
| 10 | +import android.provider.Settings; | ||
| 11 | +import android.support.v4.app.NotificationManagerCompat; | ||
| 12 | +import android.support.v7.app.AppCompatActivity; | ||
| 13 | +import android.os.Bundle; | ||
| 14 | +import android.util.Log; | ||
| 15 | +import android.widget.Toast; | ||
| 16 | + | ||
| 17 | +import com.alibaba.android.arouter.facade.annotation.Route; | ||
| 18 | +import com.cnlive.libs.base.util.SharedPreferencesHelper; | ||
| 19 | +import com.cnlive.moudle.pedometer.service.KeepLiveService; | ||
| 20 | +import com.cnlive.moudle.pedometer.service.StepService; | ||
| 21 | +import com.cnlive.moudle.pedometer.utils.ServiceUtils; | ||
| 22 | +import com.example.moudle_pedometer.R; | ||
| 23 | + | ||
| 24 | +import static android.app.Notification.EXTRA_CHANNEL_ID; | ||
| 25 | +import static android.provider.Settings.EXTRA_APP_PACKAGE; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * 计步器主activity | ||
| 29 | + */ | ||
| 30 | +@Route(path = "/moudle_pedometer/InterestActivity") | ||
| 31 | +public class PedometerMainActivity extends AppCompatActivity { | ||
| 32 | + private NotificationManagerCompat notificationManagerCompat; | ||
| 33 | + private boolean isOpen; | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + protected void onCreate(Bundle savedInstanceState) { | ||
| 37 | + super.onCreate(savedInstanceState); | ||
| 38 | + setContentView(R.layout.activity_pedometer_main); | ||
| 39 | + SharedPreferencesHelper.getInstance(getApplicationContext()).setValue("isKeepAlive", true); | ||
| 40 | +// new Handler().postDelayed(new Runnable() { | ||
| 41 | +// @Override | ||
| 42 | +// public void run() { | ||
| 43 | +// Log.e("停止服务", "停止"); | ||
| 44 | +// StepService.isKeep = false; | ||
| 45 | +// stopService(new Intent(PedometerMainActivity.this, StepService.class)); | ||
| 46 | +// SharedPreferencesHelper.getInstance(getApplicationContext()).setValue("isKeepAlive", false); | ||
| 47 | +// new Handler().postDelayed(new Runnable() { | ||
| 48 | +// @Override | ||
| 49 | +// public void run() { | ||
| 50 | +// stopService(new Intent(PedometerMainActivity.this, KeepLiveService.class)); | ||
| 51 | +// | ||
| 52 | +// } | ||
| 53 | +// }, 100); | ||
| 54 | +// } | ||
| 55 | +// }, 10000); | ||
| 56 | + | ||
| 57 | +// Intent intent = new Intent(); | ||
| 58 | +// ComponentName componentName = new ComponentName("com.android.settings", "com.android.settings.SubSettings"); | ||
| 59 | +// intent.setComponent(componentName); | ||
| 60 | +// intent.putExtra(EXTRA_APP_PACKAGE, getPackageName()); | ||
| 61 | +// intent.putExtra(EXTRA_CHANNEL_ID, getApplicationInfo().uid); | ||
| 62 | +// startActivity(intent); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + private void check() { | ||
| 66 | + //监测通知权限是否打开 | ||
| 67 | + notificationManagerCompat = NotificationManagerCompat.from(this); | ||
| 68 | + isOpen = notificationManagerCompat.areNotificationsEnabled(); | ||
| 69 | + if (isOpen) { | ||
| 70 | + startService(new Intent(getApplicationContext(), StepService.class)); | ||
| 71 | + } else { | ||
| 72 | + goToSettingNo(); | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + private void goToSettingNo() { | ||
| 77 | + Intent intent1 = new Intent(); | ||
| 78 | + try { | ||
| 79 | + // 根据isOpened结果,判断是否需要提醒用户跳转AppInfo页面,去打开App通知权限 | ||
| 80 | + intent1.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS); | ||
| 81 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
| 82 | + //这种方案适用于 API 26, 即8.0(含8.0)以上可以用 | ||
| 83 | + intent1.putExtra(EXTRA_APP_PACKAGE, getPackageName()); | ||
| 84 | + intent1.putExtra(EXTRA_CHANNEL_ID, getApplicationInfo().uid); | ||
| 85 | + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||
| 86 | + //这种方案适用于 API21——25,即 5.0——7.1 之间的版本可以使用 | ||
| 87 | + intent1.putExtra("app_package", getPackageName()); | ||
| 88 | + intent1.putExtra("app_uid", getApplicationInfo().uid); | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + // 小米6 -MIUI9.6-8.0.0系统,是个特例,通知设置界面只能控制"允许使用通知圆点"——然而这个玩意并没有卵用,我想对雷布斯说:I'm not ok!!! | ||
| 92 | + // if ("MI 6".equals(Build.MODEL)) { | ||
| 93 | + // intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | ||
| 94 | + // Uri uri = Uri.fromParts("package", getPackageName(), null); | ||
| 95 | + // intent.setData(uri); | ||
| 96 | + // // intent.setAction("com.android.settings/.SubSettings"); | ||
| 97 | + // } | ||
| 98 | + startActivity(intent1); | ||
| 99 | + } catch (Exception e) { | ||
| 100 | + //其他低版本或者异常情况,走该节点。进入APP设置界面 | ||
| 101 | + intent1.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | ||
| 102 | + intent1.putExtra("package", getPackageName()); | ||
| 103 | + startActivity(intent1); | ||
| 104 | + } | ||
| 105 | + Toast.makeText(getApplicationContext(), "您尚未开启通知权限,无法进行计步", Toast.LENGTH_LONG).show(); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + @Override | ||
| 109 | + protected void onRestart() { | ||
| 110 | + super.onRestart(); | ||
| 111 | + | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + @Override | ||
| 115 | + protected void onResume() { | ||
| 116 | + super.onResume(); | ||
| 117 | + check(); | ||
| 118 | + PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); | ||
| 119 | + | ||
| 120 | + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { | ||
| 121 | + boolean hasIgnored = powerManager.isIgnoringBatteryOptimizations(getPackageName()); | ||
| 122 | + if (!hasIgnored) { | ||
| 123 | + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); | ||
| 124 | + intent.setData(Uri.parse("package:" + getPackageName())); | ||
| 125 | + PackageManager pm = getPackageManager(); | ||
| 126 | + if (intent.resolveActivity(pm) != null) { | ||
| 127 | + startActivity(intent); | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | +} |
moudle_pedometer/src/main/java/com/cnlive/moudle/pedometer/utils/ServiceUtils.java
0 → 100644
| 1 | +package com.cnlive.moudle.pedometer.utils; | ||
| 2 | + | ||
| 3 | +import android.app.ActivityManager; | ||
| 4 | +import android.content.Context; | ||
| 5 | + | ||
| 6 | +import com.cnlive.libs.base.util.SharedPreferencesHelper; | ||
| 7 | + | ||
| 8 | +import java.util.ArrayList; | ||
| 9 | + | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 服务工具类 | ||
| 13 | + */ | ||
| 14 | +public class ServiceUtils { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 判断服务是否开启 | ||
| 18 | + * | ||
| 19 | + * @return | ||
| 20 | + */ | ||
| 21 | + public static boolean isServiceRunning(Context context, String ServiceName) { | ||
| 22 | + if (("").equals(ServiceName) || ServiceName == null) | ||
| 23 | + return false; | ||
| 24 | + ActivityManager myManager = (ActivityManager) context | ||
| 25 | + .getSystemService(Context.ACTIVITY_SERVICE); | ||
| 26 | + ArrayList<ActivityManager.RunningServiceInfo> runningService = (ArrayList<ActivityManager.RunningServiceInfo>) myManager | ||
| 27 | + .getRunningServices(30); | ||
| 28 | + for (int i = 0; i < runningService.size(); i++) { | ||
| 29 | + if (runningService.get(i).service.getClassName().toString() | ||
| 30 | + .equals(ServiceName)) { | ||
| 31 | + return true; | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + return false; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + public static boolean getIsKeepState(Context context, String value) { | ||
| 38 | + boolean result = SharedPreferencesHelper.getInstance(context).getBoolean(value, false); | ||
| 39 | + return result; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + public static void setIsKeepState(Context context, String value, boolean state) { | ||
| 43 | + SharedPreferencesHelper.getInstance(context).setValue(value, state); | ||
| 44 | + | ||
| 45 | + } | ||
| 46 | +} | ||
| 0 | \ No newline at end of file | 47 | \ No newline at end of file |
moudle_pedometer/src/main/res/drawable/ic_icon.png
0 → 100644
9.25 KB
moudle_pedometer/src/main/res/layout/activity_pedometer_main.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | +<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | + xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | + xmlns:tools="http://schemas.android.com/tools" | ||
| 5 | + android:layout_width="match_parent" | ||
| 6 | + android:layout_height="match_parent" | ||
| 7 | + tools:context="com.cnlive.moudle.pedometer.ui.activity.PedometerMainActivity"> | ||
| 8 | + | ||
| 9 | +</android.support.constraint.ConstraintLayout> |
moudle_pedometer/src/main/res/layout/custom_step_notification.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="utf-8"?> | ||
| 2 | + | ||
| 3 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 4 | + android:layout_width="match_parent" | ||
| 5 | + android:layout_height="wrap_content" | ||
| 6 | + android:gravity="center" | ||
| 7 | + android:orientation="vertical"> | ||
| 8 | + | ||
| 9 | + <LinearLayout | ||
| 10 | + android:layout_width="match_parent" | ||
| 11 | + android:layout_height="wrap_content" | ||
| 12 | + android:gravity="center_vertical"> | ||
| 13 | + | ||
| 14 | + <LinearLayout | ||
| 15 | + android:layout_width="wrap_content" | ||
| 16 | + android:layout_height="wrap_content" | ||
| 17 | + android:layout_weight="1" | ||
| 18 | + android:orientation="vertical"> | ||
| 19 | + | ||
| 20 | + <TextView | ||
| 21 | + android:id="@+id/tv_tip" | ||
| 22 | + android:layout_width="wrap_content" | ||
| 23 | + android:layout_height="wrap_content" | ||
| 24 | + android:layout_marginLeft="10dp" | ||
| 25 | + android:layout_marginTop="3dp" | ||
| 26 | + android:text="今日步数" | ||
| 27 | + android:textColor="#707070" | ||
| 28 | + android:textSize="14sp" /> | ||
| 29 | + | ||
| 30 | + <LinearLayout | ||
| 31 | + android:layout_width="wrap_content" | ||
| 32 | + android:layout_height="wrap_content"> | ||
| 33 | + | ||
| 34 | + <TextView | ||
| 35 | + android:id="@+id/tv_step_count" | ||
| 36 | + android:layout_width="wrap_content" | ||
| 37 | + android:layout_height="wrap_content" | ||
| 38 | + android:layout_centerHorizontal="true" | ||
| 39 | + android:layout_marginLeft="15dp" | ||
| 40 | + android:text="0" | ||
| 41 | + android:textColor="#707070" | ||
| 42 | + android:textSize="20sp" /> | ||
| 43 | + | ||
| 44 | + <TextView | ||
| 45 | + android:layout_width="wrap_content" | ||
| 46 | + android:layout_height="wrap_content" | ||
| 47 | + android:layout_centerHorizontal="true" | ||
| 48 | + android:layout_marginLeft="3dp" | ||
| 49 | + android:text="步" | ||
| 50 | + android:textColor="#707070" | ||
| 51 | + android:textSize="14sp" /> | ||
| 52 | + </LinearLayout> | ||
| 53 | + </LinearLayout> | ||
| 54 | + | ||
| 55 | + <LinearLayout | ||
| 56 | + android:layout_width="0dp" | ||
| 57 | + android:layout_height="match_parent" | ||
| 58 | + android:layout_weight="1" | ||
| 59 | + android:gravity="right|center"> | ||
| 60 | + | ||
| 61 | + <ImageView | ||
| 62 | + android:id="@+id/iv_icon" | ||
| 63 | + android:layout_width="35dp" | ||
| 64 | + android:layout_height="35dp" | ||
| 65 | + android:layout_marginRight="10dp" | ||
| 66 | + android:src="@drawable/ic_icon" /> | ||
| 67 | + </LinearLayout> | ||
| 68 | + </LinearLayout> | ||
| 69 | + | ||
| 70 | + | ||
| 71 | +</LinearLayout> |
moudle_pedometer/src/main/res/raw/keep_live.mp3
0 → 100644
No preview for this file type
moudle_pedometer/src/main/res/raw/no_kill.mp3
0 → 100644
No preview for this file type