1.血压,天气,计步UI
authorkfl_614 <kfl_614@163.com>
Sun, 22 Mar 2020 11:08:39 +0000 (19:08 +0800)
committerkfl_614 <kfl_614@163.com>
Sun, 22 Mar 2020 11:08:39 +0000 (19:08 +0800)
137 files changed:
.gitignore [new file with mode: 0644]
.idea/codeStyles/Project.xml [new file with mode: 0644]
.idea/gradle.xml [new file with mode: 0644]
.idea/misc.xml [new file with mode: 0644]
.idea/runConfigurations.xml [new file with mode: 0644]
.idea/vcs.xml [new file with mode: 0644]
app/.gitignore [new file with mode: 0644]
app/build.gradle [new file with mode: 0644]
app/proguard-rules.pro [new file with mode: 0644]
app/src/androidTest/java/com/ic/hardware/ExampleInstrumentedTest.java [new file with mode: 0644]
app/src/main/AndroidManifest.xml [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/blood/BloodState.java [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/blood/IBloodAidlInterface.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/blood/IBloodRemoteService.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/ped/IPedAidlInterface.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/ped/IPedRemoteService.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/ped/RecordParcelable.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/ped/RecordParcelable.java [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/weather/IWeatherAidlInterface.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/weather/IWeatherRemoteService.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/weather/WeatherParcelable.aidl [new file with mode: 0644]
app/src/main/aidl/com/ic/aidl/weather/WeatherParcelable.java [new file with mode: 0644]
app/src/main/assets/fonts/Baloo2-SemiBold.ttf [new file with mode: 0644]
app/src/main/assets/only_hr_check.gif [new file with mode: 0644]
app/src/main/java/com/ic/hardware/BloodActivity.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/HardwareApp.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/MainClockGestureDetectorImpl.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/PedometerActivity.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/WeatherActivity.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/adapter/IndicatorAdapter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/adapter/PedRecordAdapter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/adapter/PedSetupAdapter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/entitys/PedRecordEntity.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/entitys/PedSetupEntity.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/fragment/BloodFragment.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/fragment/WeatherFragment.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/fragment/pedometer/PedMainFragment.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/fragment/pedometer/PedRecordFragment.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/fragment/pedometer/PedSetupDialogFragment.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/fragment/pedometer/PedSetupFragment.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/model/BloodCheckModel.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/model/PedometerModel.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/model/WeatherModel.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/presenter/BloodPresenter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/presenter/PedometerPresenter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/presenter/WeatherPresenter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/util/IcLog.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/util/SPUtils.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/util/ServiceUtil.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/WidgetImageTextLayout.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/picker/NumericWheelAdapter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/picker/OnWheelChangedListener.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/picker/OnWheelScrollListener.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/picker/WheelAdapter.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/picker/WheelView.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/ring/OnSelectRing.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/ring/Ring.java [new file with mode: 0644]
app/src/main/java/com/ic/hardware/widget/ring/RingProgress.java [new file with mode: 0644]
app/src/main/res/anim/slide_left_in.xml [new file with mode: 0644]
app/src/main/res/anim/slide_left_out.xml [new file with mode: 0644]
app/src/main/res/anim/slide_right_in.xml [new file with mode: 0644]
app/src/main/res/anim/slide_right_out.xml [new file with mode: 0644]
app/src/main/res/drawable-v24/ic_launcher_foreground.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_launcher_background.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_ped_calories.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_ped_distance.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_ped_height.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_ped_taking.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_ped_task.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_ped_weight.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_red_flag.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_transparent_circle.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_white_circle.xml [new file with mode: 0644]
app/src/main/res/drawable/wheel_val.xml [new file with mode: 0644]
app/src/main/res/layout/activity_blood.xml [new file with mode: 0644]
app/src/main/res/layout/activity_pedometer.xml [new file with mode: 0644]
app/src/main/res/layout/activity_weather.xml [new file with mode: 0644]
app/src/main/res/layout/dialog_ped_setup_layout.xml [new file with mode: 0644]
app/src/main/res/layout/fragment_blood.xml [new file with mode: 0644]
app/src/main/res/layout/fragment_ped_main.xml [new file with mode: 0644]
app/src/main/res/layout/fragment_ped_record.xml [new file with mode: 0644]
app/src/main/res/layout/fragment_ped_setup.xml [new file with mode: 0644]
app/src/main/res/layout/fragment_weather.xml [new file with mode: 0644]
app/src/main/res/layout/item_indicator_layout.xml [new file with mode: 0644]
app/src/main/res/layout/item_ped_record.xml [new file with mode: 0644]
app/src/main/res/layout/item_ped_setup.xml [new file with mode: 0644]
app/src/main/res/layout/widget_image_text_layout.xml [new file with mode: 0644]
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml [new file with mode: 0644]
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml [new file with mode: 0644]
app/src/main/res/mipmap-hdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-hdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/ic_cancel.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/ic_ok.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/qs_background.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_baoxue.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_baoyu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_bingbao.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_daxue.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_dayu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_duoyun.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_leizhenyu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_none.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_qing.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_qingwu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_qingyu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_qingyun.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_shachenbao.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_wu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_xiaoxue.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_xiaoyu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_yangsha.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_yin.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_yinduoyun.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_yinyu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_zhenyu.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_zhongxue.png [new file with mode: 0644]
app/src/main/res/mipmap-mdpi/weather_status_zhongyu.png [new file with mode: 0644]
app/src/main/res/mipmap-xhdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-xxhdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png [new file with mode: 0644]
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png [new file with mode: 0644]
app/src/main/res/values/attrs.xml [new file with mode: 0644]
app/src/main/res/values/colors.xml [new file with mode: 0644]
app/src/main/res/values/strings.xml [new file with mode: 0644]
app/src/main/res/values/styles.xml [new file with mode: 0644]
app/src/test/java/com/ic/hardware/ExampleUnitTest.java [new file with mode: 0644]
build.gradle [new file with mode: 0644]
gradle.properties [new file with mode: 0644]
gradle/wrapper/gradle-wrapper.jar [new file with mode: 0644]
gradle/wrapper/gradle-wrapper.properties [new file with mode: 0644]
gradlew [new file with mode: 0644]
gradlew.bat [new file with mode: 0644]
settings.gradle [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..603b140
--- /dev/null
@@ -0,0 +1,14 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644 (file)
index 0000000..681f41a
--- /dev/null
@@ -0,0 +1,116 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+  </code_scheme>
+</component>
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644 (file)
index 0000000..d291b3d
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <compositeConfiguration>
+          <compositeBuild compositeDefinitionSource="SCRIPT" />
+        </compositeConfiguration>
+        <option name="distributionType" value="DEFAULT_WRAPPED" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="resolveModulePerSourceSet" value="false" />
+        <option name="testRunner" value="PLATFORM" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644 (file)
index 0000000..7bfef59
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644 (file)
index 0000000..7f68460
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644 (file)
index 0000000..94a25f7
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644 (file)
index 0000000..796b96d
--- /dev/null
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644 (file)
index 0000000..b95b0e3
--- /dev/null
@@ -0,0 +1,74 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 29
+    buildToolsVersion "29.0.2"
+    defaultConfig {
+        applicationId "com.ic.hardware"
+        minSdkVersion 19
+        targetSdkVersion 29
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    //使用JDK1.8
+    compileOptions {
+        targetCompatibility JavaVersion.VERSION_1_8
+        sourceCompatibility JavaVersion.VERSION_1_8
+    }
+
+    dataBinding {
+        enabled = true
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    sourceSets {
+        main {
+            jni.srcDirs = []
+            manifest.srcFile 'src/main/AndroidManifest.xml'
+            java.srcDirs = ['src/main/java', 'src/main/aidl']
+            resources.srcDirs = ['src/main/java', 'src/main/aidl']
+            aidl.srcDirs = ['src/main/aidl']
+            res.srcDirs = ['src/main/res']
+            assets.srcDirs = ['src/main/assets']
+        }
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+    // ViewModel
+    implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
+    // LiveData
+    implementation "androidx.lifecycle:lifecycle-livedata:2.2.0"
+    // Saved state module for ViewModel
+    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0"
+    // alternately - if using Java8, use the following instead of lifecycle-compiler
+    implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
+    // optional - ReactiveStreams support for LiveData
+    implementation "androidx.lifecycle:lifecycle-reactivestreams:2.2.0"
+
+    implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.19'
+
+    //recyclerview
+    implementation "androidx.recyclerview:recyclerview:1.1.0"
+    implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
+
+    implementation 'com.google.android.material:material:1.2.0-alpha05'
+
+    implementation 'com.jakewharton.threetenabp:threetenabp:1.2.2'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644 (file)
index 0000000..f1b4245
--- /dev/null
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/app/src/androidTest/java/com/ic/hardware/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/ic/hardware/ExampleInstrumentedTest.java
new file mode 100644 (file)
index 0000000..ce894eb
--- /dev/null
@@ -0,0 +1,27 @@
+package com.ic.hardware;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        assertEquals("com.ic.hardware", appContext.getPackageName());
+    }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..aa92f96
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.ic.hardware">
+
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
+
+    <application
+        android:name=".HardwareApp"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:roundIcon="@mipmap/ic_launcher_round"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+
+        <activity android:name=".PedometerActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".BloodActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".WeatherActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/app/src/main/aidl/com/ic/aidl/blood/BloodState.java b/app/src/main/aidl/com/ic/aidl/blood/BloodState.java
new file mode 100644 (file)
index 0000000..82ed7f7
--- /dev/null
@@ -0,0 +1,16 @@
+package com.ic.aidl.blood;
+
+public class BloodState {
+
+    public static final int BLOOD_NULL_TIPS = -1;
+    public static final int BLOOD_NOT_DEVICE = 1001;
+
+    public static final int BLOOD_PLEASE_QUIET = 1002;
+    public static final int BLOOD_START_CHECK= 1003;
+
+    public static final int BLOOD_NOT_WEAR=1004;
+    public static final int BLOOD_ING_CHECK=1005;
+
+    public static final int BLOOD_STOP_CHECK=1006;
+    public static final int BLOOD_NOT_DATA_CHECK=1007;
+}
diff --git a/app/src/main/aidl/com/ic/aidl/blood/IBloodAidlInterface.aidl b/app/src/main/aidl/com/ic/aidl/blood/IBloodAidlInterface.aidl
new file mode 100644 (file)
index 0000000..092c192
--- /dev/null
@@ -0,0 +1,14 @@
+// IWeatherAidlInterface.aidl
+package com.ic.aidl.blood;
+
+// Declare any non-default types here with import statements
+
+interface IBloodAidlInterface {
+    /**
+     * Demonstrates some basic types that you can use as parameters
+     * and return values in AIDL.
+     */
+
+     void updateBloodCheckResult(int high,int low,int heart);
+     void updateBloodCheckTips(int top,int bottom);
+}
diff --git a/app/src/main/aidl/com/ic/aidl/blood/IBloodRemoteService.aidl b/app/src/main/aidl/com/ic/aidl/blood/IBloodRemoteService.aidl
new file mode 100644 (file)
index 0000000..00546c2
--- /dev/null
@@ -0,0 +1,18 @@
+// IWeatherRemoteService.aidl
+package com.ic.aidl.blood;
+
+// Declare any non-default types here with import statements
+
+import com.ic.aidl.blood.IBloodAidlInterface;
+
+interface IBloodRemoteService {
+    /**
+     * Demonstrates some basic types that you can use as parameters
+     * and return values in AIDL.
+     */
+    void registerCallback(IBloodAidlInterface cb);
+    void unregisterCallback(IBloodAidlInterface cb);
+
+    void startCheckBlood();
+    void stopCheckBlood();
+}
diff --git a/app/src/main/aidl/com/ic/aidl/ped/IPedAidlInterface.aidl b/app/src/main/aidl/com/ic/aidl/ped/IPedAidlInterface.aidl
new file mode 100644 (file)
index 0000000..b9615f6
--- /dev/null
@@ -0,0 +1,15 @@
+// IWeatherAidlInterface.aidl
+package com.ic.aidl.ped;
+
+// Declare any non-default types here with import statements
+import com.ic.aidl.ped.RecordParcelable;
+
+interface IPedAidlInterface {
+    /**
+     * Demonstrates some basic types that you can use as parameters
+     * and return values in AIDL.
+     */
+     void updatePedCount(int count);
+     void updatePedRecord(in List<RecordParcelable> record);
+
+}
diff --git a/app/src/main/aidl/com/ic/aidl/ped/IPedRemoteService.aidl b/app/src/main/aidl/com/ic/aidl/ped/IPedRemoteService.aidl
new file mode 100644 (file)
index 0000000..60b57a3
--- /dev/null
@@ -0,0 +1,20 @@
+// IWeatherRemoteService.aidl
+package com.ic.aidl.ped;
+
+// Declare any non-default types here with import statements
+
+import com.ic.aidl.ped.IPedAidlInterface;
+
+interface IPedRemoteService {
+    /**
+     * Demonstrates some basic types that you can use as parameters
+     * and return values in AIDL.
+     */
+    void registerCallback(IPedAidlInterface cb);
+    void unregisterCallback(IPedAidlInterface cb);
+
+    void queryPedCount();
+    void queryPedRecord();
+    void syncPedHeightSetting(int height);
+    void syncPedWeightSetting(int weight);
+}
diff --git a/app/src/main/aidl/com/ic/aidl/ped/RecordParcelable.aidl b/app/src/main/aidl/com/ic/aidl/ped/RecordParcelable.aidl
new file mode 100644 (file)
index 0000000..7f54af8
--- /dev/null
@@ -0,0 +1,3 @@
+package com.ic.aidl.ped;
+
+parcelable RecordParcelable;
\ No newline at end of file
diff --git a/app/src/main/aidl/com/ic/aidl/ped/RecordParcelable.java b/app/src/main/aidl/com/ic/aidl/ped/RecordParcelable.java
new file mode 100644 (file)
index 0000000..bcdef77
--- /dev/null
@@ -0,0 +1,88 @@
+package com.ic.aidl.ped;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class RecordParcelable implements Parcelable {
+    private int number;
+    private String distance;
+    private String calorie;
+    private String date;
+
+    public RecordParcelable() {
+        number = 0;
+        distance = null;
+        calorie = null;
+        date = "";
+    }
+
+    public RecordParcelable(int number, String distance, String calorie, String date) {
+        this.number = number;
+        this.distance = distance;
+        this.calorie = calorie;
+        this.date = date;
+    }
+
+    public RecordParcelable(Parcel p) {
+        this.number = p.readInt();
+        this.distance = p.readString();
+        this.calorie = p.readString();
+        this.date = p.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(number);
+        dest.writeString(distance);
+        dest.writeString(calorie);
+        dest.writeString(date);
+    }
+
+    public static final Parcelable.Creator<RecordParcelable> CREATOR
+            = new Parcelable.Creator<RecordParcelable>() {
+        public RecordParcelable createFromParcel(Parcel in) {
+            return new RecordParcelable(in);
+        }
+
+        public RecordParcelable[] newArray(int size) {
+            return new RecordParcelable[size];
+        }
+    };
+
+    public int getNumber() {
+        return number;
+    }
+
+    public void setNumber(int number) {
+        this.number = number;
+    }
+
+    public String getDistance() {
+        return distance;
+    }
+
+    public void setDistance(String distance) {
+        this.distance = distance;
+    }
+
+    public String getCalorie() {
+        return calorie;
+    }
+
+    public void setCalorie(String calorie) {
+        this.calorie = calorie;
+    }
+
+    public String getDate() {
+        return date;
+    }
+
+    public void setDate(String date) {
+        this.date = date;
+    }
+}
diff --git a/app/src/main/aidl/com/ic/aidl/weather/IWeatherAidlInterface.aidl b/app/src/main/aidl/com/ic/aidl/weather/IWeatherAidlInterface.aidl
new file mode 100644 (file)
index 0000000..219c69e
--- /dev/null
@@ -0,0 +1,14 @@
+// IWeatherAidlInterface.aidl
+package com.ic.aidl.weather;
+
+// Declare any non-default types here with import statements
+import com.ic.aidl.weather.WeatherParcelable;
+
+interface IWeatherAidlInterface {
+    /**
+     * Demonstrates some basic types that you can use as parameters
+     * and return values in AIDL.
+     */
+     void updateCurrWeather(in WeatherParcelable weather);
+
+}
diff --git a/app/src/main/aidl/com/ic/aidl/weather/IWeatherRemoteService.aidl b/app/src/main/aidl/com/ic/aidl/weather/IWeatherRemoteService.aidl
new file mode 100644 (file)
index 0000000..2f33b2b
--- /dev/null
@@ -0,0 +1,17 @@
+// IWeatherRemoteService.aidl
+package com.ic.aidl.weather;
+
+// Declare any non-default types here with import statements
+
+import com.ic.aidl.weather.IWeatherAidlInterface;
+
+interface IWeatherRemoteService {
+    /**
+     * Demonstrates some basic types that you can use as parameters
+     * and return values in AIDL.
+     */
+    void registerCallback(IWeatherAidlInterface cb);
+    void unregisterCallback(IWeatherAidlInterface cb);
+
+    void queryWeatherInfo();
+}
diff --git a/app/src/main/aidl/com/ic/aidl/weather/WeatherParcelable.aidl b/app/src/main/aidl/com/ic/aidl/weather/WeatherParcelable.aidl
new file mode 100644 (file)
index 0000000..59b6fd3
--- /dev/null
@@ -0,0 +1,3 @@
+package com.ic.aidl.weather;
+
+parcelable WeatherParcelable;
\ No newline at end of file
diff --git a/app/src/main/aidl/com/ic/aidl/weather/WeatherParcelable.java b/app/src/main/aidl/com/ic/aidl/weather/WeatherParcelable.java
new file mode 100644 (file)
index 0000000..e7c9bbb
--- /dev/null
@@ -0,0 +1,144 @@
+package com.ic.aidl.weather;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class WeatherParcelable implements Parcelable {
+    private String city;
+    private String statusDay;   // 白天状态,如:晴
+    private String statusNight; // 晚上状态
+    private String figureDay;   // 状态索引
+    private String figureNight;
+    private String temperatureDay;  // 白天温度 或 最低温度
+    private String temperatureNight; // 晚上温度 或 最低温度
+    private String updateTime;       // 更新时间
+
+    public WeatherParcelable() {
+        city = "";
+        statusDay = "";
+        statusNight = "";
+        figureDay = "";
+        figureNight = "";
+        temperatureDay = "";
+        temperatureNight = "";
+        updateTime = "";
+    }
+
+    public WeatherParcelable(String city,
+                             String statusDay, String statusNight,
+                             String figureDay, String figureNight,
+                             String temperatureDay, String temperatureNight,
+                             String updateTime) {
+        this.city = city;
+        this.statusDay = statusDay;
+        this.statusNight = statusNight;
+        this.figureDay = figureDay;
+        this.figureNight =figureNight;
+        this.temperatureDay = temperatureDay;
+        this.temperatureNight = temperatureNight;
+        this.updateTime =updateTime;
+    }
+
+    public WeatherParcelable(Parcel p) {
+        this.city =  p.readString();
+        this.statusDay =  p.readString();
+        this.statusNight =  p.readString();
+        this.figureDay =  p.readString();
+        this.figureNight = p.readString();
+        this.temperatureDay =  p.readString();
+        this.temperatureNight =  p.readString();
+        this.updateTime = p.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(city);
+        dest.writeString(statusDay);
+        dest.writeString(statusNight);
+        dest.writeString(figureDay);
+        dest.writeString(figureNight);
+        dest.writeString(temperatureDay);
+        dest.writeString(temperatureNight);
+        dest.writeString(updateTime);
+    }
+
+    public static final Creator<WeatherParcelable> CREATOR
+            = new Creator<WeatherParcelable>() {
+        public WeatherParcelable createFromParcel(Parcel in) {
+            return new WeatherParcelable(in);
+        }
+
+        public WeatherParcelable[] newArray(int size) {
+            return new WeatherParcelable[size];
+        }
+    };
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    public String getStatusDay() {
+        return statusDay;
+    }
+
+    public void setStatusDay(String statusDay) {
+        this.statusDay = statusDay;
+    }
+
+    public String getStatusNight() {
+        return statusNight;
+    }
+
+    public void setStatusNight(String statusNight) {
+        this.statusNight = statusNight;
+    }
+
+    public String getFigureDay() {
+        return figureDay;
+    }
+
+    public void setFigureDay(String figureDay) {
+        this.figureDay = figureDay;
+    }
+
+    public String getFigureNight() {
+        return figureNight;
+    }
+
+    public void setFigureNight(String figureNight) {
+        this.figureNight = figureNight;
+    }
+
+    public String getTemperatureDay() {
+        return temperatureDay;
+    }
+
+    public void setTemperatureDay(String temperatureDay) {
+        this.temperatureDay = temperatureDay;
+    }
+
+    public String getTemperatureNight() {
+        return temperatureNight;
+    }
+
+    public void setTemperatureNight(String temperatureNight) {
+        this.temperatureNight = temperatureNight;
+    }
+
+    public String getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(String updateTime) {
+        this.updateTime = updateTime;
+    }
+}
diff --git a/app/src/main/assets/fonts/Baloo2-SemiBold.ttf b/app/src/main/assets/fonts/Baloo2-SemiBold.ttf
new file mode 100644 (file)
index 0000000..fe3453d
Binary files /dev/null and b/app/src/main/assets/fonts/Baloo2-SemiBold.ttf differ
diff --git a/app/src/main/assets/only_hr_check.gif b/app/src/main/assets/only_hr_check.gif
new file mode 100644 (file)
index 0000000..67011c6
Binary files /dev/null and b/app/src/main/assets/only_hr_check.gif differ
diff --git a/app/src/main/java/com/ic/hardware/BloodActivity.java b/app/src/main/java/com/ic/hardware/BloodActivity.java
new file mode 100644 (file)
index 0000000..dd89530
--- /dev/null
@@ -0,0 +1,31 @@
+package com.ic.hardware;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import android.os.Bundle;
+
+import com.ic.hardware.fragment.BloodFragment;
+
+public class BloodActivity extends AppCompatActivity {
+
+    private FragmentManager fragmentManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_blood);
+        fragmentManager = getSupportFragmentManager();
+        commitFragment(BloodFragment.newInstance());
+    }
+
+    private void commitFragment(Fragment fragment) {
+        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+        fragmentTransaction.replace(R.id.main_content_fl, fragment);
+        fragmentTransaction.commit();
+    }
+
+
+}
diff --git a/app/src/main/java/com/ic/hardware/HardwareApp.java b/app/src/main/java/com/ic/hardware/HardwareApp.java
new file mode 100644 (file)
index 0000000..e6e1e68
--- /dev/null
@@ -0,0 +1,17 @@
+package com.ic.hardware;
+
+import android.app.Application;
+
+import com.ic.hardware.util.IcLog;
+import com.jakewharton.threetenabp.AndroidThreeTen;
+
+public class HardwareApp extends Application {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        IcLog.setLogHead("IcHardware");
+
+        AndroidThreeTen.init(this);
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/MainClockGestureDetectorImpl.java b/app/src/main/java/com/ic/hardware/MainClockGestureDetectorImpl.java
new file mode 100644 (file)
index 0000000..8ffa181
--- /dev/null
@@ -0,0 +1,44 @@
+package com.ic.hardware;
+
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import com.ic.hardware.util.IcLog;
+
+
+public class MainClockGestureDetectorImpl extends GestureDetector.SimpleOnGestureListener {
+    private static final String TAG = "MainClockGesture";
+
+    public interface OnClockGestureFlingListener{
+        void onDirection(int flag);
+    }
+
+    private OnClockGestureFlingListener mListener;
+
+    public MainClockGestureDetectorImpl(OnClockGestureFlingListener flingListener ){
+        this.mListener = flingListener;
+    }
+
+    @Override
+    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+        IcLog.i(TAG, "onFling x1:" + e1.getX()+ "x2:" + e2.getX());
+
+        if(e1.getX() - e2.getX() > 30){
+            onCallBackListener(1);
+            return true;
+        }
+
+        if(e2.getX() - e1.getX() > 30){
+            onCallBackListener(2);
+            return true;
+        }
+
+        return super.onFling(e1, e2, velocityX, velocityY);
+    }
+
+    private void onCallBackListener(int flag){
+        if(mListener != null){
+            mListener.onDirection(flag);
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/PedometerActivity.java b/app/src/main/java/com/ic/hardware/PedometerActivity.java
new file mode 100644 (file)
index 0000000..7e98bfb
--- /dev/null
@@ -0,0 +1,180 @@
+package com.ic.hardware;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.view.GestureDetectorCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.os.Bundle;
+import android.view.MotionEvent;
+
+import com.ic.hardware.adapter.IndicatorAdapter;
+import com.ic.hardware.fragment.pedometer.PedMainFragment;
+import com.ic.hardware.fragment.pedometer.PedRecordFragment;
+import com.ic.hardware.fragment.pedometer.PedSetupDialogFragment;
+import com.ic.hardware.fragment.pedometer.PedSetupFragment;
+import com.ic.hardware.model.PedometerModel;
+import com.ic.hardware.util.IcLog;
+import com.ic.hardware.util.SPUtils;
+
+public class PedometerActivity extends AppCompatActivity
+        implements PedSetupDialogFragment.NoticeDialogListener,
+        PedMainFragment.QueryPedometerCountListener,
+        PedRecordFragment.QueryPedometerRecordListener,
+        MainClockGestureDetectorImpl.OnClockGestureFlingListener {
+
+    private static final String TAG = "PedometerActivity";
+
+    private FragmentManager fragmentManager;
+
+    private PedometerModel pedometerModel;
+    private IndicatorAdapter indicatorAdapter;
+    private int currIndex;
+    private Fragment[] mFtList;
+
+    private GestureDetectorCompat gesture;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_pedometer);
+        initFt();
+        initIndicator();
+        initPedometerModel();
+        initOnTouchListener();
+    }
+
+    private void initPedometerModel() {
+        PedometerModel.Factory factory = new PedometerModel.Factory(getApplication());
+        pedometerModel = new ViewModelProvider(this, factory).get(PedometerModel.class);
+
+        pedometerModel.getObsPedCount().observe(this, integer -> {
+            ((PedMainFragment) mFtList[1]).updatePedNumber(integer);
+        });
+
+        pedometerModel.getObsPedRecord().observe(this, record -> {
+            ((PedRecordFragment) mFtList[2]).updatePedRecord(record);
+        });
+
+        pedometerModel.onBindService();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (pedometerModel != null) {
+            pedometerModel.onUnBindService();
+            pedometerModel = null;
+        }
+    }
+
+    @Override
+    public void onQueryCount() {
+        pedometerModel.queryPedCount();
+    }
+
+    @Override
+    public void onQueryRecord() {
+        pedometerModel.queryPedRecord();
+    }
+
+    @Override
+    public void onDialogUpdate(int type, int value) {
+        ((PedSetupFragment) mFtList[0]).updateSetuUpValue(type, value);
+        switch (type) {
+            case 0:
+                pedometerModel.syncPedHeightSetting(value);
+                SPUtils.setPedHeightSP(getApplicationContext(), value);
+            case 1:
+                pedometerModel.syncPedWeightSetting(value);
+                SPUtils.setPedWighttSP(getApplicationContext(), value);
+                break;
+        }
+    }
+
+    @Override
+    public void onDirection(int flag) {
+        switch (flag) {
+            case 1:
+                //左滑
+                if (currIndex == mFtList.length - 1) {
+                    return;
+                } else {
+                    currIndex++;
+                    commitFragment(mFtList[currIndex]);
+                    indicatorAdapter.updateIndicatorState(currIndex);
+                }
+                break;
+            case 2:
+                //右滑
+                IcLog.i(TAG,"Right >>>>>>>>>>>>>>>");
+                if (currIndex == 0) {
+                    return;
+                } else {
+                    currIndex--;
+                    commitFragment(mFtList[currIndex]);
+                    indicatorAdapter.updateIndicatorState(currIndex);
+                }
+                break;
+        }
+    }
+
+
+    private void initFt() {
+        fragmentManager = getSupportFragmentManager();
+        mFtList = new Fragment[3];
+        mFtList[0] = PedSetupFragment.newInstance();
+        mFtList[1] = PedMainFragment.newInstance();
+        mFtList[2] = PedRecordFragment.newInstance();
+        currIndex = 1;
+        commitFragment(mFtList[1]);
+    }
+
+    private void initIndicator() {
+        RecyclerView indicatorRv = findViewById(R.id.ped_indicator_rv);
+        indicatorAdapter = new IndicatorAdapter();
+        LinearLayoutManager mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
+        indicatorRv.setLayoutManager(mLayoutManager);
+        indicatorRv.setAdapter(indicatorAdapter);
+    }
+
+    private void commitFragment(Fragment fragment) {
+        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+
+        for (int i = 0; i < mFtList.length; i++) {
+            if(mFtList[i].isAdded()){
+                fragmentTransaction.hide(mFtList[i]);
+            }
+        }
+
+        if (fragment.isAdded()) {
+            fragmentTransaction.show(fragment);
+        } else {
+            fragmentTransaction.add(R.id.ped_content_fl, fragment);
+        }
+
+
+        fragmentTransaction.commit();
+    }
+
+
+    private void initOnTouchListener() {
+        gesture  = new GestureDetectorCompat(this ,new MainClockGestureDetectorImpl(this));
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        gesture.onTouchEvent(ev);
+        super.dispatchTouchEvent(ev);
+        return false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return gesture.onTouchEvent(event);
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/WeatherActivity.java b/app/src/main/java/com/ic/hardware/WeatherActivity.java
new file mode 100644 (file)
index 0000000..d786e16
--- /dev/null
@@ -0,0 +1,30 @@
+package com.ic.hardware;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import android.os.Bundle;
+
+import com.ic.hardware.fragment.WeatherFragment;
+
+public class WeatherActivity extends AppCompatActivity {
+
+    private FragmentManager fragmentManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_weather);
+        fragmentManager = getSupportFragmentManager();
+        commitFragment(WeatherFragment.newInstance());
+    }
+
+    private void commitFragment(Fragment fragment) {
+        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+        fragmentTransaction.replace(R.id.weather_content_fl, fragment);
+        fragmentTransaction.commit();
+    }
+
+}
diff --git a/app/src/main/java/com/ic/hardware/adapter/IndicatorAdapter.java b/app/src/main/java/com/ic/hardware/adapter/IndicatorAdapter.java
new file mode 100644 (file)
index 0000000..fcddf59
--- /dev/null
@@ -0,0 +1,58 @@
+package com.ic.hardware.adapter;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ic.hardware.databinding.ItemIndicatorLayoutBinding;
+
+public class IndicatorAdapter extends RecyclerView.Adapter<IndicatorAdapter.IndicatorHolder> {
+
+    private boolean[] stateList = new boolean[]{false, true, false};
+
+    private int oldCheckIndex=1;
+
+    public void updateIndicatorState(int index){
+        if(oldCheckIndex != index){
+            stateList[index] = true;
+            stateList[oldCheckIndex] =false;
+            oldCheckIndex = index;
+
+            notifyDataSetChanged();
+        }
+    }
+
+    @NonNull
+    @Override
+    public IndicatorHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new IndicatorHolder(ItemIndicatorLayoutBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull IndicatorHolder holder, int position) {
+        holder.bindItem(stateList[position]);
+    }
+
+    @Override
+    public int getItemCount() {
+        return stateList.length;
+    }
+
+    static class IndicatorHolder extends RecyclerView.ViewHolder {
+        private ItemIndicatorLayoutBinding binding = null;
+
+        public IndicatorHolder(@NonNull ItemIndicatorLayoutBinding itemView) {
+            super(itemView.getRoot());
+            binding = itemView;
+        }
+
+        public void bindItem(boolean state) {
+            if (binding != null) {
+                binding.setIsCheckItem(state);
+                binding.executePendingBindings();
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/adapter/PedRecordAdapter.java b/app/src/main/java/com/ic/hardware/adapter/PedRecordAdapter.java
new file mode 100644 (file)
index 0000000..42f40a9
--- /dev/null
@@ -0,0 +1,83 @@
+package com.ic.hardware.adapter;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ic.aidl.ped.RecordParcelable;
+import com.ic.hardware.R;
+import com.ic.hardware.databinding.ItemPedRecordBinding;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PedRecordAdapter extends RecyclerView.Adapter<PedRecordAdapter.RecordHolder> {
+
+    private Context mContext;
+    private List<RecordParcelable> mPedRecord;
+
+    public PedRecordAdapter(Context context){
+        this.mContext = context;
+        this.mPedRecord = new ArrayList<>();
+    }
+
+    public void updatePedRecordList(List<RecordParcelable> records){
+        mPedRecord.clear();
+        mPedRecord.addAll(records);
+        notifyDataSetChanged();
+    }
+
+    @NonNull
+    @Override
+    public RecordHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new RecordHolder(ItemPedRecordBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RecordHolder holder, int position) {
+        holder.bindPedRecord(mContext,mPedRecord.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return mPedRecord.size();
+    }
+
+    static class RecordHolder extends RecyclerView.ViewHolder {
+        private ItemPedRecordBinding binding = null;
+
+        public RecordHolder(@NonNull ItemPedRecordBinding itemView) {
+            super(itemView.getRoot());
+            binding = itemView;
+        }
+
+        public void bindPedRecord(Context context,RecordParcelable entity) {
+            if (binding == null || entity == null) {
+                return;
+            }
+
+            binding.setTakingIcon(R.drawable.ic_ped_taking);
+            binding.setTakingColor(Color.parseColor("#FFA500"));
+            binding.setTakingUnit(context.getString(R.string.tips_target_unit));
+
+            binding.setCaloriesIcon(R.drawable.ic_ped_calories);
+            binding.setCaloriesColor(Color.WHITE);
+            binding.setCaloriesUnit(context.getString(R.string.tips_calories_unit));
+
+            binding.setDistanceIcon(R.drawable.ic_ped_distance);
+            binding.setDistanceColor(Color.WHITE);
+            binding.setDistanceUnit(context.getString(R.string.tips_distance_unit));
+
+            binding.setRecordCalories(entity.getCalorie());
+            binding.setRecordDate(entity.getDate());
+            binding.setRecordDistance(entity.getDistance());
+            binding.setRecordTaking(String.valueOf(entity.getNumber()));
+
+            binding.executePendingBindings();
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/adapter/PedSetupAdapter.java b/app/src/main/java/com/ic/hardware/adapter/PedSetupAdapter.java
new file mode 100644 (file)
index 0000000..6aae4b1
--- /dev/null
@@ -0,0 +1,104 @@
+package com.ic.hardware.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ic.hardware.R;
+import com.ic.hardware.databinding.ItemPedSetupBinding;
+import com.ic.hardware.entitys.PedSetupEntity;
+import com.ic.hardware.fragment.pedometer.PedSetupFragment;
+import com.ic.hardware.util.SPUtils;
+
+
+public class PedSetupAdapter extends RecyclerView.Adapter<PedSetupAdapter.SetUpHolder> {
+
+    private final PedSetupEntity[] setupArray;
+
+    private static final int[] TITLE_RES = new int[]{
+            R.string.ped_height_name,
+            R.string.tips_weight_name,
+            R.string.tips_target_name,
+    };
+
+    private static final int[] UNIT_RES = new int[]{
+            R.string.tips_height_unit,
+            R.string.tips_weight_unit,
+            R.string.tips_target_unit,
+    };
+
+    private static final int[] ICON_RES = new int[]{
+            R.drawable.ic_ped_height,
+            R.drawable.ic_ped_weight,
+            R.drawable.ic_ped_task,
+    };
+
+    private PedSetupFragment.OnPedSetupItemClickListener mListener;
+
+    public PedSetupAdapter(Context context, PedSetupFragment.OnPedSetupItemClickListener listener){
+        setupArray = new PedSetupEntity[3];
+        int[] valueArray = new int[]{
+                SPUtils.getPedHeightSP(context),
+                SPUtils.getPedWighttSP(context),
+                SPUtils.getPedTargetSP(context)
+        };
+
+        for (int i=0; i<setupArray.length; i++){
+            PedSetupEntity entity = new PedSetupEntity();
+            entity.setTitle(context.getString(TITLE_RES[i]));
+            entity.setUnit(context.getString(UNIT_RES[i]));
+            entity.setValue(String.valueOf(valueArray[i]));
+            entity.setIcon(ContextCompat.getDrawable(context,ICON_RES[i]));
+            setupArray[i]=entity;
+        }
+
+        this.mListener=listener;
+    }
+
+    public void notifyUpdateValue(int type,int value){
+        setupArray[type].setValue(String.valueOf(value));
+        notifyDataSetChanged();
+    }
+
+    @NonNull
+    @Override
+    public SetUpHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new SetUpHolder(ItemPedSetupBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull SetUpHolder holder, int position) {
+        holder.bindItem(setupArray[position],mListener,position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return setupArray.length;
+    }
+
+    static class SetUpHolder extends RecyclerView.ViewHolder {
+        private ItemPedSetupBinding binding = null;
+
+        public SetUpHolder(@NonNull ItemPedSetupBinding itemView) {
+            super(itemView.getRoot());
+            binding = itemView;
+        }
+
+        public void bindItem(PedSetupEntity entity,PedSetupFragment.OnPedSetupItemClickListener listener,int position) {
+            if (binding != null) {
+                binding.setSetupItemIcon(entity.getIcon());
+                binding.setSetupItemTitle(entity.getTitle());
+                binding.setSetupItemValue(entity.getValue());
+                binding.setSetupItemUnit(entity.getUnit());
+
+                binding.setOnItemClick(listener);
+                binding.setItemPos(position);
+                binding.executePendingBindings();
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/entitys/PedRecordEntity.java b/app/src/main/java/com/ic/hardware/entitys/PedRecordEntity.java
new file mode 100644 (file)
index 0000000..82e3cce
--- /dev/null
@@ -0,0 +1,47 @@
+package com.ic.hardware.entitys;
+
+public class PedRecordEntity {
+    private String recordDate;
+    private String recordTaking;
+    private String recordCalories;
+    private String recordDistance;
+
+    public PedRecordEntity(String recordDate,String recordTaking,String recordCalories,String recordDistance){
+        this.recordDate=recordDate;
+        this.recordTaking=recordTaking;
+        this.recordCalories=recordCalories;
+        this.recordDistance=recordDistance;
+    }
+
+    public String getRecordDate() {
+        return recordDate;
+    }
+
+    public void setRecordDate(String recordDate) {
+        this.recordDate = recordDate;
+    }
+
+    public String getRecordTaking() {
+        return recordTaking;
+    }
+
+    public void setRecordTaking(String recordTaking) {
+        this.recordTaking = recordTaking;
+    }
+
+    public String getRecordCalories() {
+        return recordCalories;
+    }
+
+    public void setRecordCalories(String recordCalories) {
+        this.recordCalories = recordCalories;
+    }
+
+    public String getRecordDistance() {
+        return recordDistance;
+    }
+
+    public void setRecordDistance(String recordDistance) {
+        this.recordDistance = recordDistance;
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/entitys/PedSetupEntity.java b/app/src/main/java/com/ic/hardware/entitys/PedSetupEntity.java
new file mode 100644 (file)
index 0000000..1d76163
--- /dev/null
@@ -0,0 +1,52 @@
+package com.ic.hardware.entitys;
+
+import android.graphics.drawable.Drawable;
+
+public class PedSetupEntity {
+    private Drawable icon;
+    private String title;
+    private String value;
+    private String unit;
+
+    public PedSetupEntity() {
+    }
+
+    public PedSetupEntity(Drawable icon, String title, String value, String unit) {
+        this.icon = icon;
+        this.title = title;
+        this.value = value;
+        this.unit = unit;
+    }
+
+    public Drawable getIcon() {
+        return icon;
+    }
+
+    public void setIcon(Drawable icon) {
+        this.icon = icon;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getUnit() {
+        return unit;
+    }
+
+    public void setUnit(String unit) {
+        this.unit = unit;
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/fragment/BloodFragment.java b/app/src/main/java/com/ic/hardware/fragment/BloodFragment.java
new file mode 100644 (file)
index 0000000..5845042
--- /dev/null
@@ -0,0 +1,165 @@
+package com.ic.hardware.fragment;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.Uri;
+import android.nfc.Tag;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import android.os.PowerManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.ic.hardware.HardwareApp;
+import com.ic.hardware.BloodActivity;
+import com.ic.hardware.databinding.FragmentBloodBinding;
+import com.ic.hardware.model.BloodCheckModel;
+import com.ic.hardware.util.IcLog;
+
+import java.io.IOException;
+
+import pl.droidsonroids.gif.GifDrawable;
+import pl.droidsonroids.gif.GifImageView;
+
+import static android.content.Context.POWER_SERVICE;
+
+
+public class BloodFragment extends Fragment {
+
+    private static final String TAG = "BloodFragment";
+
+    private Context mContext;
+    private HardwareApp mApp;
+
+    private FragmentBloodBinding bloodBinding;
+
+    private PowerManager.WakeLock mWakeLock = null;
+    private BloodCheckModel bloodModel;
+
+    private GifDrawable gifFromAssets;
+
+    public static BloodFragment newInstance() {
+        return new BloodFragment();
+    }
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        this.mContext = context;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mApp = (HardwareApp) mContext.getApplicationContext();
+        initWakeLock(mContext);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        bloodBinding = FragmentBloodBinding.inflate(inflater, container, false);
+
+        bloodBinding.setIsShowTips(true);
+
+        initBloodModel();
+        setGifDrawable();
+        bloodModel.registerScreenReceiver();
+        return bloodBinding.getRoot();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mWakeLock != null) {
+            mWakeLock.acquire();
+        }
+
+        bloodModel.startCheckBlood();
+
+        gifFromAssets.start();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        bloodModel.stopCheckBlood();
+        gifFromAssets.stop();
+
+        releaseWakeLock();
+    }
+
+    private void initBloodModel() {
+        BloodCheckModel.Factory factory = new BloodCheckModel.Factory(mApp);
+        bloodModel= new ViewModelProvider(this, factory).get(BloodCheckModel.class);
+        bloodModel.getObsBloodValue().observe(getViewLifecycleOwner(), value -> {
+            bloodBinding.setIsShowTips(false);
+            bloodBinding.setHighValue(value[0]);
+            bloodBinding.setLowValue(value[1]);
+            bloodBinding.setHeartValue(value[2]);
+        });
+
+        bloodModel.getObsTipsInfo().observe(getViewLifecycleOwner(),tips ->{
+            bloodBinding.setIsShowTips(true);
+            bloodBinding.setTopTips(tips[0]);
+            bloodBinding.setBottomTips(tips[1]);
+        });
+
+        bloodModel.getObsCheckEnd().observe(getViewLifecycleOwner(),end ->{
+            switch (end){
+                case 1:
+                    IcLog.i(TAG,"Release WakeLock");
+                    releaseWakeLock();
+                    break;
+                case 2:
+                    ((BloodActivity)mContext).finish();
+                    break;
+            }
+        });
+    }
+
+    @SuppressLint("InvalidWakeLockTag")
+    private void initWakeLock(Context context) {
+        PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
+        if (powerManager != null) {
+            mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "BloodWakeLock");
+        }
+    }
+
+    private void releaseWakeLock() {
+        if (mWakeLock != null) {
+            mWakeLock.release();
+        }
+        mWakeLock = null;
+    }
+
+    private void setGifDrawable() {
+        GifImageView gifImageView = bloodBinding.icHeartGif;
+        gifImageView.setScaleType(ImageView.ScaleType.CENTER);
+        try {
+            gifFromAssets = new GifDrawable(mContext.getAssets(), "only_hr_check.gif");
+            gifImageView.setImageDrawable(gifFromAssets);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        IcLog.i(TAG,"onDestroy");
+
+        bloodModel.unregisterScreenReceiver();
+        bloodModel = null;
+
+        if (gifFromAssets != null) {
+            gifFromAssets.recycle();
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/fragment/WeatherFragment.java b/app/src/main/java/com/ic/hardware/fragment/WeatherFragment.java
new file mode 100644 (file)
index 0000000..ee38dd3
--- /dev/null
@@ -0,0 +1,159 @@
+package com.ic.hardware.fragment;
+
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.ic.hardware.HardwareApp;
+import com.ic.hardware.R;
+import com.ic.hardware.databinding.FragmentWeatherBinding;
+import com.ic.hardware.model.WeatherModel;
+
+import org.threeten.bp.OffsetDateTime;
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class WeatherFragment extends Fragment {
+
+    private Context mContext;
+    private HardwareApp mApp;
+
+    private FragmentWeatherBinding weatherBinding;
+    private WeatherModel weatherModel;
+
+    public static WeatherFragment newInstance() {
+        return new WeatherFragment();
+    }
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        this.mContext = context;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mApp = (HardwareApp) mContext.getApplicationContext();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        weatherBinding = FragmentWeatherBinding.inflate(inflater, container, false);
+        initWeatherModel();
+        return weatherBinding.getRoot();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        weatherModel.onUnBindService();
+    }
+
+    private void initWeatherModel() {
+        WeatherModel.Factory factory = new WeatherModel.Factory(mApp);
+        weatherModel = new ViewModelProvider(this, factory).get(WeatherModel.class);
+
+
+        weatherModel.getObsWeatherParcelable().observe(getViewLifecycleOwner(), parcelable -> {
+
+            if (parcelable == null) {
+                weatherBinding.setIsShowTips(true);
+                weatherBinding.setIsErrorTips(getString(R.string.tips_error_weather_no_data));
+                return;
+            }
+
+            OffsetDateTime time = OffsetDateTime.now();
+
+            boolean inNight = time.getHour() > 18;
+            weatherBinding.setWeatherCity(parcelable.getCity());
+            weatherBinding.setWeatherSection(
+                    String.format(getString(R.string.weather_temp_space_degree),
+                            parcelable.getTemperatureDay(),
+                            parcelable.getTemperatureNight()));
+
+            String degree = getString(R.string.weather_temp_degree);
+
+            if (inNight) {
+                weatherBinding.setWeatherState(parcelable.getStatusNight());
+                weatherBinding.setWeatherTemp(String.format(degree, parcelable.getTemperatureNight()));
+                weatherBinding.setWeatherIcon(ContextCompat.getDrawable(mContext, getStatusImage(parcelable.getStatusNight())));
+            } else {
+                weatherBinding.setWeatherState(parcelable.getStatusDay());
+                weatherBinding.setWeatherTemp(String.format(degree, parcelable.getTemperatureDay()));
+                weatherBinding.setWeatherIcon(ContextCompat.getDrawable(mContext, getStatusImage(parcelable.getStatusDay())));
+            }
+        });
+
+        weatherModel.onBindService();
+    }
+
+    public static int getStatusImage(String stuats) {
+        int imageId = R.mipmap.weather_status_none;
+        if (stuats != null) {
+            if (stuats.contains("多云") || stuats.contains("晴")) {
+                imageId = R.mipmap.weather_status_qingyun;
+            } else if (stuats.contains("多云") && stuats.contains("阴")) {
+                imageId = R.mipmap.weather_status_yinduoyun;
+            } else if (stuats.contains("阴") && stuats.contains("雨")) {
+                imageId = R.mipmap.weather_status_yinyu;
+            } else if (stuats.contains("晴") && stuats.contains("雨")) {
+                imageId = R.mipmap.weather_status_qingyu;
+            } else if (stuats.contains("晴") && stuats.contains("雾")) {
+                imageId = R.mipmap.weather_status_qingwu;
+            } else if (stuats.contains("晴")) {
+                imageId = R.mipmap.weather_status_qing;
+            } else if (stuats.contains("多云")) {
+                imageId = R.mipmap.weather_status_duoyun;
+            } else if (stuats.contains("雷阵雨")) {
+                imageId = R.mipmap.weather_status_leizhenyu;
+            } else if (stuats.contains("阵雨")) {
+                imageId = R.mipmap.weather_status_zhenyu;
+            } else if (stuats.contains("小雨")) {
+                imageId = R.mipmap.weather_status_xiaoyu;
+            } else if (stuats.contains("中雨")) {
+                imageId = R.mipmap.weather_status_zhongyu;
+            } else if (stuats.contains("大雨")) {
+                imageId = R.mipmap.weather_status_dayu;
+            } else if (stuats.contains("暴雨")) {
+                imageId = R.mipmap.weather_status_baoyu;
+            } else if (stuats.contains("冰雹")) {
+                imageId = R.mipmap.weather_status_bingbao;
+            } else if (stuats.contains("小雪")) {
+                imageId = R.mipmap.weather_status_xiaoxue;
+            } else if (stuats.contains("中雪")) {
+                imageId = R.mipmap.weather_status_zhongxue;
+            } else if (stuats.contains("大雪")) {
+                imageId = R.mipmap.weather_status_daxue;
+            } else if (stuats.contains("暴雪")) {
+                imageId = R.mipmap.weather_status_baoxue;
+            } else if (stuats.contains("扬沙")) {
+                imageId = R.mipmap.weather_status_yangsha;
+            } else if (stuats.contains("沙尘")) {
+                imageId = R.mipmap.weather_status_shachenbao;
+            } else if (stuats.contains("雾")) {
+                imageId = R.mipmap.weather_status_wu;
+
+                // PARRY 2017.04.27
+            } else if (stuats.contains("阴")) {
+                imageId = R.mipmap.weather_status_yin;
+            }
+        }
+
+        return imageId;
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/fragment/pedometer/PedMainFragment.java b/app/src/main/java/com/ic/hardware/fragment/pedometer/PedMainFragment.java
new file mode 100644 (file)
index 0000000..d396c28
--- /dev/null
@@ -0,0 +1,115 @@
+package com.ic.hardware.fragment.pedometer;
+
+
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.core.view.GestureDetectorCompat;
+import androidx.fragment.app.Fragment;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.ic.hardware.HardwareApp;
+import com.ic.hardware.MainClockGestureDetectorImpl;
+import com.ic.hardware.databinding.FragmentPedMainBinding;
+import com.ic.hardware.util.SPUtils;
+import com.ic.hardware.widget.ring.Ring;
+import com.ic.hardware.widget.ring.RingProgress;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class PedMainFragment extends Fragment {
+
+    public interface QueryPedometerCountListener {
+        void onQueryCount();
+    }
+
+    private Context mContext;
+    private HardwareApp mApp;
+
+    private FragmentPedMainBinding pedMainBinding;
+
+    private QueryPedometerCountListener queryListner;
+
+    public static PedMainFragment newInstance() {
+        return new PedMainFragment();
+    }
+
+    private int targetValue;
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        this.mContext = context;
+
+        try {
+            queryListner = (QueryPedometerCountListener) context;
+        } catch (ClassCastException e) {
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mApp = (HardwareApp) mContext.getApplicationContext();
+    }
+
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        pedMainBinding = FragmentPedMainBinding.inflate(inflater, container, false);
+        updatePedNumber(0);
+        targetValue = SPUtils.getPedTargetSP(mApp.getApplicationContext());
+        pedMainBinding.setPedTarget(String.valueOf(targetValue));
+        return pedMainBinding.getRoot();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        queryListner.onQueryCount();
+    }
+
+    public void updatePedNumber(int number) {
+        int value = (int) (number * 0.01);
+        if (value < 1) {
+            value = 1;
+        }
+        setupRingProgress(value);
+        setupRingProgressNumberTv(number);
+    }
+
+    private void setupRingProgress(int number) {
+        RingProgress mRingProgress = pedMainBinding.ringProgress;
+
+        Ring r = new Ring(number, "", "", Color.GREEN, Color.YELLOW);
+        List<Ring> mlistRing = new ArrayList<>();
+        mlistRing.add(r);
+        mRingProgress.setData(mlistRing, 5000);
+    }
+
+    private void setupRingProgressNumberTv(int number) {
+        TextView pedNumber = pedMainBinding.pedNumberTv;
+        Typeface typeFace = Typeface.createFromAsset(mContext.getAssets(), "fonts/Baloo2-SemiBold.ttf");
+        pedNumber.setTypeface(typeFace);
+
+        ValueAnimator animator = ValueAnimator.ofInt(0, number);
+        animator.setDuration(5000);
+        animator.addUpdateListener(animation -> pedMainBinding.setPedNumber(animation.getAnimatedValue().toString()));
+        animator.start();
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/fragment/pedometer/PedRecordFragment.java b/app/src/main/java/com/ic/hardware/fragment/pedometer/PedRecordFragment.java
new file mode 100644 (file)
index 0000000..70d0b98
--- /dev/null
@@ -0,0 +1,78 @@
+package com.ic.hardware.fragment.pedometer;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.ic.aidl.ped.RecordParcelable;
+import com.ic.hardware.R;
+import com.ic.hardware.adapter.PedRecordAdapter;
+
+import java.util.List;
+
+public class PedRecordFragment extends Fragment {
+
+    public interface QueryPedometerRecordListener{
+        void onQueryRecord();
+    }
+
+    public static PedRecordFragment newInstance() {
+        return new PedRecordFragment();
+    }
+
+    private Context mContext;
+    private RecyclerView mSetupRv;
+    private PedRecordAdapter setupAdapte;
+    private QueryPedometerRecordListener queryListner;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        this.mContext=context;
+
+        try {
+            queryListner = (QueryPedometerRecordListener) context;
+        } catch (ClassCastException e) {
+        }
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_ped_record, container, false);
+        mSetupRv = view.findViewById(R.id.ped_record_rv);
+        initRecyclerView();
+        queryListner.onQueryRecord();
+        return view;
+    }
+
+    public void  updatePedRecord(List<RecordParcelable> records){
+        setupAdapte.updatePedRecordList(records);
+    }
+
+    private void initRecyclerView() {
+        setupAdapte = new PedRecordAdapter(mContext);
+        LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);
+        mSetupRv.setLayoutManager(mLayoutManager);
+        mSetupRv.setAdapter(setupAdapte);
+    }
+
+}
diff --git a/app/src/main/java/com/ic/hardware/fragment/pedometer/PedSetupDialogFragment.java b/app/src/main/java/com/ic/hardware/fragment/pedometer/PedSetupDialogFragment.java
new file mode 100644 (file)
index 0000000..1d81205
--- /dev/null
@@ -0,0 +1,118 @@
+package com.ic.hardware.fragment.pedometer;
+
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.icu.text.DisplayContext;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ImageButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.collection.ArrayMap;
+import androidx.fragment.app.DialogFragment;
+
+import com.ic.hardware.R;
+import com.ic.hardware.util.SPUtils;
+import com.ic.hardware.widget.picker.NumericWheelAdapter;
+import com.ic.hardware.widget.picker.WheelView;
+
+public class PedSetupDialogFragment extends DialogFragment {
+
+    public interface NoticeDialogListener {
+        void onDialogUpdate(int type, int value);
+    }
+
+    public static PedSetupDialogFragment newInstance(int type) {
+        PedSetupDialogFragment frag = new PedSetupDialogFragment();
+        Bundle args = new Bundle();
+        args.putInt("type", type);
+        frag.setArguments(args);
+        return frag;
+    }
+
+    private Context mContext;
+    private int mCurrType;
+
+    private WheelView numberPicker;
+
+    private final ArrayMap<Integer, int[]> limil = new ArrayMap<>();
+
+    private final String[] UNIT_RES = new String[2];
+    private final int[] VALUE_CURR = new int[2];
+
+    private NoticeDialogListener mListener;
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        this.mContext = context;
+        try {
+            mListener = (NoticeDialogListener) context;
+        } catch (ClassCastException e) {
+        }
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Bundle bundle = getArguments();
+        if (bundle != null) {
+            mCurrType = bundle.getInt("type");
+        }
+
+        VALUE_CURR[0] = SPUtils.getPedHeightSP(mContext);
+        VALUE_CURR[1] = SPUtils.getPedWighttSP(mContext);
+
+        UNIT_RES[0] = getString(R.string.tips_height_unit);
+        UNIT_RES[1] = getString(R.string.tips_weight_unit);
+
+        limil.put(0, new int[]{30, 250});
+        limil.put(1, new int[]{15, 150});
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+
+        Dialog mDialog = getDialog();
+        if (mDialog != null) {
+            getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
+        }
+
+        View popView = inflater.inflate(R.layout.dialog_ped_setup_layout, container);
+        numberPicker = (WheelView) popView.findViewById(R.id.dialog_value_picker);
+        setNumberPicker(limil.get(mCurrType)[0], limil.get(mCurrType)[1], UNIT_RES[mCurrType], VALUE_CURR[mCurrType]);
+
+        ImageButton bt = (ImageButton) popView.findViewById(R.id.dialog_picker_ok);
+        bt.setOnClickListener(v -> {
+            mListener.onDialogUpdate(mCurrType,limil.get(mCurrType)[0]+numberPicker.getCurrentItem());
+            dismiss();
+        });
+        ImageButton cancel = (ImageButton) popView.findViewById(R.id.dialog_picker_cancel);
+        cancel.setOnClickListener(v -> dismiss());
+
+        return popView;
+    }
+
+    private void setNumberPicker(final int numberMin, int numberMax, String label, int numberDefault) {
+        numberPicker.setAdapter(new NumericWheelAdapter(numberMin, numberMax));
+        numberPicker.setLabel(label);
+        numberPicker.setCyclic(true);
+        numberPicker.TEXT_SIZE = (int) spToPx(65);
+        numberPicker.ITEM_TEXT_SIZE = (int) spToPx(42);
+
+        numberPicker.setCurrentItem(numberDefault - numberMin);
+    }
+
+    private float spToPx(float sp) {
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/fragment/pedometer/PedSetupFragment.java b/app/src/main/java/com/ic/hardware/fragment/pedometer/PedSetupFragment.java
new file mode 100644 (file)
index 0000000..7504e6f
--- /dev/null
@@ -0,0 +1,83 @@
+package com.ic.hardware.fragment.pedometer;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.ic.hardware.R;
+import com.ic.hardware.adapter.PedSetupAdapter;
+
+
+public class PedSetupFragment extends Fragment{
+
+    private static final String TAG = "PedSetupFragment";
+
+    public interface OnPedSetupItemClickListener{
+        void onPedSetupItem(int pos);
+    }
+
+    private Context mContext;
+    private RecyclerView mSetupRv;
+    private PedSetupAdapter setupAdapte;
+
+    public static PedSetupFragment newInstance() {
+        return new PedSetupFragment();
+    }
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        this.mContext = context;
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_ped_setup, container, false);
+        mSetupRv = view.findViewById(R.id.ped_setup_rv);
+        initRecyclerView();
+        return view;
+    }
+
+    private void initRecyclerView() {
+        setupAdapte = new PedSetupAdapter(mContext,listener);
+        LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);
+        mSetupRv.setLayoutManager(mLayoutManager);
+        mSetupRv.setAdapter(setupAdapte);
+    }
+
+    private OnPedSetupItemClickListener listener = pos -> {
+        if(pos == 2){
+            return;
+        }
+        FragmentManager fm = getChildFragmentManager();
+        PedSetupDialogFragment alertDialog = PedSetupDialogFragment.newInstance(pos);
+        alertDialog.show(fm, "PedSetupDialog");
+    };
+
+    public void updateSetuUpValue(int type,int value){
+        setupAdapte.notifyUpdateValue(type,value);
+    }
+
+}
+
diff --git a/app/src/main/java/com/ic/hardware/model/BloodCheckModel.java b/app/src/main/java/com/ic/hardware/model/BloodCheckModel.java
new file mode 100644 (file)
index 0000000..d4284d8
--- /dev/null
@@ -0,0 +1,74 @@
+package com.ic.hardware.model;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.ic.hardware.presenter.BloodPresenter;
+
+
+public class BloodCheckModel extends AndroidViewModel {
+
+    private final BloodPresenter bloodPresenter;
+
+    private LiveData<String[]> mObsBloodValue;
+    private LiveData<String[]> mObsTipsInfo;
+    private LiveData<Integer> mObsCheckEnd;
+
+    private BloodCheckModel(@NonNull Application application) {
+        super(application);
+        this.bloodPresenter = new BloodPresenter(application.getApplicationContext());
+        mObsBloodValue = bloodPresenter.getObsBloodValue();
+        mObsTipsInfo = bloodPresenter.getObsTipsInfo();
+        mObsCheckEnd = bloodPresenter.getObsCheckEnd();
+    }
+
+    public LiveData<String[]> getObsBloodValue() {
+        return mObsBloodValue;
+    }
+
+    public LiveData<String[]> getObsTipsInfo() {
+        return mObsTipsInfo;
+    }
+
+    public LiveData<Integer> getObsCheckEnd() {
+        return mObsCheckEnd;
+    }
+
+    public void startCheckBlood(){
+        bloodPresenter.startCheckBlood();
+    }
+
+    public void stopCheckBlood(){
+        bloodPresenter.stopCheckBlood();
+    }
+
+    public void registerScreenReceiver(){
+        bloodPresenter.registerScreenReceiver();
+    }
+
+    public void unregisterScreenReceiver(){
+        bloodPresenter.unregisterScreenReceiver();
+    }
+
+    public static class Factory extends ViewModelProvider.NewInstanceFactory {
+
+        @NonNull
+        private final Application mApplication;
+
+        public Factory(@NonNull Application application) {
+            mApplication = application;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        @NonNull
+        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+            return (T) new BloodCheckModel(mApplication);
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/model/PedometerModel.java b/app/src/main/java/com/ic/hardware/model/PedometerModel.java
new file mode 100644 (file)
index 0000000..3f9dcc1
--- /dev/null
@@ -0,0 +1,78 @@
+package com.ic.hardware.model;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.ic.aidl.ped.RecordParcelable;
+import com.ic.hardware.presenter.PedometerPresenter;
+
+import java.util.List;
+
+public class PedometerModel extends AndroidViewModel {
+
+    private final PedometerPresenter pedometerPresenter;
+
+    private LiveData<Integer> mObsPedCount;
+    private LiveData<List<RecordParcelable>> mObsPedRecord;
+
+    private PedometerModel(@NonNull Application application) {
+        super(application);
+        pedometerPresenter = new PedometerPresenter(application.getApplicationContext());
+        mObsPedCount = pedometerPresenter.getObsPedCount();
+        mObsPedRecord = pedometerPresenter.getObsPedRecord();
+    }
+
+    public LiveData<Integer> getObsPedCount() {
+        return mObsPedCount;
+    }
+
+    public LiveData<List<RecordParcelable>> getObsPedRecord() {
+        return mObsPedRecord;
+    }
+
+    public void onBindService(){
+        pedometerPresenter.bindService();
+    }
+
+    public void onUnBindService(){
+        pedometerPresenter.unbindService();
+    }
+
+    public void  queryPedCount(){
+        pedometerPresenter.queryPedCount();
+    }
+
+    public void  queryPedRecord(){
+        pedometerPresenter.queryPedRecord();
+    }
+
+    public void  syncPedHeightSetting(int height){
+        pedometerPresenter.syncPedHeightSetting(height);
+    }
+
+    public void  syncPedWeightSetting(int weight){
+        pedometerPresenter.syncPedWeightSetting(weight);
+    }
+
+    public static class Factory extends ViewModelProvider.NewInstanceFactory {
+
+        @NonNull
+        private final Application mApplication;
+
+        public Factory(@NonNull Application application) {
+            mApplication = application;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        @NonNull
+        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+            return (T) new PedometerModel(mApplication);
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/model/WeatherModel.java b/app/src/main/java/com/ic/hardware/model/WeatherModel.java
new file mode 100644 (file)
index 0000000..0761933
--- /dev/null
@@ -0,0 +1,53 @@
+package com.ic.hardware.model;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.ic.aidl.weather.WeatherParcelable;
+import com.ic.hardware.presenter.WeatherPresenter;
+
+public class WeatherModel extends AndroidViewModel {
+
+    private final LiveData<WeatherParcelable> weatherParcelable;
+    private final WeatherPresenter weatherPresenter;
+
+    private WeatherModel(@NonNull Application application) {
+        super(application);
+        weatherPresenter = new WeatherPresenter(application.getApplicationContext());
+        weatherParcelable = weatherPresenter.getObsWeatherInfo();
+    }
+
+    public LiveData<WeatherParcelable> getObsWeatherParcelable() {
+        return weatherParcelable;
+    }
+
+    public void onBindService(){
+        weatherPresenter.bindService();
+    }
+
+    public void onUnBindService(){
+        weatherPresenter.unbindService();
+    }
+
+    public static class Factory extends ViewModelProvider.NewInstanceFactory {
+
+        @NonNull
+        private final Application mApplication;
+
+        public Factory(@NonNull Application application) {
+            mApplication = application;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        @NonNull
+        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+            return (T) new WeatherModel(mApplication);
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/presenter/BloodPresenter.java b/app/src/main/java/com/ic/hardware/presenter/BloodPresenter.java
new file mode 100644 (file)
index 0000000..9b280eb
--- /dev/null
@@ -0,0 +1,175 @@
+package com.ic.hardware.presenter;
+
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import androidx.lifecycle.MutableLiveData;
+
+import com.ic.aidl.blood.BloodState;
+import com.ic.aidl.blood.IBloodAidlInterface;
+import com.ic.aidl.blood.IBloodRemoteService;
+import com.ic.hardware.R;
+import com.ic.hardware.util.IcLog;
+import com.ic.hardware.util.ServiceUtil;
+
+public class BloodPresenter {
+
+    private static final String TAG = "BloodPresenter";
+
+    private Context mContext;
+
+    private MutableLiveData<String[]> mObsBloodValue;
+    private MutableLiveData<String[]> mObsTipsInfo;
+    private MutableLiveData<Integer> mObsCheckEnd;
+
+    private IBloodRemoteService mService;
+
+    private SparseArray<String> mTipsSpares;
+
+    private Handler mHandler;
+
+    public BloodPresenter(Context context) {
+        this.mContext = context;
+        mTipsSpares = new SparseArray<String>();
+        mTipsSpares.put(BloodState.BLOOD_NULL_TIPS, "");
+        mTipsSpares.put(BloodState.BLOOD_NOT_DEVICE, context.getString(R.string.tips_not_device));
+        mTipsSpares.put(BloodState.BLOOD_PLEASE_QUIET, context.getString(R.string.tips_quiet_please));
+        mTipsSpares.put(BloodState.BLOOD_START_CHECK, context.getString(R.string.tips_start_check));
+        mTipsSpares.put(BloodState.BLOOD_NOT_WEAR, context.getString(R.string.tips_not_wear));
+        mTipsSpares.put(BloodState.BLOOD_ING_CHECK, context.getString(R.string.tips_ing_check));
+        mTipsSpares.put(BloodState.BLOOD_STOP_CHECK, context.getString(R.string.tips_stop_check));
+        mTipsSpares.put(BloodState.BLOOD_NOT_DATA_CHECK, context.getString(R.string.tips_not_data_check));
+
+        mObsBloodValue = new MutableLiveData<>();
+        mObsTipsInfo = new MutableLiveData<>();
+        mObsCheckEnd = new MutableLiveData<>();
+
+        mHandler = new Handler();
+    }
+
+    public MutableLiveData<String[]> getObsBloodValue() {
+        return mObsBloodValue;
+    }
+
+    public MutableLiveData<String[]> getObsTipsInfo() {
+        return mObsTipsInfo;
+    }
+
+    public MutableLiveData<Integer> getObsCheckEnd() {
+        return mObsCheckEnd;
+    }
+
+    public void registerScreenReceiver() {
+
+        ServiceUtil.bindServiceUtil(mContext, "com.ic.action.WORK_SERVICE", connection, 102);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        mContext.registerReceiver(screenOnOffReceiver, intentFilter);
+    }
+
+    public void unregisterScreenReceiver() {
+
+        mContext.unregisterReceiver(screenOnOffReceiver);
+
+        try {
+            mService.unregisterCallback(bloodInterface);
+            ServiceUtil.unBindServiceUtil(mContext, connection);
+        } catch (Exception e) {
+            IcLog.e(TAG, e.getMessage());
+        }finally {
+            mService=null;
+            bloodInterface=null;
+        }
+
+        connection = null;
+        mHandler = null;
+    }
+
+    public void startCheckBlood() {
+        if (mService != null) {
+            try {
+                mService.startCheckBlood();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+    public void stopCheckBlood() {
+        if (mService != null) {
+            try {
+                mService.stopCheckBlood();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+
+    private void updateTipsInfo(String t, String b) {
+        if (mObsTipsInfo != null) {
+            mObsTipsInfo.postValue(new String[]{t, b});
+        }
+    }
+
+    private void updateBoolValue(int lv, int hv, int hh) {
+        if (mObsBloodValue != null) {
+            mObsBloodValue.postValue(new String[]{
+                    String.valueOf(hv),
+                    String.valueOf(lv),
+                    String.valueOf(hh)});
+        }
+    }
+
+    private BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                IcLog.i(TAG, "灭屏了");
+                mObsCheckEnd.setValue(2);
+            }
+        }
+    };
+
+
+    private ServiceConnection connection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mService = IBloodRemoteService.Stub.asInterface(service);
+                mService.registerCallback(bloodInterface);
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    };
+
+    private IBloodAidlInterface.Stub bloodInterface = new IBloodAidlInterface.Stub() {
+        @Override
+        public void updateBloodCheckResult(int high, int low, int heart) throws RemoteException {
+            updateBoolValue(high, low, heart);
+            mHandler.postDelayed(() -> mObsCheckEnd.postValue(1), 10 * 1000);
+        }
+
+        @Override
+        public void updateBloodCheckTips(int top, int bottom) throws RemoteException {
+            updateTipsInfo(mTipsSpares.get(top), mTipsSpares.get(bottom));
+        }
+    };
+
+
+}
diff --git a/app/src/main/java/com/ic/hardware/presenter/PedometerPresenter.java b/app/src/main/java/com/ic/hardware/presenter/PedometerPresenter.java
new file mode 100644 (file)
index 0000000..f71180b
--- /dev/null
@@ -0,0 +1,127 @@
+package com.ic.hardware.presenter;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.lifecycle.MutableLiveData;
+
+import com.ic.aidl.ped.IPedAidlInterface;
+import com.ic.aidl.ped.IPedRemoteService;
+import com.ic.aidl.ped.RecordParcelable;
+import com.ic.hardware.util.IcLog;
+import com.ic.hardware.util.ServiceUtil;
+
+import java.util.List;
+
+public class PedometerPresenter {
+    private static final String TAG = "PedometerPresenter";
+
+    private Context mContext;
+
+    private IPedRemoteService mService;
+    private MutableLiveData<Integer> mObsPedCount;
+    private MutableLiveData<List<RecordParcelable>> mObsPedRecord;
+
+    public PedometerPresenter(Context context){
+        mContext=context;
+        mObsPedCount = new MutableLiveData<>();
+        mObsPedRecord = new MutableLiveData<>();
+    }
+
+    public MutableLiveData<Integer> getObsPedCount() {
+        return mObsPedCount;
+    }
+
+    public MutableLiveData<List<RecordParcelable>> getObsPedRecord() {
+        return mObsPedRecord;
+    }
+
+    public void bindService(){
+        ServiceUtil.bindServiceUtil(mContext, "com.ic.action.WORK_SERVICE", connection, 103);
+    }
+
+    public void unbindService(){
+        try {
+            mService.unregisterCallback(pedInterface);
+            ServiceUtil.unBindServiceUtil(mContext,connection);
+        } catch (Exception e) {
+            IcLog.e(TAG,"unbindService");
+        }finally {
+            mService=null;
+            pedInterface=null;
+        }
+    }
+
+    public void queryPedCount() {
+        if (mService != null) {
+            try {
+                mService.queryPedCount();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+    public void queryPedRecord() {
+        if (mService != null) {
+            try {
+                mService.queryPedRecord();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+    public void syncPedHeightSetting(int height){
+        if (mService != null) {
+            try {
+                mService.syncPedHeightSetting(height);
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+    public void syncPedWeightSetting(int weight){
+        if (mService != null) {
+            try {
+                mService.syncPedWeightSetting(weight);
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+
+    private ServiceConnection connection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mService = IPedRemoteService.Stub.asInterface(service);
+                mService.registerCallback(pedInterface);
+                queryPedCount();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    };
+
+    private IPedAidlInterface.Stub pedInterface = new IPedAidlInterface.Stub() {
+        @Override
+        public void updatePedCount(int count) throws RemoteException {
+            mObsPedCount.postValue(count);
+        }
+
+        @Override
+        public void updatePedRecord(List<RecordParcelable> record) throws RemoteException {
+            mObsPedRecord.postValue(record);
+        }
+    };
+}
diff --git a/app/src/main/java/com/ic/hardware/presenter/WeatherPresenter.java b/app/src/main/java/com/ic/hardware/presenter/WeatherPresenter.java
new file mode 100644 (file)
index 0000000..a99a265
--- /dev/null
@@ -0,0 +1,84 @@
+package com.ic.hardware.presenter;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.lifecycle.MutableLiveData;
+
+import com.ic.aidl.weather.IWeatherAidlInterface;
+import com.ic.aidl.weather.IWeatherRemoteService;
+import com.ic.aidl.weather.WeatherParcelable;
+import com.ic.hardware.util.IcLog;
+import com.ic.hardware.util.ServiceUtil;
+
+public class WeatherPresenter {
+    private static final String TAG = "WeatherPresenter";
+
+    private Context mContext;
+
+    private MutableLiveData<WeatherParcelable> mObsWeatherInfo;
+    private IWeatherRemoteService mService;
+
+    public WeatherPresenter(Context context) {
+        this.mContext =context;
+
+        mObsWeatherInfo = new MutableLiveData<>();
+    }
+
+    public MutableLiveData<WeatherParcelable> getObsWeatherInfo() {
+        return mObsWeatherInfo;
+    }
+
+    public void bindService(){
+        ServiceUtil.bindServiceUtil(mContext, "com.ic.action.WORK_SERVICE", connection, 101);
+    }
+
+    public void unbindService(){
+        try {
+            mService.unregisterCallback(weatherInterface);
+            ServiceUtil.unBindServiceUtil(mContext,connection);
+        } catch (Exception e) {
+            IcLog.e(TAG, e.getMessage());
+        }finally {
+            mService=null;
+            weatherInterface=null;
+        }
+    }
+
+    private void queryWeatherInfo() {
+        if (mService != null) {
+            try {
+                mService.queryWeatherInfo();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+    }
+
+    private ServiceConnection connection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mService = IWeatherRemoteService.Stub.asInterface(service);
+                mService.registerCallback(weatherInterface);
+                queryWeatherInfo();
+            } catch (Exception e) {
+                IcLog.e(TAG, e.getMessage());
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    };
+
+    private IWeatherAidlInterface.Stub weatherInterface = new IWeatherAidlInterface.Stub() {
+        @Override
+        public void updateCurrWeather(WeatherParcelable weather) throws RemoteException {
+            mObsWeatherInfo.postValue(weather);
+        }
+    };
+}
diff --git a/app/src/main/java/com/ic/hardware/util/IcLog.java b/app/src/main/java/com/ic/hardware/util/IcLog.java
new file mode 100644 (file)
index 0000000..5992f07
--- /dev/null
@@ -0,0 +1,59 @@
+package com.ic.hardware.util;
+
+import android.util.Log;
+
+import com.ic.hardware.BuildConfig;
+
+/**
+ * @author RyanLee
+ * @date 2018/2/2
+ */
+
+public class IcLog {
+
+    private static final String DOT = ".";
+    private static boolean isDebug = BuildConfig.DEBUG;
+    private static String tagHead = "";
+
+    public static void v(String tag, String msg) {
+        if (isDebug) {
+            Log.v(mergeHeadTag(tag), msg);
+        }
+    }
+
+    public static void d(String tag, String msg) {
+        if (isDebug) {
+            Log.d(mergeHeadTag(tag), msg);
+        }
+    }
+
+    public static void i(String tag, String msg) {
+        if (isDebug) {
+            Log.i(mergeHeadTag(tag), msg);
+        }
+    }
+
+    public static void w(String tag, String msg) {
+        if (isDebug) {
+            Log.w(mergeHeadTag(tag), msg);
+        }
+    }
+
+    public static void e(String tag, String msg) {
+        if (isDebug) {
+            Log.e(mergeHeadTag(tag), msg);
+        }
+    }
+
+    public static void setDebug(boolean debug) {
+        isDebug = debug;
+    }
+
+    public static void setLogHead(String head) {
+        tagHead = head + DOT;
+    }
+
+    private static String mergeHeadTag(final String tag) {
+        return tagHead + tag;
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/util/SPUtils.java b/app/src/main/java/com/ic/hardware/util/SPUtils.java
new file mode 100644 (file)
index 0000000..6d5a7bc
--- /dev/null
@@ -0,0 +1,52 @@
+package com.ic.hardware.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class SPUtils {
+
+    private static final String Ped_Height = "Ped_Height";
+    private static final String Ped_Weight = "Ped_Weight";
+    private static final String Ped_Target = "Ped_Target";
+
+
+    private static SharedPreferences getSp(Context context) {
+        return context.getSharedPreferences("IcHardwarePed", Context.MODE_PRIVATE);
+    }
+
+    public static int getPedHeightSP(Context context) {
+        return getSp(context).getInt(Ped_Height, 170);
+    }
+
+    public static int getPedWighttSP(Context context) {
+        return getSp(context).getInt(Ped_Weight, 75);
+    }
+
+    public static int getPedTargetSP(Context context) {
+        return getSp(context).getInt(Ped_Target, 10000);
+    }
+
+    public static void setPedHeightSP(Context context,int height) {
+        SharedPreferences.Editor editor = getSp(context).edit();
+        if (editor != null) {
+            editor.putInt(Ped_Weight, height);
+            editor.apply();
+        }
+    }
+
+    public static void setPedWighttSP(Context context,int weight) {
+        SharedPreferences.Editor editor = getSp(context).edit();
+        if (editor != null) {
+            editor.putInt(Ped_Weight, weight);
+            editor.apply();
+        }
+    }
+
+    public static void setPedTargetSP(Context context,int target) {
+        SharedPreferences.Editor editor = getSp(context).edit();
+        if (editor != null) {
+            editor.putInt(Ped_Target, target);
+            editor.apply();
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/util/ServiceUtil.java b/app/src/main/java/com/ic/hardware/util/ServiceUtil.java
new file mode 100644 (file)
index 0000000..28bc27d
--- /dev/null
@@ -0,0 +1,46 @@
+package com.ic.hardware.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+
+import java.util.List;
+
+public class ServiceUtil {
+
+    public static void startServiceUtil(Context context , String serviceAction, String packageName) {
+        Intent intent = getExplicitIntent(context, new Intent(serviceAction));
+        if(intent != null){
+            intent.setPackage(packageName);
+            context.startService(intent);
+        }
+    }
+
+    public static void bindServiceUtil(Context context , String action, ServiceConnection connection,int type) {
+        Intent in = getExplicitIntent(context, new Intent(action));
+        if(in != null){
+            in.putExtra("REQ_TYPE",type);
+            context.bindService(in, connection, 0);
+        }
+    }
+
+    public static void unBindServiceUtil(Context context ,ServiceConnection connection) {
+        context.unbindService(connection);
+    }
+
+    private static Intent getExplicitIntent(Context context, Intent impl) {
+        List<ResolveInfo> resolveInfo = context.getPackageManager().queryIntentServices(impl, 0);
+
+        if (resolveInfo.size() != 1) return null;
+
+        Intent intent = new Intent(impl);
+        ServiceInfo info = resolveInfo.get(0).serviceInfo;
+        if (info == null) return null;
+        intent.setComponent(new ComponentName(info.packageName, info.name));
+
+        return intent;
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/WidgetImageTextLayout.java b/app/src/main/java/com/ic/hardware/widget/WidgetImageTextLayout.java
new file mode 100644 (file)
index 0000000..4a9ef4f
--- /dev/null
@@ -0,0 +1,53 @@
+package com.ic.hardware.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.databinding.BindingAdapter;
+
+import com.ic.hardware.R;
+
+public class WidgetImageTextLayout extends LinearLayout {
+
+    private TextView mValueTv;
+    private TextView mUnitTv;
+    private ImageView mIconIv;
+
+    public WidgetImageTextLayout(Context context) {
+        this(context, null);
+    }
+
+    public WidgetImageTextLayout(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetImageTextLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        View view = View.inflate(context, R.layout.widget_image_text_layout, this);
+
+        mValueTv = (TextView) view.findViewById(R.id.widget_image_value);
+        mUnitTv = (TextView) view.findViewById(R.id.widget_image_unit);
+        mIconIv = (ImageView) view.findViewById(R.id.widget_image_icon);
+    }
+
+    private void updateWidgetImageText(String value, String unit, int iconResId,int color) {
+        mValueTv.setText(value);
+        mUnitTv.setText(unit);
+        mIconIv.setImageResource(iconResId);
+        mValueTv.setTextColor(color);
+    }
+
+    @BindingAdapter(value = {"icApp:widgetImageValue", "icApp:widgetImageUnit", "icApp:widgetImageIcon","icApp:widgetImageColor"}, requireAll = false)
+    public static void updateTimeText(WidgetImageTextLayout view, String value, String unit, int iconResId,int color) {
+        if (view != null) {
+            view.updateWidgetImageText(value, unit, iconResId,color);
+        }
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/picker/NumericWheelAdapter.java b/app/src/main/java/com/ic/hardware/widget/picker/NumericWheelAdapter.java
new file mode 100644 (file)
index 0000000..3bee5c5
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright 2010 Yuri Kanivets
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.ic.hardware.widget.picker;
+
+/**
+ * Numeric Wheel adapter.
+ */
+public class NumericWheelAdapter implements WheelAdapter {
+
+       /** The default min value */
+       public static final int DEFAULT_MAX_VALUE = 9;
+
+       /** The default max value */
+       private static final int DEFAULT_MIN_VALUE = 0;
+
+       // Values
+       private int minValue;
+       private int maxValue;
+
+       // format
+       private String format;
+
+       /**
+        * Default constructor
+        */
+       public NumericWheelAdapter() {
+               this(DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE);
+       }
+
+       /**
+        * Constructor
+        * 
+        * @param minValue
+        *            the wheel min value
+        * @param maxValue
+        *            the wheel max value
+        */
+       public NumericWheelAdapter(int minValue, int maxValue) {
+               this(minValue, maxValue, null);
+       }
+
+       /**
+        * Constructor
+        * 
+        * @param minValue
+        *            the wheel min value
+        * @param maxValue
+        *            the wheel max value
+        * @param format
+        *            the format string
+        */
+       public NumericWheelAdapter(int minValue, int maxValue, String format) {
+               this.minValue = minValue;
+               this.maxValue = maxValue;
+               this.format = format;
+       }
+
+       @Override
+       public String getItem(int index) {
+               if (index >= 0 && index < getItemsCount()) {
+                       int value = minValue + index;
+                       return format != null ? String.format(format, value) : Integer
+                                       .toString(value);
+               }
+               return null;
+       }
+
+       @Override
+       public int getItemsCount() {
+               return maxValue - minValue + 1;
+       }
+
+       @Override
+       public int getMaximumLength() {
+               int max = Math.max(Math.abs(maxValue), Math.abs(minValue));
+               int maxLen = Integer.toString(max).length();
+               if (minValue < 0) {
+                       maxLen++;
+               }
+               return maxLen;
+       }
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/picker/OnWheelChangedListener.java b/app/src/main/java/com/ic/hardware/widget/picker/OnWheelChangedListener.java
new file mode 100644 (file)
index 0000000..54aaa58
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2010 Yuri Kanivets
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.ic.hardware.widget.picker;
+
+/**
+ * Wheel changed listener interface.
+ * <p>
+ * The currentItemChanged() method is called whenever current wheel positions is
+ * changed:
+ * <li>New Wheel position is set
+ * <li>Wheel view is scrolled
+ */
+public interface OnWheelChangedListener {
+       /**
+        * Callback method to be invoked when current item changed
+        * 
+        * @param wheel
+        *            the wheel view whose state has changed
+        * @param oldValue
+        *            the old value of current item
+        * @param newValue
+        *            the new value of current item
+        */
+       void onChanged(WheelView wheel, int oldValue, int newValue);
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/picker/OnWheelScrollListener.java b/app/src/main/java/com/ic/hardware/widget/picker/OnWheelScrollListener.java
new file mode 100644 (file)
index 0000000..56b5299
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  Copyright 2010 Yuri Kanivets
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.ic.hardware.widget.picker;
+
+/**
+ * Wheel scrolled listener interface.
+ */
+public interface OnWheelScrollListener {
+       /**
+        * Callback method to be invoked when scrolling started.
+        * 
+        * @param wheel
+        *            the wheel view whose state has changed.
+        */
+       void onScrollingStarted(WheelView wheel);
+
+       /**
+        * Callback method to be invoked when scrolling ended.
+        * 
+        * @param wheel
+        *            the wheel view whose state has changed.
+        */
+       void onScrollingFinished(WheelView wheel);
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/picker/WheelAdapter.java b/app/src/main/java/com/ic/hardware/widget/picker/WheelAdapter.java
new file mode 100644 (file)
index 0000000..d1e5b7d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ *  Copyright 2010 Yuri Kanivets
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.ic.hardware.widget.picker;
+
+public interface WheelAdapter {
+       /**
+        * Gets items count
+        * 
+        * @return the count of wheel items
+        */
+    int getItemsCount();
+
+       /**
+        * Gets a wheel item by index.
+        * 
+        * @param index
+        *            the item index
+        * @return the wheel item text or null
+        */
+    String getItem(int index);
+
+       /**
+        * Gets maximum item length. It is used to determine the wheel width. If -1
+        * is returned there will be used the default wheel width.
+        * 
+        * @return the maximum item length or -1
+        */
+    int getMaximumLength();
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/picker/WheelView.java b/app/src/main/java/com/ic/hardware/widget/picker/WheelView.java
new file mode 100644 (file)
index 0000000..dcd488f
--- /dev/null
@@ -0,0 +1,1041 @@
+/*
+ *  Android Wheel Control.
+ *  https://code.google.com/p/android-wheel/
+ *
+ *  Copyright 2010 Yuri Kanivets
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package com.ic.hardware.widget.picker;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.GradientDrawable.Orientation;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.widget.Scroller;
+
+import com.ic.hardware.R;
+
+import java.util.LinkedList;
+import java.util.List;
+
+
+/**
+ * Numeric wheel view.
+ *
+ * @author Yuri Kanivets
+ */
+public class WheelView extends View {
+    /**
+     * Scrolling duration
+     */
+    private static final int SCROLLING_DURATION = 400;
+
+    /**
+     * Minimum delta for scrolling
+     */
+    private static final int MIN_DELTA_FOR_SCROLLING = 1;
+
+    /**
+     * Current value & label text color
+     */
+//     private static final int VALUE_TEXT_COLOR = 0xF0000000;
+    private static final int VALUE_TEXT_COLOR = 0xFFFFA70D;
+
+    /**
+     * Items text color
+     */
+//     private static final int ITEMS_TEXT_COLOR = 0xFF000000;
+    private static final int ITEMS_TEXT_COLOR = 0xFFA9A9A9;
+
+    /**
+     * Top and bottom shadows colors
+     */
+    private static final int[] SHADOWS_COLORS = new int[]{0xFF111111,
+            0x00AAAAAA, 0x00AAAAAA};
+
+    /**
+     * Additional items height (is added to standard text item height)
+     */
+    private static final int ADDITIONAL_ITEM_HEIGHT = 5;
+
+    /**
+     * Text size
+     */
+    public int TEXT_SIZE;
+    public int ITEM_TEXT_SIZE;
+
+    /**
+     * Top and bottom items offset (to hide that)
+     */
+    private final int ITEM_OFFSET = TEXT_SIZE / DEF_VISIBLE_ITEMS;
+
+    /**
+     * Additional width for items layout
+     */
+    private static final int ADDITIONAL_ITEMS_SPACE = 10;
+
+    /**
+     * Label offset
+     */
+    private static final int LABEL_OFFSET = 5;
+
+    /**
+     * Left and right padding value
+     */
+    private static final int PADDING = 10;
+
+    /**
+     * Default count of visible items
+     */
+    private static final int DEF_VISIBLE_ITEMS = 3;
+
+    // Wheel Values
+    private WheelAdapter adapter = null;
+    private int currentItem = 0;
+
+    // Widths
+    private int itemsWidth = 0;
+    private int labelWidth = 0;
+
+    // Count of visible items
+    private int visibleItems = DEF_VISIBLE_ITEMS;
+
+    // Item height
+    private int itemHeight = 0;
+
+    // Text paints
+    private TextPaint itemsPaint;
+    private TextPaint valuePaint;
+    private TextPaint labelPaint;
+
+    // Layouts
+    private StaticLayout itemsLayout;
+    private StaticLayout labelLayout;
+    private StaticLayout valueLayout;
+
+    // Label & background
+    private String label;
+    private Drawable centerDrawable;
+
+    // Shadows drawables
+    private GradientDrawable topShadow;
+    private GradientDrawable bottomShadow;
+
+    // Scrolling
+    private boolean isScrollingPerformed;
+    private int scrollingOffset;
+
+    // Scrolling animation
+    private GestureDetector gestureDetector;
+    private Scroller scroller;
+    private int lastScrollY;
+
+    // Cyclic
+    boolean isCyclic = false;
+
+    // Listeners
+    private List<OnWheelChangedListener> changingListeners = new LinkedList<OnWheelChangedListener>();
+    private List<OnWheelScrollListener> scrollingListeners = new LinkedList<OnWheelScrollListener>();
+
+    /**
+     * Constructor
+     */
+    public WheelView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initData(context);
+    }
+
+    /**
+     * Constructor
+     */
+    public WheelView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initData(context);
+    }
+
+    /**
+     * Constructor
+     */
+    public WheelView(Context context) {
+        super(context);
+        initData(context);
+    }
+
+    /**
+     * Initializes class data
+     *
+     * @param context the context
+     */
+    private void initData(Context context) {
+        gestureDetector = new GestureDetector(context, gestureListener);
+        gestureDetector.setIsLongpressEnabled(false);
+
+        scroller = new Scroller(context);
+    }
+
+    /**
+     * Gets wheel adapter
+     *
+     * @return the adapter
+     */
+    public WheelAdapter getAdapter() {
+        return adapter;
+    }
+
+    /**
+     * Sets wheel adapter
+     *
+     * @param adapter the new wheel adapter
+     */
+    public void setAdapter(WheelAdapter adapter) {
+        this.adapter = adapter;
+        invalidateLayouts();
+        invalidate();
+    }
+
+    /**
+     * Set the the specified scrolling interpolator
+     *
+     * @param interpolator the interpolator
+     */
+    public void setInterpolator(Interpolator interpolator) {
+        scroller.forceFinished(true);
+        scroller = new Scroller(getContext(), interpolator);
+    }
+
+    /**
+     * Gets count of visible items
+     *
+     * @return the count of visible items
+     */
+    public int getVisibleItems() {
+        return visibleItems;
+    }
+
+    /**
+     * Sets count of visible items
+     *
+     * @param count the new count
+     */
+    public void setVisibleItems(int count) {
+        visibleItems = count;
+        invalidate();
+    }
+
+    /**
+     * Gets label
+     *
+     * @return the label
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Sets label
+     *
+     * @param newLabel the label to set
+     */
+    public void setLabel(String newLabel) {
+        if (label == null || !label.equals(newLabel)) {
+            label = newLabel;
+            labelLayout = null;
+            invalidate();
+        }
+    }
+
+    /**
+     * Adds wheel changing listener
+     *
+     * @param listener the listener
+     */
+    public void addChangingListener(OnWheelChangedListener listener) {
+        changingListeners.add(listener);
+    }
+
+    /**
+     * Removes wheel changing listener
+     *
+     * @param listener the listener
+     */
+    public void removeChangingListener(OnWheelChangedListener listener) {
+        changingListeners.remove(listener);
+    }
+
+    /**
+     * Notifies changing listeners
+     *
+     * @param oldValue the old wheel value
+     * @param newValue the new wheel value
+     */
+    protected void notifyChangingListeners(int oldValue, int newValue) {
+        for (OnWheelChangedListener listener : changingListeners) {
+            listener.onChanged(this, oldValue, newValue);
+        }
+    }
+
+    /**
+     * Adds wheel scrolling listener
+     *
+     * @param listener the listener
+     */
+    public void addScrollingListener(OnWheelScrollListener listener) {
+        scrollingListeners.add(listener);
+    }
+
+    /**
+     * Removes wheel scrolling listener
+     *
+     * @param listener the listener
+     */
+    public void removeScrollingListener(OnWheelScrollListener listener) {
+        scrollingListeners.remove(listener);
+    }
+
+    /**
+     * Notifies listeners about starting scrolling
+     */
+    protected void notifyScrollingListenersAboutStart() {
+        for (OnWheelScrollListener listener : scrollingListeners) {
+            listener.onScrollingStarted(this);
+        }
+    }
+
+    /**
+     * Notifies listeners about ending scrolling
+     */
+    protected void notifyScrollingListenersAboutEnd() {
+        for (OnWheelScrollListener listener : scrollingListeners) {
+            listener.onScrollingFinished(this);
+        }
+    }
+
+    /**
+     * Gets current value
+     *
+     * @return the current value
+     */
+    public int getCurrentItem() {
+        return currentItem;
+    }
+
+    /**
+     * Sets the current item. Does nothing when index is wrong.
+     *
+     * @param index    the item index
+     * @param animated the animation flag
+     */
+    public void setCurrentItem(int index, boolean animated) {
+        if (adapter == null || adapter.getItemsCount() == 0) {
+            return; // throw?
+        }
+        if (index < 0 || index >= adapter.getItemsCount()) {
+            if (isCyclic) {
+                while (index < 0) {
+                    index += adapter.getItemsCount();
+                }
+                index %= adapter.getItemsCount();
+            } else {
+                return; // throw?
+            }
+        }
+        if (index != currentItem) {
+            if (animated) {
+                scroll(index - currentItem, SCROLLING_DURATION);
+            } else {
+                invalidateLayouts();
+
+                int old = currentItem;
+                currentItem = index;
+
+                notifyChangingListeners(old, currentItem);
+
+                invalidate();
+            }
+        }
+    }
+
+    /**
+     * Sets the current item w/o animation. Does nothing when index is wrong.
+     *
+     * @param index the item index
+     */
+    public void setCurrentItem(int index) {
+        setCurrentItem(index, false);
+    }
+
+    /**
+     * Tests if wheel is cyclic. That means before the 1st item there is shown
+     * the last one
+     *
+     * @return true if wheel is cyclic
+     */
+    public boolean isCyclic() {
+        return isCyclic;
+    }
+
+    /**
+     * Set wheel cyclic flag
+     *
+     * @param isCyclic the flag to set
+     */
+    public void setCyclic(boolean isCyclic) {
+        this.isCyclic = isCyclic;
+
+        invalidate();
+        invalidateLayouts();
+    }
+
+    /**
+     * Invalidates layouts
+     */
+    private void invalidateLayouts() {
+        itemsLayout = null;
+        valueLayout = null;
+        scrollingOffset = 0;
+    }
+
+    /**
+     * Initializes resources
+     */
+    private void initResourcesIfNecessary() {
+        if (itemsPaint == null) {
+            itemsPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG
+                    | Paint.FAKE_BOLD_TEXT_FLAG);
+            // itemsPaint.density = getResources().getDisplayMetrics().density;
+            itemsPaint.setTextSize(TEXT_SIZE);
+//                     itemsPaint.setTextSize(ITEM_TEXT_SIZE);
+        }
+
+        if (labelPaint == null) {
+            labelPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+            // labelPaint.density = getResources().getDisplayMetrics().density;
+            labelPaint.setTextSize(ITEM_TEXT_SIZE);
+        }
+
+        if (valuePaint == null) {
+            valuePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG
+                    | Paint.FAKE_BOLD_TEXT_FLAG | Paint.DITHER_FLAG);
+            // valuePaint.density = getResources().getDisplayMetrics().density;
+            valuePaint.setTextSize(TEXT_SIZE);
+            valuePaint.setShadowLayer(0.1f, 0, 0.1f, 0xFFC0C0C0);
+        }
+
+        if (centerDrawable == null) {
+            centerDrawable = getContext().getResources().getDrawable(
+                    R.drawable.wheel_val);
+        }
+
+        if (topShadow == null) {
+            topShadow = new GradientDrawable(Orientation.TOP_BOTTOM,
+                    SHADOWS_COLORS);
+        }
+
+        if (bottomShadow == null) {
+            bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP,
+                    SHADOWS_COLORS);
+        }
+
+//             setBackgroundResource(R.drawable.wheel_bg);
+        setBackgroundResource(R.color.transparent);
+    }
+
+    /**
+     * Calculates desired height for layout
+     *
+     * @param layout the source layout
+     * @return the desired layout height
+     */
+    private int getDesiredHeight(Layout layout) {
+        if (layout == null) {
+            return 0;
+        }
+
+        int desired = getItemHeight() * visibleItems - ITEM_OFFSET * 2
+                - ADDITIONAL_ITEM_HEIGHT;
+
+        // Check against our minimum height
+        desired = Math.max(desired, getSuggestedMinimumHeight());
+
+        return desired;
+    }
+
+    /**
+     * Returns text item by index
+     *
+     * @param index the item index
+     * @return the item or null
+     */
+    private String getTextItem(int index) {
+        if (adapter == null || adapter.getItemsCount() == 0) {
+            return null;
+        }
+        int count = adapter.getItemsCount();
+        if ((index < 0 || index >= count) && !isCyclic) {
+            return null;
+        } else {
+            while (index < 0) {
+                index = count + index;
+            }
+        }
+
+        index %= count;
+        return adapter.getItem(index);
+    }
+
+    /**
+     * Builds text depending on current value
+     *
+     * @param useCurrentValue
+     * @return the text
+     */
+    private String buildText(boolean useCurrentValue) {
+        StringBuilder itemsText = new StringBuilder();
+        int addItems = visibleItems / 2 + 1;
+
+        for (int i = currentItem - addItems; i <= currentItem + addItems; i++) {
+            if (useCurrentValue || i != currentItem) {
+                String text = getTextItem(i);
+                if (text != null) {
+                    itemsText.append(text);
+                }
+            }
+            if (i < currentItem + addItems) {
+                itemsText.append("\n");
+            }
+        }
+
+        return itemsText.toString();
+    }
+
+    /**
+     * Returns the max item length that can be present
+     *
+     * @return the max length
+     */
+    private int getMaxTextLength() {
+        WheelAdapter adapter = getAdapter();
+        if (adapter == null) {
+            return 0;
+        }
+
+        int adapterLength = adapter.getMaximumLength();
+        if (adapterLength > 0) {
+            return adapterLength;
+        }
+
+        String maxText = null;
+        int addItems = visibleItems / 2;
+        for (int i = Math.max(currentItem - addItems, 0); i < Math.min(
+                currentItem + visibleItems, adapter.getItemsCount()); i++) {
+            String text = adapter.getItem(i);
+            if (text != null
+                    && (maxText == null || maxText.length() < text.length())) {
+                maxText = text;
+            }
+        }
+
+        return maxText != null ? maxText.length() : 0;
+    }
+
+    /**
+     * Returns height of wheel item
+     *
+     * @return the item height
+     */
+    private int getItemHeight() {
+        if (itemHeight != 0) {
+            return itemHeight;
+        } else if (itemsLayout != null && itemsLayout.getLineCount() > 2) {
+            itemHeight = itemsLayout.getLineTop(2) - itemsLayout.getLineTop(1);
+            return itemHeight;
+        }
+
+        return getHeight() / visibleItems;
+    }
+
+    /**
+     * Calculates control width and creates text layouts
+     *
+     * @param widthSize the input layout width
+     * @param mode      the layout mode
+     * @return the calculated control width
+     */
+    private int calculateLayoutWidth(int widthSize, int mode) {
+        initResourcesIfNecessary();
+
+        int width = widthSize;
+
+        int maxLength = getMaxTextLength();
+        if (maxLength > 0) {
+//                     float textWidth = FloatMath.ceil(Layout.getDesiredWidth("0",
+//                                     itemsPaint));
+            float textWidth = (float) Math.ceil(Layout.getDesiredWidth("0",
+                    itemsPaint));
+            itemsWidth = (int) (maxLength * textWidth);
+        } else {
+            itemsWidth = 0;
+        }
+        itemsWidth += ADDITIONAL_ITEMS_SPACE; // make it some more
+
+        labelWidth = 0;
+        if (label != null && label.length() > 0) {
+//                     labelWidth = (int) FloatMath.ceil(Layout.getDesiredWidth(label,
+//                                     valuePaint));
+            labelWidth = (int) Math.ceil(Layout.getDesiredWidth(label,
+                    labelPaint));
+        }
+
+        boolean recalculate = false;
+        if (mode == MeasureSpec.EXACTLY) {
+            width = widthSize;
+            recalculate = true;
+        } else {
+            width = itemsWidth + labelWidth + 2 * PADDING;
+            if (labelWidth > 0) {
+                width += LABEL_OFFSET;
+            }
+
+            // Check against our minimum width
+            width = Math.max(width, getSuggestedMinimumWidth());
+
+            if (mode == MeasureSpec.AT_MOST && widthSize < width) {
+                width = widthSize;
+                recalculate = true;
+            }
+        }
+
+        if (recalculate) {
+            // recalculate width
+            int pureWidth = width - LABEL_OFFSET - 2 * PADDING;
+            if (pureWidth <= 0) {
+                itemsWidth = labelWidth = 0;
+            }
+            if (labelWidth > 0) {
+                double newWidthItems = (double) itemsWidth * pureWidth
+                        / (itemsWidth + labelWidth);
+                itemsWidth = (int) newWidthItems;
+                labelWidth = pureWidth - itemsWidth;
+            } else {
+                itemsWidth = pureWidth + LABEL_OFFSET; // no label
+            }
+        }
+
+        if (itemsWidth > 0) {
+            createLayouts(itemsWidth, labelWidth);
+        }
+
+        return width;
+    }
+
+    /**
+     * Creates layouts
+     *
+     * @param widthItems width of items layout
+     * @param widthLabel width of label layout
+     */
+    private void createLayouts(int widthItems, int widthLabel) {
+        if (itemsLayout == null || itemsLayout.getWidth() > widthItems) {
+            itemsLayout = new StaticLayout(buildText(isScrollingPerformed),
+                    itemsPaint, widthItems,
+                    /* widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE :*/ Layout.Alignment.ALIGN_CENTER,
+                    1,
+                    ADDITIONAL_ITEM_HEIGHT, false);
+        } else {
+            itemsLayout.increaseWidthTo(widthItems);
+        }
+
+        if (!isScrollingPerformed
+                && (valueLayout == null || valueLayout.getWidth() > widthItems)) {
+            String text = getAdapter() != null ? getAdapter().getItem(
+                    currentItem) : null;
+            valueLayout = new StaticLayout(text != null ? text : "",
+                    valuePaint, widthItems,
+                    /* widthLabel > 0 ? Layout.Alignment.ALIGN_OPPOSITE :*/ Layout.Alignment.ALIGN_CENTER,
+                    1,
+                    ADDITIONAL_ITEM_HEIGHT, false);
+        } else if (isScrollingPerformed) {
+            valueLayout = null;
+        } else {
+            valueLayout.increaseWidthTo(widthItems);
+        }
+
+        if (widthLabel > 0) {
+            if (labelLayout == null || labelLayout.getWidth() > widthLabel) {
+//                TextPaint labelPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FAKE_BOLD_TEXT_FLAG);
+//                // labelPaint.density = getResources().getDisplayMetrics().density;
+//                labelPaint.setTextSize(ITEM_TEXT_SIZE);
+                labelLayout = new StaticLayout(label, /* valuePaint */ labelPaint, widthLabel,
+                        Layout.Alignment.ALIGN_NORMAL, 1,
+                        ADDITIONAL_ITEM_HEIGHT, false);
+            } else {
+                labelLayout.increaseWidthTo(widthLabel);
+            }
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+        int width = calculateLayoutWidth(widthSize, widthMode);
+
+        int height;
+        if (heightMode == MeasureSpec.EXACTLY) {
+            height = heightSize;
+        } else {
+            height = getDesiredHeight(itemsLayout);
+
+            if (heightMode == MeasureSpec.AT_MOST) {
+                height = Math.min(height, heightSize);
+            }
+        }
+
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (itemsLayout == null) {
+            if (itemsWidth == 0) {
+                calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
+            } else {
+                createLayouts(itemsWidth, labelWidth);
+            }
+        }
+
+        if (itemsWidth > 0) {
+            canvas.save();
+            // Skip padding space and hide a part of top and bottom items
+            canvas.translate(PADDING, -ITEM_OFFSET);
+            drawItems(canvas);
+            drawValue(canvas);
+            canvas.restore();
+        }
+
+//             drawCenterRect(canvas);
+//             drawShadows(canvas);
+    }
+
+    /**
+     * Draws shadows on top and bottom of control
+     *
+     * @param canvas the canvas for drawing
+     */
+    private void drawShadows(Canvas canvas) {
+        topShadow.setBounds(0, 0, getWidth(), getHeight() / visibleItems);
+        topShadow.draw(canvas);
+
+        bottomShadow.setBounds(0, getHeight() - getHeight() / visibleItems,
+                getWidth(), getHeight());
+        bottomShadow.draw(canvas);
+    }
+
+    /**
+     * Draws value and label layout
+     *
+     * @param canvas the canvas for drawing
+     */
+    private void drawValue(Canvas canvas) {
+        valuePaint.setColor(VALUE_TEXT_COLOR);
+        valuePaint.drawableState = getDrawableState();
+
+        labelPaint.setColor(ITEMS_TEXT_COLOR);
+        labelPaint.drawableState = getDrawableState();
+
+        Rect bounds = new Rect();
+        int baseLine = itemsLayout.getLineBounds(visibleItems / 2, bounds);
+
+        // draw label
+        if (labelLayout != null) {
+            canvas.save();
+//            canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET,
+//                    bounds.top + ((bounds.height() - labelLayout.getHeight()) / 2));
+            // 确保 Label 与 Value 中线对齐
+            canvas.translate(itemsLayout.getWidth() + LABEL_OFFSET,
+                    baseLine - (labelLayout.getHeight()/2));
+            labelLayout.draw(canvas);
+            canvas.restore();
+        }
+
+        // draw current value
+        if (valueLayout != null) {
+            canvas.save();
+            canvas.translate(0, bounds.top + scrollingOffset);
+            valueLayout.draw(canvas);
+            canvas.restore();
+        }
+    }
+
+    /**
+     * Draws items
+     *
+     * @param canvas the canvas for drawing
+     */
+    private void drawItems(Canvas canvas) {
+        canvas.save();
+
+        int top = itemsLayout.getLineTop(1);
+        canvas.translate(0, -top + scrollingOffset);
+
+        itemsPaint.setColor(ITEMS_TEXT_COLOR);
+        itemsPaint.setTextSize(ITEM_TEXT_SIZE);
+        itemsPaint.drawableState = getDrawableState();
+        itemsLayout.draw(canvas);
+        itemsPaint.setTextSize(TEXT_SIZE);
+
+        canvas.restore();
+    }
+
+    /**
+     * Draws rect for current value
+     *
+     * @param canvas the canvas for drawing
+     */
+    private void drawCenterRect(Canvas canvas) {
+        int center = getHeight() / 2;
+        int offset = getItemHeight() / 2;
+        centerDrawable.setBounds(0, center - offset, getWidth(), center
+                + offset);
+        centerDrawable.draw(canvas);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        WheelAdapter adapter = getAdapter();
+        if (adapter == null) {
+            return true;
+        }
+
+        if (!gestureDetector.onTouchEvent(event)
+                && event.getAction() == MotionEvent.ACTION_UP) {
+            justify();
+        }
+        return true;
+    }
+
+    /**
+     * Scrolls the wheel
+     *
+     * @param delta the scrolling value
+     */
+    private void doScroll(int delta) {
+        scrollingOffset += delta;
+
+        int count = scrollingOffset / getItemHeight();
+        int pos = currentItem - count;
+        if (isCyclic && adapter.getItemsCount() > 0) {
+            // fix position by rotating
+            while (pos < 0) {
+                pos += adapter.getItemsCount();
+            }
+            pos %= adapter.getItemsCount();
+        } else if (isScrollingPerformed) {
+            //
+            if (pos < 0) {
+                count = currentItem;
+                pos = 0;
+            } else if (pos >= adapter.getItemsCount()) {
+                count = currentItem - adapter.getItemsCount() + 1;
+                pos = adapter.getItemsCount() - 1;
+            }
+        } else {
+            // fix position
+            pos = Math.max(pos, 0);
+            pos = Math.min(pos, adapter.getItemsCount() - 1);
+        }
+
+        int offset = scrollingOffset;
+        if (pos != currentItem) {
+            setCurrentItem(pos, false);
+        } else {
+            invalidate();
+        }
+
+        // update offset
+        scrollingOffset = offset - count * getItemHeight();
+        if (scrollingOffset > getHeight()) {
+            scrollingOffset = scrollingOffset % getHeight() + getHeight();
+        }
+    }
+
+    // gesture listener
+    private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
+        public boolean onDown(MotionEvent e) {
+            if (isScrollingPerformed) {
+                scroller.forceFinished(true);
+                clearMessages();
+                return true;
+            }
+            return false;
+        }
+
+        public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                                float distanceX, float distanceY) {
+            startScrolling();
+            doScroll((int) -distanceY);
+            return true;
+        }
+
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                               float velocityY) {
+            lastScrollY = currentItem * getItemHeight() + scrollingOffset;
+            int maxY = isCyclic ? 0x7FFFFFFF : adapter.getItemsCount()
+                    * getItemHeight();
+            int minY = isCyclic ? -maxY : 0;
+            scroller.fling(0, lastScrollY, 0, (int) -velocityY / 2, 0, 0, minY,
+                    maxY);
+            setNextMessage(MESSAGE_SCROLL);
+            return true;
+        }
+    };
+
+    // Messages
+    private final int MESSAGE_SCROLL = 0;
+    private final int MESSAGE_JUSTIFY = 1;
+
+    /**
+     * Set next message to queue. Clears queue before.
+     *
+     * @param message the message to set
+     */
+    private void setNextMessage(int message) {
+        clearMessages();
+        animationHandler.sendEmptyMessage(message);
+    }
+
+    /**
+     * Clears messages from queue
+     */
+    private void clearMessages() {
+        animationHandler.removeMessages(MESSAGE_SCROLL);
+        animationHandler.removeMessages(MESSAGE_JUSTIFY);
+    }
+
+    // animation handler
+    private Handler animationHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            scroller.computeScrollOffset();
+            int currY = scroller.getCurrY();
+            int delta = lastScrollY - currY;
+            lastScrollY = currY;
+            if (delta != 0) {
+                doScroll(delta);
+            }
+
+            // scrolling is not finished when it comes to final Y
+            // so, finish it manually
+            if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {
+                currY = scroller.getFinalY();
+                scroller.forceFinished(true);
+            }
+            if (!scroller.isFinished()) {
+                animationHandler.sendEmptyMessage(msg.what);
+            } else if (msg.what == MESSAGE_SCROLL) {
+                justify();
+            } else {
+                finishScrolling();
+            }
+        }
+    };
+
+    /**
+     * Justifies wheel
+     */
+    private void justify() {
+        if (adapter == null) {
+            return;
+        }
+
+        lastScrollY = 0;
+        int offset = scrollingOffset;
+        int itemHeight = getItemHeight();
+        boolean needToIncrease = offset > 0 ? currentItem < adapter
+                .getItemsCount() : currentItem > 0;
+        if ((isCyclic || needToIncrease)
+                && Math.abs((float) offset) > (float) itemHeight / 2) {
+            if (offset < 0)
+                offset += itemHeight + MIN_DELTA_FOR_SCROLLING;
+            else
+                offset -= itemHeight + MIN_DELTA_FOR_SCROLLING;
+        }
+        if (Math.abs(offset) > MIN_DELTA_FOR_SCROLLING) {
+            scroller.startScroll(0, 0, 0, offset, SCROLLING_DURATION);
+            setNextMessage(MESSAGE_JUSTIFY);
+        } else {
+            finishScrolling();
+        }
+    }
+
+    /**
+     * Starts scrolling
+     */
+    private void startScrolling() {
+        if (!isScrollingPerformed) {
+            isScrollingPerformed = true;
+            notifyScrollingListenersAboutStart();
+        }
+    }
+
+    /**
+     * Finishes scrolling
+     */
+    void finishScrolling() {
+        if (isScrollingPerformed) {
+            notifyScrollingListenersAboutEnd();
+            isScrollingPerformed = false;
+        }
+        invalidateLayouts();
+        invalidate();
+    }
+
+    /**
+     * Scroll the wheel
+     *
+     * @param itemsToScroll items to scroll
+     * @param time          scrolling duration
+     */
+    public void scroll(int itemsToScroll, int time) {
+        scroller.forceFinished(true);
+
+        lastScrollY = scrollingOffset;
+        int offset = itemsToScroll * getItemHeight();
+
+        scroller.startScroll(0, lastScrollY, 0, offset - lastScrollY, time);
+        setNextMessage(MESSAGE_SCROLL);
+
+        startScrolling();
+    }
+
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/ring/OnSelectRing.java b/app/src/main/java/com/ic/hardware/widget/ring/OnSelectRing.java
new file mode 100644 (file)
index 0000000..eda60ca
--- /dev/null
@@ -0,0 +1,5 @@
+package com.ic.hardware.widget.ring;
+
+public interface OnSelectRing {
+    void Selected(Ring r);
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/ring/Ring.java b/app/src/main/java/com/ic/hardware/widget/ring/Ring.java
new file mode 100644 (file)
index 0000000..e510c17
--- /dev/null
@@ -0,0 +1,75 @@
+package com.ic.hardware.widget.ring;
+
+import android.graphics.RectF;
+
+public class Ring {
+    private int progress;
+    private String name;
+    private String value;
+    private RectF rectFRing;
+    private int startColor;
+    private int endColor;
+
+
+    public Ring(int p,  String value, String name,int startColor, int endColor) {
+        this.progress = p;
+        this.name = name;
+        this.value = value;
+        this.startColor = startColor;
+        this.endColor = endColor;
+
+    }
+
+    public Ring() {
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getProgress() {
+        return progress;
+    }
+
+    public void setProgress(int progress) {
+        this.progress = progress;
+    }
+
+    public RectF getRectFRing() {
+        return rectFRing;
+    }
+
+    public void setRectFRing(RectF rectFRing) {
+        this.rectFRing = rectFRing;
+    }
+
+
+    public int getStartColor() {
+        return startColor;
+    }
+
+    public void setStartColor(int startColor) {
+        this.startColor = startColor;
+    }
+
+    public int getEndColor() {
+        return endColor;
+    }
+
+    public void setEndColor(int endColor) {
+        this.endColor = endColor;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+}
diff --git a/app/src/main/java/com/ic/hardware/widget/ring/RingProgress.java b/app/src/main/java/com/ic/hardware/widget/ring/RingProgress.java
new file mode 100644 (file)
index 0000000..6c8fa30
--- /dev/null
@@ -0,0 +1,647 @@
+package com.ic.hardware.widget.ring;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+
+import com.ic.hardware.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RingProgress extends View {
+
+    private Paint mPaint;
+    private Bitmap mBitmapBg;
+    private Paint mPaintText;
+    private int SweepAngle = 180;
+    private int mPadding;
+    private int mWidth;
+
+    private int ringWidth = 0;
+
+
+    private int rotateAngle = 270;
+
+    private int bgShadowColor = Color.argb(100, 0, 0, 0);
+    private int bgColor = Color.rgb(141, 141, 141);
+
+    private List<Ring> mListRing = new ArrayList<>();
+    private RectF rectFBg = new RectF();
+    private boolean isCorner = true;
+    private boolean isDrawBg = true;
+    private boolean isDrawBgShadow = true;
+    private float ringWidthScale = 0f;
+    private boolean bgChange = false;
+
+    public int getSweepAngle() {
+        return SweepAngle;
+    }
+
+    public void setSweepAngle(int sweepAngle) {
+
+        if (sweepAngle < 0)
+            sweepAngle = 0;
+        else if (sweepAngle > 360)
+            sweepAngle = 360;
+        SweepAngle = sweepAngle;
+        bgChange = true;
+        invalidate();
+    }
+
+    public int getRotateAngle() {
+        return rotateAngle;
+    }
+
+    public void setRotateAngle(int rotateAngle) {
+
+        if (rotateAngle < 0)
+            rotateAngle = 0;
+        else if (rotateAngle > 360)
+            rotateAngle = 360;
+
+
+        this.rotateAngle = rotateAngle;
+        invalidate();
+    }
+
+    public boolean isCorner() {
+        return isCorner;
+    }
+
+    public void setCorner(boolean corner) {
+        isCorner = corner;
+        bgChange = true;
+        invalidate();
+    }
+
+    public boolean isDrawBgShadow() {
+        return isDrawBgShadow;
+    }
+
+    public void setDrawBgShadow(boolean drawBgShadow) {
+        isDrawBgShadow = drawBgShadow;
+        bgChange = true;
+        invalidate();
+    }
+
+    public void setDrawBgShadow(boolean drawBgShadow, int color) {
+        isDrawBgShadow = drawBgShadow;
+        this.bgShadowColor = color;
+        bgChange = true;
+        invalidate();
+    }
+
+
+    public boolean isDrawBg() {
+        return isDrawBg;
+    }
+
+    public void setDrawBg(boolean drawBg) {
+        isDrawBg = drawBg;
+        bgChange = true;
+        invalidate();
+    }
+
+    public void setDrawBg(boolean drawBg, int color) {
+        isDrawBg = drawBg;
+        this.bgColor = color;
+        bgChange = true;
+        invalidate();
+    }
+
+    public List<Ring> getmListRing() {
+        return mListRing;
+    }
+
+    public void setmListRing(List<Ring> mListRing) {
+        this.mListRing = mListRing;
+    }
+
+
+    public float getRingWidthScale() {
+        return ringWidthScale;
+    }
+
+    public void setRingWidthScale(float ringWidthScale) {
+        this.ringWidthScale = ringWidthScale;
+        bgChange = true;
+        invalidate();
+    }
+
+
+    public void setData(List<Ring> mListRing, int time) {
+        this.mListRing.clear();
+        for (int i = 0; i < mListRing.size(); i++) {
+
+            RectF r = new RectF();
+            r.top = rectFBg.top + ringWidth * i;
+            r.bottom = rectFBg.bottom - ringWidth * i;
+            r.left = rectFBg.left + ringWidth * i;
+            r.right = rectFBg.right - ringWidth * i;
+            mListRing.get(i).setRectFRing(r);
+        }
+
+        for (int i = 0; i < mListRing.size(); i++) {
+            this.mListRing.add(mListRing.get(i));
+        }
+        if (time > 0)
+            startAnim(time);
+        else
+            invalidate();
+    }
+
+
+    public RingProgress(Context context) {
+        this(context, null);
+    }
+
+    public RingProgress(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public RingProgress(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(attrs);
+    }
+
+
+    private void init(AttributeSet attrs) {
+        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.RingProgress);
+        isCorner = typedArray.getBoolean(R.styleable.RingProgress_showRingCorner, false);
+        isDrawBg = typedArray.getBoolean(R.styleable.RingProgress_showBackground, false);
+        isDrawBgShadow = typedArray.getBoolean(R.styleable.RingProgress_showBackgroundShadow, false);
+        rotateAngle = typedArray.getInt(R.styleable.RingProgress_rotate, 270);
+        ringWidthScale = typedArray.getFloat(R.styleable.RingProgress_ringWidthScale, 0.5f);
+        bgShadowColor = typedArray.getColor(R.styleable.RingProgress_bgShadowColor, bgShadowColor);
+        bgColor = typedArray.getColor(R.styleable.RingProgress_bgColor, bgColor);
+        SweepAngle = typedArray.getInt(R.styleable.RingProgress_ringSweepAngle, 180);
+        typedArray.recycle();
+
+
+        initPaint();
+    }
+
+
+    private void initPaint() {
+        mPaint = new Paint();
+
+        mPaintText = new Paint();
+        mPaintText.setAntiAlias(true);
+        mPaintText.setStyle(Paint.Style.FILL);
+        mPaintText.setColor(Color.WHITE);
+
+    }
+
+
+    private void setmBitmapBg(Paint paint, Bitmap mBitmapBg) {
+        Canvas canvas = null;
+        canvas = new Canvas(mBitmapBg);
+
+        for (int i = 0; i < mListRing.size(); i++) {
+            paint.reset();
+            paint.setAntiAlias(true);
+            paint.setStrokeWidth(ringWidth);
+            paint.setStyle(Paint.Style.STROKE);
+            if (isCorner) {
+                paint.setStrokeCap(Paint.Cap.ROUND);
+                paint.setStrokeJoin(Paint.Join.ROUND);
+            }
+
+            int red = (bgColor & 0xff0000) >> 16;
+            int green = (bgColor & 0x00ff00) >> 8;
+            int blue = (bgColor & 0x0000ff);
+            int colorvaluer = red + (255 - red) / mListRing.size() * i;
+            int colorvalueg = green + (255 - green) / mListRing.size() * i;
+            int colorvalueb = blue + (255 - blue) / mListRing.size() * i;
+
+            paint.setColor(Color.rgb(colorvaluer, colorvalueg, colorvalueb));
+
+
+            Path pathBg = new Path();
+            RectF r = new RectF();
+            r.top = rectFBg.top + ringWidth * i;
+            r.bottom = rectFBg.bottom - ringWidth * i;
+            r.left = rectFBg.left + ringWidth * i;
+            r.right = rectFBg.right - ringWidth * i;
+            mListRing.get(i).setRectFRing(r);
+
+            pathBg.addArc(r, 0, SweepAngle);
+
+            if (i == 0 && isDrawBgShadow) {
+                paint.setShadowLayer(ringWidth / 3,
+                        0 - ringWidth / 4,
+                        0, bgShadowColor);
+            }
+            if (isDrawBg)
+                canvas.drawPath(pathBg, paint);
+
+        }
+        bgChange = false;
+
+    }
+
+
+    private Bitmap getmBitmapBg(Paint paint) {
+
+        if (mBitmapBg == null) {
+            mBitmapBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
+            setmBitmapBg(paint, mBitmapBg);
+
+        }
+
+        if (bgChange) {
+            mBitmapBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
+            setmBitmapBg(paint, mBitmapBg);
+        }
+
+
+        return mBitmapBg;
+
+    }
+
+
+    private void drawBg(Canvas canvas, Paint paint) {
+        paint.setAntiAlias(true);
+        canvas.drawBitmap(getmBitmapBg(paint), 0
+                , 0, paint);
+    }
+
+
+    private void drawProgress(Canvas canvas, Paint paint) {
+
+
+        for (int i = 0; i < mListRing.size(); i++) {
+
+            paint.reset();
+            paint.setAntiAlias(true);
+            paint.setStrokeWidth(ringWidth);
+            paint.setStyle(Paint.Style.STROKE);
+            Path pathProgress = new Path();
+            pathProgress.addArc(mListRing.get(i).getRectFRing(), 0,
+
+                    (int) (SweepAngle / 100f * mListRing.get(i).getProgress() * mAnimatedValue)
+
+            );
+
+            Shader mShader = new LinearGradient(mListRing.get(i).getRectFRing().left, mListRing.get(i).getRectFRing().top,
+                    mListRing.get(i).getRectFRing().left, mListRing.get(i).getRectFRing().bottom,
+                    new int[]{
+                            mListRing.get(i).getStartColor(),
+                            mListRing.get(i).getEndColor()
+
+                    }, new float[]{0f, 1f},
+                    Shader.TileMode.CLAMP);
+
+            paint.setShader(mShader);
+            if (isCorner) {
+                paint.setStrokeCap(Paint.Cap.ROUND);
+                paint.setStrokeJoin(Paint.Join.ROUND);
+            }
+            canvas.drawPath(pathProgress, paint);
+            paint.setShader(null);
+
+
+            mPaintText.setTextSize(mPaint.getStrokeWidth() / 2);
+
+
+            String textvalue = String.valueOf(mListRing.get(i).getValue());
+
+            float arc_length = (float) (Math.PI * mListRing.get(i).getRectFRing().width()
+                    * (mListRing.get(i).getProgress() / 100f))
+                    * (SweepAngle / 360f);
+
+            float textvalue_length = getFontlength(mPaintText, textvalue);
+
+            if (mAnimatedValue == 1) {
+
+
+                if (arc_length - textvalue_length * 1.5f <= 0) {
+
+
+                    float textvalue_length_one = textvalue_length * 1.0f / textvalue.length();
+
+
+                    int textvalue_size = (int) (arc_length / textvalue_length_one);
+
+                    if (textvalue_size >= textvalue.length()) {
+                        canvas.drawTextOnPath(textvalue
+                                ,
+                                pathProgress,
+                                10,
+                                getFontHeight(mPaintText) / 3,
+                                mPaintText);
+                    } else {
+                        String text = textvalue.substring(0, 1);
+                        for (int j = 0; j < textvalue_size; j++) {
+                            text = text + ".";
+                        }
+
+                        canvas.drawTextOnPath(text
+                                ,
+                                pathProgress,
+                                10,
+                                getFontHeight(mPaintText) / 3,
+                                mPaintText);
+
+
+                    }
+                } else {
+
+                    canvas.drawTextOnPath(textvalue
+                            ,
+                            pathProgress,
+                            (float) (arc_length
+                                    - textvalue_length * 1.5f),
+                            getFontHeight(mPaintText) / 3,
+                            mPaintText
+
+                    );
+                }
+            }
+
+
+            String text = String.valueOf(mListRing.get(i).getName());
+
+
+            float textlength = getFontlength(mPaintText, text);
+
+            float textlength_one = textlength * 1.0f / text.length();
+
+            float showtextlength = (float) (arc_length
+                    - textvalue_length * 1.8f);
+            if (showtextlength < 0)
+                showtextlength = 0;
+
+            float textsize = showtextlength / textlength_one;
+
+            if (textsize > text.length()) {
+                textsize = text.length();
+            } else if (textsize < 1) {
+                textsize = 0;
+            }
+
+            canvas.drawTextOnPath(text.substring(0, (int) textsize)
+                    ,
+                    pathProgress,
+
+                    10, getFontHeight(mPaintText) / 3,
+                    mPaintText
+
+            );
+
+
+        }
+
+    }
+
+
+    @SuppressLint("DrawAllocation")
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+
+        canvas.rotate(rotateAngle, getMeasuredWidth() / 2f, getMeasuredHeight() / 2f);
+        canvas.save();
+
+        if (mListRing.size() > 0)
+            ringWidth = (int) (
+                    mWidth / 2f / (mListRing.size() + 0.5f) * (1 - ringWidthScale));
+        mPadding = ringWidth;
+        rectFBg = new RectF(getMeasuredWidth() / 2 - mWidth / 2 + mPadding
+                , getMeasuredHeight() / 2 - mWidth / 2 + mPadding
+                , getMeasuredWidth() / 2 + mWidth / 2 - mPadding
+                , getMeasuredHeight() / 2 + mWidth / 2 - mPadding);
+
+        drawBg(canvas, mPaint);
+        drawProgress(canvas, mPaint);
+        canvas.restore();
+
+    }
+
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
+        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+        if (widthSpecMode == MeasureSpec.AT_MOST
+                && heightSpecMode == MeasureSpec.AT_MOST) {
+            setMeasuredDimension(dip2px(30), dip2px(30));
+        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
+            setMeasuredDimension(heightSpecSize, heightSpecSize);
+        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
+            setMeasuredDimension(widthSpecSize, widthSpecSize);
+        }
+        if (getMeasuredWidth() > getHeight())
+            mWidth = getMeasuredHeight();
+        else
+            mWidth = getMeasuredWidth();
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        if (w > h)
+            mWidth = h;
+        else
+            mWidth = w;
+
+
+    }
+
+
+    public float getFontlength(Paint paint, String str) {
+        return paint.measureText(str);
+    }
+
+    public float getFontHeight(Paint paint) {
+        Paint.FontMetrics fm = paint.getFontMetrics();
+        return fm.descent - fm.ascent;
+    }
+
+    public int dip2px(float dpValue) {
+        final float scale = getContext().getResources().getDisplayMetrics().density;
+        return (int) (dpValue * scale + 0.5f);
+    }
+
+
+    public void startAnim(int time) {
+        stopAnim();
+        startViewAnim(0f, 1f, time);
+    }
+
+    private ValueAnimator valueAnimator;
+    private float mAnimatedValue = 1f;
+
+    public void stopAnim() {
+        if (valueAnimator != null) {
+            clearAnimation();
+            valueAnimator.setRepeatCount(0);
+            valueAnimator.cancel();
+            mAnimatedValue = 0f;
+            postInvalidate();
+        }
+    }
+
+
+    private ValueAnimator startViewAnim(float startF, final float endF, long time) {
+        valueAnimator = ValueAnimator.ofFloat(startF, endF);
+        valueAnimator.setDuration(time);
+        valueAnimator.setInterpolator(new LinearInterpolator());
+        valueAnimator.setRepeatCount(0);
+        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
+        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+
+                mAnimatedValue = (float) valueAnimator.getAnimatedValue();
+                invalidate();
+
+
+            }
+        });
+        valueAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+            }
+
+            @Override
+            public void onAnimationRepeat(Animator animation) {
+                super.onAnimationRepeat(animation);
+            }
+        });
+        if (!valueAnimator.isRunning()) {
+            valueAnimator.start();
+
+        }
+
+        return valueAnimator;
+    }
+
+
+    float startX = 0;
+    float startY = 0;
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+
+        if (MotionEvent.ACTION_DOWN == event.getAction()) {
+            startX = event.getX();
+            startY = event.getY();
+            return true;
+        } else if (MotionEvent.ACTION_UP == event.getAction()) {
+            if (Math.abs(event.getX() - startX) < 5 &&
+                    Math.abs(event.getY() - startY) < 5) {
+
+
+                float distance = (float) Math.sqrt(Math.pow(Math.abs((event.getX() - rectFBg.centerX())), 2)
+                        + Math.pow(Math.abs((event.getY() - rectFBg.centerY())), 2));
+
+                for (int i = mListRing.size() - 1; i >= 0; i--) {
+
+                    if (mListRing.get(i).getRectFRing().width() / 2 + ringWidth / 2 > distance
+                            && mListRing.get(i).getRectFRing().width() / 2 - ringWidth / 2 < distance
+                    ) {
+
+                        float angle =
+                                Math.round((float)
+                                        (Math.asin(
+                                                Math.abs((event.getY() - rectFBg.centerY())) / distance) / Math.PI * 180));
+
+                        if (event.getY() - rectFBg.centerY() >= 0 &&
+                                event.getX() - rectFBg.centerX() >= 0) {
+                            angle = angle + 0;
+
+                        } else if (event.getY() - rectFBg.centerY() <= 0 &&
+                                event.getX() - rectFBg.centerX() >= 0) {
+                            angle = 360 - angle;
+                        } else if (event.getY() - rectFBg.centerY() <= 0 &&
+                                event.getX() - rectFBg.centerX() <= 0) {
+                            angle = 180 + angle;
+                        } else if (event.getY() - rectFBg.centerY() >= 0 &&
+                                event.getX() - rectFBg.centerX() <= 0) {
+                            angle = 180 - angle;
+
+                        }
+
+
+                        if (mListRing.get(i).getProgress() / 100f * 360f + rotateAngle <= 360 && rotateAngle <= 360) {
+
+                            if (angle >= rotateAngle && angle <= mListRing.get(i).getProgress() / 100f * 360f + rotateAngle) {
+                                if (mOnSelectRing != null) {
+                                    mOnSelectRing.Selected(mListRing.get(i));
+                                }
+                            }
+
+
+                        } else if (mListRing.get(i).getProgress() / 100f * 360f + rotateAngle >= 360 && rotateAngle <= 360) {
+
+
+                            if (angle >= rotateAngle && angle <= 360) {
+                                if (mOnSelectRing != null) {
+                                    mOnSelectRing.Selected(mListRing.get(i));
+                                }
+                            } else if (angle >= 0 && mListRing.get(i).getProgress() / 100f * 360f + rotateAngle - 360 >= angle) {
+                                if (mOnSelectRing != null) {
+                                    mOnSelectRing.Selected(mListRing.get(i));
+                                }
+                            }
+
+
+                        }
+
+
+                        return true;
+
+                    }
+
+                }
+
+
+                return true;
+
+            }
+
+
+        }
+
+        return false;
+    }
+
+
+    private OnSelectRing mOnSelectRing;
+
+    public OnSelectRing getmOnSelectRing() {
+        return mOnSelectRing;
+    }
+
+    public void setOnSelectRing(OnSelectRing mOnSelectRing) {
+        this.mOnSelectRing = mOnSelectRing;
+    }
+}
diff --git a/app/src/main/res/anim/slide_left_in.xml b/app/src/main/res/anim/slide_left_in.xml
new file mode 100644 (file)
index 0000000..363aa32
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:interpolator="@android:interpolator/accelerate_cubic"
+        android:valueFrom="1000" android:valueTo="0"
+        android:valueType="floatType"
+        android:propertyName="translationX"
+        android:duration="@android:integer/config_mediumAnimTime" />
+</set>
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_left_out.xml b/app/src/main/res/anim/slide_left_out.xml
new file mode 100644 (file)
index 0000000..8ceff44
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:interpolator="@android:interpolator/accelerate_cubic"
+        android:valueFrom="0" android:valueTo="-1000"
+        android:valueType="floatType"
+        android:propertyName="translationX"
+        android:duration="@android:integer/config_mediumAnimTime" />
+</set>
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_right_in.xml b/app/src/main/res/anim/slide_right_in.xml
new file mode 100644 (file)
index 0000000..c8c45b6
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <translate
+        android:fromXDelta="100%p"
+        android:toXDelta="0%p"
+        android:duration="200"/>
+
+    <alpha
+        android:fromAlpha="0.5"
+        android:toAlpha="1.0"
+        android:duration="200"/>
+
+</set>
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_right_out.xml b/app/src/main/res/anim/slide_right_out.xml
new file mode 100644 (file)
index 0000000..5687d63
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <translate
+        android:fromXDelta="0%p"
+        android:toXDelta="100%p"
+        android:duration="200"/>
+
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="0.5"
+        android:duration="200"/>
+
+</set>
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644 (file)
index 0000000..1f6bb29
--- /dev/null
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillType="evenOdd"
+        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000">
+        <aapt:attr name="android:fillColor">
+            <gradient
+                android:endX="78.5885"
+                android:endY="90.9159"
+                android:startX="48.7653"
+                android:startY="61.0927"
+                android:type="linear">
+                <item
+                    android:color="#44000000"
+                    android:offset="0.0" />
+                <item
+                    android:color="#00000000"
+                    android:offset="1.0" />
+            </gradient>
+        </aapt:attr>
+    </path>
+    <path
+        android:fillColor="#FFFFFF"
+        android:fillType="nonZero"
+        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+        android:strokeWidth="1"
+        android:strokeColor="#00000000" />
+</vector>
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644 (file)
index 0000000..0d025f9
--- /dev/null
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#008577"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/app/src/main/res/drawable/ic_ped_calories.xml b/app/src/main/res/drawable/ic_ped_calories.xml
new file mode 100644 (file)
index 0000000..b063cb6
--- /dev/null
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024">
+    <path
+        android:fillColor="#d81e06"
+        android:pathData="M893.82,599.55c-11.71,-152.7 -82.82,-248.45 -145.6,-332.86C690.18,188.48 640,120.96 640,21.38a21.38,21.38 0,0 0,-33.73 -17.34C512,71.42 433.47,184.96 405.95,293.44c-19.01,75.52 -21.57,160.32 -21.89,216.32 -87.04,-18.56 -106.75,-148.67 -106.88,-150.08a21.44,21.44 0,0 0,-30.53 -16.19c-4.48,2.24 -112,56.77 -118.21,274.69A384.45,384.45 0,0 0,512 1024h1.15A384.45,384.45 0,0 0,896 640c0,-10.56 -2.18,-40.38 -2.18,-40.38zM512,981.31c-70.59,0 -128,-61.18 -128,-136.32 0,-2.56 0,-5.12 0.19,-8.32 0.83,-31.74 6.85,-53.38 13.44,-67.78 12.42,26.56 34.56,51.01 70.4,51.01 11.84,0 21.38,-9.54 21.38,-21.31 0,-30.4 0.64,-65.41 8.19,-97.02 6.72,-28.03 22.85,-57.92 43.2,-81.79 9.09,31.04 26.75,56.13 43.97,80.64 24.7,35.14 50.24,71.36 54.72,133.25 0.26,3.65 0.51,7.36 0.51,11.33 0,75.14 -57.41,136.32 -128,136.32z" />
+</vector>
diff --git a/app/src/main/res/drawable/ic_ped_distance.xml b/app/src/main/res/drawable/ic_ped_distance.xml
new file mode 100644 (file)
index 0000000..0dac9b6
--- /dev/null
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024">
+    <path
+        android:fillColor="#1296db"
+        android:pathData="M674.3,87.72a324.27,324.27 0,0 0,-443.73 0c-136.53,127.49 -127.83,346.97 3.93,477.87l182.95,184.15a48.64,48.64 0,0 0,69.29 0L648.53,588.46l22.02,-22.02c131.58,-131.75 139.61,-351.23 3.75,-478.72zM452.44,461.99a143.36,143.36 0,1 1,143.36 -143.19,143.36 143.36,0 0,1 -143.7,143.19zM865.62,839.17h-51.2a25.6,25.6 0,0 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM949.42,828.93a25.6,25.6 0,0 1,-11.95 -48.3,66.73 66.73,0 0,0 26.45,-25.09 25.6,25.6 0,0 1,44.03 25.94,117.42 117.42,0 0,1 -46.76,44.54 27.14,27.14 0,0 1,-11.78 2.9zM991.06,712.02a25.6,25.6 0,0 1,-23.55 -15.36,65.54 65.54,0 0,0 -23.21,-28.16A25.6,25.6 0,0 1,972.8 626.35a115.37,115.37 0,0 1,41.13 50.01,25.6 25.6,0 0,1 -13.31,34.13 26.62,26.62 0,0 1,-9.56 1.54zM876.89,657.07h-51.2a25.6,25.6 0,1 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM740.35,657.07h-51.2a25.6,25.6 0,1 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM219.82,657.07h-51.2a25.6,25.6 0,0 1,0 -51.2h51.2a25.6,25.6 0,1 1,0 51.2zM83.29,657.07h-51.2a25.6,25.6 0,0 1,0 -51.2h51.2a25.6,25.6 0,1 1,0 51.2zM601.6,1021.1h-51.2a25.6,25.6 0,0 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM465.07,1021.1h-51.2a25.6,25.6 0,0 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM328.53,1021.1h-24.23a121.34,121.34 0,0 1,-34.13 -4.95,25.6 25.6,0 0,1 15.02,-48.98 65.54,65.54 0,0 0,19.11 2.73h24.23a25.6,25.6 0,0 1,0 51.2zM218.45,960.17a25.6,25.6 0,0 1,-24.23 -17.07A117.25,117.25 0,0 1,187.73 904.53a122.37,122.37 0,0 1,2.9 -26.11,25.6 25.6,0 0,1 50.01,11.43 60.59,60.59 0,0 0,-1.71 14.68,66.39 66.39,0 0,0 3.58,21.67 25.43,25.43 0,0 1,-15.7 32.6,23.89 23.89,0 0,1 -8.36,1.37zM268.29,846.51a25.6,25.6 0,0 1,-10.24 -48.98,116.22 116.22,0 0,1 46.25,-9.56h13.99a25.6,25.6 0,0 1,0 51.2h-13.99a64.17,64.17 0,0 0,-25.94 5.29,25.94 25.94,0 0,1 -10.07,2.05zM727.89,839.17h-51.2a25.6,25.6 0,1 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM591.36,839.17h-51.2a25.6,25.6 0,1 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2zM454.83,839.17h-51.2a25.6,25.6 0,1 1,0 -51.2h51.2a25.6,25.6 0,0 1,0 51.2z" />
+</vector>
diff --git a/app/src/main/res/drawable/ic_ped_height.xml b/app/src/main/res/drawable/ic_ped_height.xml
new file mode 100644 (file)
index 0000000..7a68699
--- /dev/null
@@ -0,0 +1,4 @@
+<vector android:height="48dp" android:viewportHeight="1024"
+    android:viewportWidth="1024" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#009688" android:pathData="M400.27,319.83a106.91,106.91 0,1 0,-106.46 -107.36,106.91 106.91,0 0,0 106.46,107.36zM896.48,0.45h-97.89a21.43,21.43 0,0 0,0 42.85h76.69v202.09L822.95,245.4a21.43,21.43 0,0 0,0 42.85h51.88v202.54L822.95,490.8a21.43,21.43 0,0 0,0 42.85h51.88v202.09L822.95,735.75a21.43,21.43 0,0 0,0 42.85h51.88v202.54h-76.69a21.43,21.43 0,0 0,0 42.85h97.89a21.2,21.2 0,0 0,21.2 -21.2L917.23,21.65a21.2,21.2 0,0 0,-21.2 -21.2zM553.64,397.42a149.77,149.77 0,0 0,-111.42 -49.62L357.86,347.8a149.77,149.77 0,0 0,-111.42 49.62l-135.33,151.57a19.4,19.4 0,0 0,0 26.16l45.11,45.11a18.04,18.04 0,0 0,13.98 5.41,18.95 18.95,0 0,0 13.53,-6.32l97.44,-112.32v496.21a18.95,18.95 0,0 0,18.95 18.95L367.34,1022.2a18.95,18.95 0,0 0,18.95 -18.95v-288.7h27.52v288.7a18.95,18.95 0,0 0,18.95 18.95L498.16,1022.2a18.95,18.95 0,0 0,18.95 -18.95v-496.21l97.44,112.32a18.95,18.95 0,0 0,13.53 6.32,18.5 18.5,0 0,0 13.98,-5.41l45.11,-45.11a19.4,19.4 0,0 0,0 -26.16z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_ped_taking.xml b/app/src/main/res/drawable/ic_ped_taking.xml
new file mode 100644 (file)
index 0000000..50a8cd8
--- /dev/null
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="1024"
+    android:viewportHeight="1024">
+  <path
+      android:pathData="M359.93,164.43c0,85.09 -68.94,154.02 -154.03,154.02 -85.05,0 -153.99,-68.94 -153.99,-154.02 0,-85.04 68.94,-153.98 153.99,-153.98 85.09,0 154.03,68.94 154.03,153.98zM567.83,87.47c0,42.54 -34.47,77.01 -77.02,77.01s-77.01,-34.47 -77.01,-77.01c0,-42.54 34.47,-77.01 77.01,-77.01s77.02,34.47 77.02,77.01zM791.12,106.69c0,42.54 -34.47,77.01 -77.01,77.01s-77.02,-34.47 -77.02,-77.01 34.47,-77.01 77.02,-77.01c42.55,-0 77.01,34.47 77.01,77.01zM972.09,206.8c0,42.54 -34.47,77.01 -77.02,77.01s-77.01,-34.47 -77.01,-77.01 34.47,-77.01 77.01,-77.01c42.54,0 77.02,34.47 77.02,77.01zM291.53,405.08s49.09,-135.71 236.78,-158.81c0,0 289.75,-20.21 339.65,265.46 24.36,139.59 14.21,441.95 -209.71,487.97 -54.05,11.1 -210.79,-2.71 -190.58,-144.19 7.13,-50.03 54.05,-131.47 0,-202.12 -37.54,-49.09 -225.23,-63.52 -176.14,-248.32z"
+      android:fillColor="#C79707"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_ped_task.xml b/app/src/main/res/drawable/ic_ped_task.xml
new file mode 100644 (file)
index 0000000..0c0d57d
--- /dev/null
@@ -0,0 +1,4 @@
+<vector android:height="48dp" android:viewportHeight="1024"
+    android:viewportWidth="1024" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#d81e06" android:pathData="M381.87,121.6c53.33,0 98.13,14.93 147.2,29.87 53.33,19.2 108.8,36.27 174.93,36.27 57.6,0 115.2,-12.8 174.93,-42.67 -6.4,81.07 -12.8,192 -21.33,283.73 -25.6,25.6 -91.73,72.53 -217.6,72.53 -12.8,0 -25.6,0 -40.53,-2.13 -14.93,0 -29.87,-2.13 -42.67,-2.13 -138.67,0 -224,51.2 -273.07,102.4L170.67,187.73l17.07,-8.53c72.53,-38.4 136.53,-57.6 194.13,-57.6M955.73,8.53c-17.07,0 -44.8,21.33 -44.8,21.33C827.73,85.33 763.73,102.4 704,102.4c-110.93,0 -196.27,-66.13 -324.27,-66.13 -64,0 -138.67,17.07 -232.53,66.13l-10.67,-46.93c-4.27,-19.2 -21.33,-32 -40.53,-32 -4.27,0 -8.53,0 -10.67,2.13 -23.47,6.4 -36.27,29.87 -32,51.2l243.2,906.67c4.27,19.2 21.33,32 40.53,32 4.27,0 8.53,0 10.67,-2.13 23.47,-6.4 36.27,-29.87 29.87,-51.2l-68.27,-251.73c25.6,-49.07 89.6,-125.87 247.47,-125.87 12.8,0 23.47,0 36.27,2.13 17.07,2.13 32,2.13 46.93,2.13 219.73,0 300.8,-123.73 300.8,-123.73s32,-405.33 29.87,-437.33c0,-14.93 -6.4,-19.2 -14.93,-19.2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_ped_weight.xml b/app/src/main/res/drawable/ic_ped_weight.xml
new file mode 100644 (file)
index 0000000..f0613d5
--- /dev/null
@@ -0,0 +1,4 @@
+<vector android:height="48dp" android:viewportHeight="1024"
+    android:viewportWidth="1024" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#1296db" android:pathData="M896.21,214.25a78.88,78.88 0,0 0,-55.55 -22.84L692.84,191.41a179.47,179.47 0,0 0,-52.16 -106.5A182.61,182.61 0,0 0,511.77 32a182.61,182.61 0,0 0,-128.92 52.91,179.47 179.47,0 0,0 -52.16,106.51L182.87,191.41c-21.65,0 -41.32,8.75 -55.55,22.84A77.32,77.32 0,0 0,104.21 269.33v591.42a77.33,77.33 0,0 0,23.09 55.07A78.77,78.77 0,0 0,182.86 938.67h657.81c21.62,0 41.28,-8.75 55.55,-22.84a77.47,77.47 0,0 0,23.09 -55.08L919.32,269.33c0,-21.46 -8.86,-40.94 -23.09,-55.07zM803.96,477.9c17.45,5.1 3.07,39.27 -17.66,35.31 -14.72,-2.79 -11.33,-43.84 17.66,-35.31zM774.36,447.69c22.17,6.51 3.89,49.9 -22.45,44.87 -18.7,-3.54 -14.4,-55.71 22.45,-44.87zM695.32,485.41c-24.35,-4.64 -15.87,-75.54 32.06,-61.44 28.85,8.49 2.22,67.99 -32.06,61.44zM413.16,114.94a139.79,139.79 0,0 1,98.6 -40.45c38.51,0 73.37,15.48 98.6,40.45a137.17,137.17 0,0 1,40.82 97.67c0,38.14 -15.63,72.68 -40.82,97.69a139.65,139.65 0,0 1,-98.6 40.45c-38.51,0 -73.35,-15.45 -98.6,-40.45a137.03,137.03 0,0 1,-40.82 -97.69c0,-38.13 15.58,-72.67 40.82,-97.67zM306.97,423.96c47.94,-14.09 56.41,56.81 32.06,61.45 -34.29,6.55 -60.92,-52.95 -32.06,-61.44zM259.99,447.69c36.85,-10.84 41.14,41.33 22.44,44.87 -26.35,5.01 -44.61,-38.36 -22.44,-44.87zM230.38,477.9c28.99,-8.53 32.39,32.51 17.66,35.31 -20.74,3.97 -35.1,-30.21 -17.66,-35.31zM204.75,564.96c-15.45,2.93 -26.17,-22.53 -13.15,-26.36 21.62,-6.38 24.13,24.27 13.15,26.35zM221.05,536.91c-18.08,3.47 -30.59,-26.33 -15.36,-30.82 25.27,-7.43 28.2,28.37 15.36,30.82zM433,825.9c-2.07,43.64 -50.26,58.9 -79.65,39.15 -32.57,-21.87 -43.86,-76.54 -63.17,-121.02 -12.16,-28.02 -30.13,-53.65 -43.54,-74.27 -21.78,-33.5 -30.5,-71.22 3.15,-108.52 25.81,-28.58 55.58,-47.04 88.16,-50.14 39.47,-3.83 61.01,25.07 57.56,68.43 -2.9,36.05 -19.31,61.76 -13.91,97.54 7.15,47.57 54.49,83.41 51.41,148.85zM362.61,360.38a207.65,207.65 0,0 1,-60.69 -126.47h28.77a179.27,179.27 0,0 0,52.16 106.43,182.61 182.61,0 0,0 128.92,52.91 182.61,182.61 0,0 0,128.92 -52.91c28.23,-27.95 47.25,-65.07 52.16,-106.43h28.8c-5.03,49.15 -27.31,93.35 -60.74,126.47a211.26,211.26 0,0 1,-149.14 61.19,211.27 211.27,0 0,1 -149.15,-61.19zM787.7,669.75c-13.41,20.61 -31.39,46.25 -43.55,74.27 -19.31,44.48 -30.59,99.17 -63.17,121.01 -29.39,19.77 -77.58,4.5 -79.65,-39.13 -3.08,-65.46 44.27,-101.3 51.41,-148.86 5.39,-35.77 -11.02,-61.48 -13.92,-97.54 -3.47,-43.35 18.1,-72.25 57.57,-68.43 32.57,3.11 62.35,21.57 88.15,50.15 33.64,37.29 24.93,75.01 3.16,108.51zM813.29,536.91c-12.83,-2.45 -9.9,-38.25 15.37,-30.82 15.23,4.49 2.72,34.28 -15.37,30.82zM829.59,564.96c-10.98,-2.1 -8.47,-32.74 13.16,-26.36 13.01,3.82 2.28,29.29 -13.16,26.35zM511.77,255.15c15.8,0 28.6,-12.68 28.6,-28.33a27.73,27.73 0,0 0,-3.86 -14.21l39.61,-68a14.02,14.02 0,0 0,-5.17 -19.31,14.29 14.29,0 0,0 -19.49,5.14l-39.65,68.04h-0.03c-15.81,0 -28.6,12.67 -28.6,28.33 0,15.66 12.8,28.33 28.59,28.33z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_red_flag.xml b/app/src/main/res/drawable/ic_red_flag.xml
new file mode 100644 (file)
index 0000000..c22e389
--- /dev/null
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="1024"
+    android:viewportWidth="1024" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FF0000" android:pathData="M854.81,186.18a30.02,30.02 0,0 0,-28.39 0,230.87 230.87,0 0,1 -118.23,27.93 239.71,239.71 0,0 1,-128 -33.05A302.55,302.55 0,0 0,425.19 139.64a290.21,290.21 0,0 0,-118.69 23.27l-7.91,3.26 -3.26,-7.68a29.09,29.09 0,0 0,-55.85 10.47v653.03A58.18,58.18 0,1 0,325.82 872.73a57.72,57.72 0,0 0,-28.16 -49.57v-256l4.42,-2.33a237.15,237.15 0,0 1,123.11 -30.49,239.94 239.94,0 0,1 128,32.81 297.43,297.43 0,0 0,155 39.56,291.37 291.37,0 0,0 144.76,-34.21 28.16,28.16 0,0 0,15.83 -25.13V209.45a28.16,28.16 0,0 0,-13.96 -23.27z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_transparent_circle.xml b/app/src/main/res/drawable/ic_transparent_circle.xml
new file mode 100644 (file)
index 0000000..7fd130f
--- /dev/null
@@ -0,0 +1,5 @@
+<vector android:height="18dp" android:tint="#A7A5A5"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#A7A5A5" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_white_circle.xml b/app/src/main/res/drawable/ic_white_circle.xml
new file mode 100644 (file)
index 0000000..a032825
--- /dev/null
@@ -0,0 +1,5 @@
+<vector android:height="18dp" android:tint="#FFFFFF"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/wheel_val.xml b/app/src/main/res/drawable/wheel_val.xml
new file mode 100644 (file)
index 0000000..ea38cf2
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+       <gradient
+               android:startColor="#10000000"
+               android:centerColor="#00000000"
+               android:endColor="#10000000"
+               android:angle="0" />
+
+       <stroke android:width="1dp" android:color="#00000000" />
+</shape>
diff --git a/app/src/main/res/layout/activity_blood.xml b/app/src/main/res/layout/activity_blood.xml
new file mode 100644 (file)
index 0000000..1f5824c
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/main_content_fl"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".BloodActivity" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_pedometer.xml b/app/src/main/res/layout/activity_pedometer.xml
new file mode 100644 (file)
index 0000000..235addf
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/ped_root_id"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".PedometerActivity">
+
+    <FrameLayout
+        android:id="@+id/ped_content_fl"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/ped_indicator_rv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:overScrollMode="never"
+        android:layout_gravity="bottom|center_horizontal"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_weather.xml b/app/src/main/res/layout/activity_weather.xml
new file mode 100644 (file)
index 0000000..632aa96
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/weather_content_fl"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".PedometerActivity" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_ped_setup_layout.xml b/app/src/main/res/layout/dialog_ped_setup_layout.xml
new file mode 100644 (file)
index 0000000..471f82c
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@mipmap/qs_background"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_gravity="center"
+        android:layout_marginTop="10dp"
+        android:layout_weight="1">
+
+        <com.ic.hardware.widget.picker.WheelView
+            android:id="@+id/dialog_value_picker"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
+    </LinearLayout>
+
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="10dp">
+
+        <ImageButton
+            android:id="@+id/dialog_picker_cancel"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:background="#0000"
+            android:contentDescription="@null"
+            android:src="@mipmap/ic_cancel"
+            android:layout_marginStart="20dp"/>
+
+        <ImageButton
+            android:id="@+id/dialog_picker_ok"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|end"
+            android:background="#0000"
+            android:contentDescription="@null"
+            android:src="@mipmap/ic_ok"
+            android:layout_alignParentEnd="true"
+            android:layout_marginEnd="20dp"/>
+    </RelativeLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_blood.xml b/app/src/main/res/layout/fragment_blood.xml
new file mode 100644 (file)
index 0000000..75747d8
--- /dev/null
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+        <import type="android.view.View"/>
+
+        <variable
+            name="topTips"
+            type="String" />
+
+        <variable
+            name="bottomTips"
+            type="String" />
+
+        <variable
+            name="isShowTips"
+            type="boolean" />
+
+        <variable
+            name="heartValue"
+            type="String" />
+
+        <variable
+            name="highValue"
+            type="String" />
+
+        <variable
+            name="lowValue"
+            type="String" />
+    </data>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#000000">
+
+        <pl.droidsonroids.gif.GifImageView
+            android:id="@+id/ic_heart_gif"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
+
+        <TextView
+            android:id="@+id/ic_tips_top"
+            style="@style/bloodTextStyle"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="10dp"
+            android:text="@{topTips}"
+            android:visibility="@{isShowTips ? View.VISIBLE : View.GONE}"/>
+
+        <TextView
+            android:id="@+id/ic_tips_bottom"
+            style="@style/bloodTextStyle"
+            android:layout_gravity="bottom|center_horizontal"
+            android:layout_marginBottom="10dp"
+            android:text="@{bottomTips}"
+            android:visibility="@{isShowTips ? View.VISIBLE : View.GONE}"/>
+
+        <RelativeLayout
+            android:id="@+id/ic_heart_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="20dp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}">
+
+            <TextView
+                android:id="@+id/ic_heart_name"
+                style="@style/bloodTextStyle"
+                android:layout_marginEnd="10dp"
+                android:text="@string/ic_heart_rate" />
+
+            <TextView
+                style="@style/bloodTextStyle"
+                android:id="@+id/ic_heart_value"
+                android:layout_marginStart="10dp"
+                android:layout_toEndOf="@id/ic_heart_name"
+                android:text="@{heartValue}"/>
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/ic_high_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginStart="30dp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}">
+
+            <TextView
+                android:id="@+id/ic_high_name"
+                style="@style/bloodTextStyle"
+                android:text="@string/ic_high_pressure" />
+
+            <TextView
+                android:id="@+id/ic_high_value"
+                style="@style/bloodTextStyle"
+                android:layout_below="@id/ic_high_name"
+                android:layout_centerHorizontal="true"
+                android:text="@{highValue}"/>
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/ic_low_layout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|end"
+            android:layout_marginEnd="30dp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}">
+
+            <TextView
+                android:id="@+id/ic_low_name"
+                style="@style/bloodTextStyle"
+                android:text="@string/ic_low_pressure" />
+
+            <TextView
+                android:id="@+id/ic_low_value"
+                style="@style/bloodTextStyle"
+                android:layout_below="@id/ic_low_name"
+                android:layout_centerHorizontal="true"
+                android:text="@{lowValue}"/>
+        </RelativeLayout>
+
+    </FrameLayout>
+
+</layout>
diff --git a/app/src/main/res/layout/fragment_ped_main.xml b/app/src/main/res/layout/fragment_ped_main.xml
new file mode 100644 (file)
index 0000000..476d431
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+        <variable
+            name="pedNumber"
+            type="String" />
+
+        <variable
+            name="pedTarget"
+            type="String" />
+    </data>
+
+    <FrameLayout
+        android:background="@mipmap/qs_background"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        tools:context=".fragment.pedometer.PedMainFragment">
+
+        <com.ic.hardware.widget.ring.RingProgress
+            android:id="@+id/ring_progress"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            app:bgColor="#9C9B9B"
+            app:ringSweepAngle="280"
+            app:ringWidthScale="0.7"
+            app:rotate="130"
+            app:showBackground="true"
+            app:showRingCorner="true" />
+
+        <TextView
+            android:id="@+id/ped_number_tv"
+            android:includeFontPadding="false"
+            android:layout_gravity="center_horizontal"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="90dp"
+            android:layout_width="wrap_content"
+            android:text="@{pedNumber}"
+            android:textColor="#FCBE03"
+            android:textSize="72sp" />
+
+        <TextView
+            android:id="@+id/ped_date_tv"
+            android:layout_gravity="center_horizontal"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="60dp"
+            android:layout_width="wrap_content"
+            android:text="@string/ped_main_today"
+            android:textColor="#FFFFFF"
+            android:textSize="36sp" />
+
+        <TextView
+            android:includeFontPadding="false"
+            android:layout_gravity="center_horizontal|bottom"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="90dp"
+            android:layout_width="wrap_content"
+            android:text="@string/ped_main_step"
+            android:textColor="#90FFFFFF"
+            android:textSize="36sp" />
+
+        <TextView
+            android:drawableTop="@drawable/ic_red_flag"
+            android:layout_gravity="center_horizontal|bottom"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dp"
+            android:layout_width="wrap_content"
+            android:drawablePadding="1dp"
+            android:text="@{pedTarget}"
+            android:textColor="#FFFFFF"
+            android:textSize="32sp" />
+    </FrameLayout>
+</layout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_ped_record.xml b/app/src/main/res/layout/fragment_ped_record.xml
new file mode 100644 (file)
index 0000000..cdff5c3
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@mipmap/qs_background"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    tools:context=".fragment.pedometer.PedRecordFragment">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/ped_record_name"
+        android:textColor="#FFFFFF"
+        android:textSize="42sp"
+        android:layout_marginTop="10dp"/>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/ped_record_rv"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="10dp"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_ped_setup.xml b/app/src/main/res/layout/fragment_ped_setup.xml
new file mode 100644 (file)
index 0000000..384fdfa
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@mipmap/qs_background"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    tools:context=".fragment.pedometer.PedSetupFragment">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/ped_head_name"
+        android:textColor="#FFFFFF"
+        android:textSize="42sp"
+        android:layout_marginTop="10dp"/>
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/ped_setup_rv"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="10dp"
+        android:overScrollMode="never"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_weather.xml b/app/src/main/res/layout/fragment_weather.xml
new file mode 100644 (file)
index 0000000..8121684
--- /dev/null
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+        <import type="android.graphics.drawable.Drawable" />
+
+        <import type="android.view.View"/>
+
+        <variable
+            name="weatherIcon"
+            type="Drawable" />
+
+        <variable
+            name="weatherState"
+            type="String" />
+
+        <variable
+            name="weatherTemp"
+            type="String" />
+
+        <variable
+            name="weatherCity"
+            type="String" />
+
+        <variable
+            name="weatherSection"
+            type="String" />
+
+        <variable
+            name="isErrorTips"
+            type="String" />
+
+        <variable
+            name="isShowTips"
+            type="boolean" />
+    </data>
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/mainBgColor"
+        tools:context=".fragment.WeatherFragment">
+
+        <ImageView
+            android:id="@+id/weather_icon_iv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_marginTop="20dp"
+            android:src="@{weatherIcon}" />
+
+        <TextView
+            android:id="@+id/weather_tips_iv"
+            style="@style/WeatherTextStyle"
+            android:layout_below="@id/weather_icon_iv"
+            android:layout_centerHorizontal="true"
+            android:text="@{isErrorTips}"
+            android:textSize="36sp"
+            android:visibility="@{isShowTips ? View.VISIBLE : View.GONE}"/>
+
+        <TextView
+            android:id="@+id/weather_state_tv"
+            style="@style/WeatherTextStyle"
+            android:layout_below="@id/weather_icon_iv"
+            android:layout_centerHorizontal="true"
+            android:text="@{weatherState}"
+            android:textSize="52sp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}"/>
+
+        <View
+            android:id="@+id/weather_sp"
+            android:layout_width="1dp"
+            android:layout_height="40dp"
+            android:layout_below="@id/weather_state_tv"
+            android:layout_centerHorizontal="true"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}"/>
+
+        <TextView
+            style="@style/WeatherTextStyle"
+            android:layout_below="@id/weather_state_tv"
+            android:layout_marginEnd="5dp"
+            android:layout_toStartOf="@id/weather_sp"
+            android:text="@{weatherTemp}"
+            android:textColor="#FF9800"
+            android:textSize="62sp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}"/>
+
+        <TextView
+            android:id="@+id/weather_city_tv"
+            style="@style/WeatherTextStyle"
+            android:layout_below="@id/weather_state_tv"
+            android:layout_marginStart="5dp"
+            android:layout_toEndOf="@id/weather_sp"
+            android:text="@{weatherCity}"
+            android:textSize="30sp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}"/>
+
+        <TextView
+            style="@style/WeatherTextStyle"
+            android:layout_below="@id/weather_city_tv"
+            android:layout_marginStart="5dp"
+            android:layout_toEndOf="@id/weather_sp"
+            android:text="@{weatherSection}"
+            android:textSize="30sp"
+            android:visibility="@{isShowTips ? View.GONE : View.VISIBLE}"/>
+    </RelativeLayout>
+</layout>
diff --git a/app/src/main/res/layout/item_indicator_layout.xml b/app/src/main/res/layout/item_indicator_layout.xml
new file mode 100644 (file)
index 0000000..8ca6c6d
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <data>
+
+        <variable
+            name="isCheckItem"
+            type="boolean" />
+    </data>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@{isCheckItem ? @drawable/ic_white_circle : @drawable/ic_transparent_circle}" />
+    </LinearLayout>
+</layout>
diff --git a/app/src/main/res/layout/item_ped_record.xml b/app/src/main/res/layout/item_ped_record.xml
new file mode 100644 (file)
index 0000000..d0e1cd9
--- /dev/null
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:icApp="http://schemas.android.com/tools">
+
+    <data>
+
+        <variable
+            name="recordDate"
+            type="String" />
+
+        <variable
+            name="recordTaking"
+            type="String" />
+
+        <variable
+            name="takingIcon"
+            type="int" />
+
+        <variable
+            name="takingColor"
+            type="int" />
+
+        <variable
+            name="takingUnit"
+            type="String" />
+
+        <variable
+            name="recordCalories"
+            type="String" />
+
+        <variable
+            name="caloriesIcon"
+            type="int" />
+
+        <variable
+            name="caloriesColor"
+            type="int" />
+
+        <variable
+            name="caloriesUnit"
+            type="String" />
+
+        <variable
+            name="recordDistance"
+            type="String" />
+
+        <variable
+            name="distanceIcon"
+            type="int" />
+
+        <variable
+            name="distanceUnit"
+            type="String" />
+
+        <variable
+            name="distanceColor"
+            type="int" />
+
+    </data>
+
+    <com.google.android.material.card.MaterialCardView
+        style="@style/Widget.MaterialComponents.CardView"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:layout_marginStart="10dp"
+        android:layout_marginEnd="10dp"
+        app:cardBackgroundColor="#305C5C5C"
+        app:cardCornerRadius="25dp">
+
+        <TextView
+            android:id="@+id/item_record_date"
+            style="@style/RecordTextStyle"
+            android:layout_marginStart="5dp"
+            android:layout_marginTop="5dp"
+            android:text="@{recordDate}" />
+
+        <com.ic.hardware.widget.WidgetImageTextLayout
+            android:id="@+id/item_record_taking"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginStart="5dp"
+            icApp:widgetImageIcon="@{takingIcon}"
+            icApp:widgetImageUnit="@{takingUnit}"
+            icApp:widgetImageColor="@{takingColor}"
+            icApp:widgetImageValue="@{recordTaking}" />
+
+        <com.ic.hardware.widget.WidgetImageTextLayout
+            android:id="@+id/item_record_calories"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:layout_marginStart="5dp"
+            icApp:widgetImageIcon="@{caloriesIcon}"
+            icApp:widgetImageUnit="@{caloriesUnit}"
+            icApp:widgetImageColor="@{caloriesColor}"
+            icApp:widgetImageValue="@{recordCalories}"
+            android:layout_marginTop="5dp"
+            android:layout_marginEnd="5dp"/>
+
+
+        <com.ic.hardware.widget.WidgetImageTextLayout
+            android:id="@+id/item_record_distance"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end|bottom"
+            icApp:widgetImageIcon="@{distanceIcon}"
+            icApp:widgetImageUnit="@{distanceUnit}"
+            icApp:widgetImageColor="@{distanceColor}"
+            icApp:widgetImageValue="@{recordDistance}"
+            android:layout_marginEnd="5dp"/>
+
+    </com.google.android.material.card.MaterialCardView>
+</layout>
diff --git a/app/src/main/res/layout/item_ped_setup.xml b/app/src/main/res/layout/item_ped_setup.xml
new file mode 100644 (file)
index 0000000..d66053f
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <data>
+        <variable
+            name="setupItemValue"
+            type="String" />
+
+        <variable
+            name="setupItemTitle"
+            type="String" />
+
+        <variable
+            name="setupItemUnit"
+            type="String" />
+
+        <variable
+            name="setupItemIcon"
+            type="android.graphics.drawable.Drawable" />
+
+        <variable
+            name="itemPos"
+            type="int" />
+
+        <variable
+            name="onItemClick"
+            type="com.ic.hardware.fragment.pedometer.PedSetupFragment.OnPedSetupItemClickListener" />
+    </data>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="70dp"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:onClick="@{()->onItemClick.onPedSetupItem(itemPos)}">
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@{setupItemIcon}"
+            android:layout_marginStart="10dp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{setupItemTitle}"
+            android:textColor="#50FFFFFF"
+            android:textSize="36sp"
+            android:layout_marginStart="5dp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{setupItemValue}"
+            android:textColor="#80FFFFFF"
+            android:textSize="36sp"
+            android:layout_marginStart="5dp"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@{setupItemUnit}"
+            android:textColor="#80FFFFFF"
+            android:textSize="36sp"
+            android:layout_marginStart="5dp"/>
+
+    </LinearLayout>
+</layout>
diff --git a/app/src/main/res/layout/widget_image_text_layout.xml b/app/src/main/res/layout/widget_image_text_layout.xml
new file mode 100644 (file)
index 0000000..cab81c0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:id="@+id/widget_image_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <TextView
+        style="@style/RecordTextStyle"
+        android:id="@+id/widget_image_value"
+        android:layout_marginStart="5dp"/>
+
+    <TextView
+        style="@style/RecordTextStyle"
+        android:id="@+id/widget_image_unit"
+        android:layout_marginStart="5dp"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644 (file)
index 0000000..eca70cf
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644 (file)
index 0000000..eca70cf
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_launcher_background" />
+    <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..898f3ed
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..dffca36
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_cancel.png b/app/src/main/res/mipmap-mdpi/ic_cancel.png
new file mode 100644 (file)
index 0000000..29a4bc8
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_cancel.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..64ba76f
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..dae5e08
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_ok.png b/app/src/main/res/mipmap-mdpi/ic_ok.png
new file mode 100644 (file)
index 0000000..5001e57
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_ok.png differ
diff --git a/app/src/main/res/mipmap-mdpi/qs_background.png b/app/src/main/res/mipmap-mdpi/qs_background.png
new file mode 100644 (file)
index 0000000..1bc9fbd
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/qs_background.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_baoxue.png b/app/src/main/res/mipmap-mdpi/weather_status_baoxue.png
new file mode 100644 (file)
index 0000000..4257a7b
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_baoxue.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_baoyu.png b/app/src/main/res/mipmap-mdpi/weather_status_baoyu.png
new file mode 100644 (file)
index 0000000..ad1d7e7
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_baoyu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_bingbao.png b/app/src/main/res/mipmap-mdpi/weather_status_bingbao.png
new file mode 100644 (file)
index 0000000..91005e2
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_bingbao.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_daxue.png b/app/src/main/res/mipmap-mdpi/weather_status_daxue.png
new file mode 100644 (file)
index 0000000..f9748b8
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_daxue.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_dayu.png b/app/src/main/res/mipmap-mdpi/weather_status_dayu.png
new file mode 100644 (file)
index 0000000..ea95f73
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_dayu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_duoyun.png b/app/src/main/res/mipmap-mdpi/weather_status_duoyun.png
new file mode 100644 (file)
index 0000000..73c5477
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_duoyun.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_leizhenyu.png b/app/src/main/res/mipmap-mdpi/weather_status_leizhenyu.png
new file mode 100644 (file)
index 0000000..7a22ef5
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_leizhenyu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_none.png b/app/src/main/res/mipmap-mdpi/weather_status_none.png
new file mode 100644 (file)
index 0000000..e2805a8
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_none.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_qing.png b/app/src/main/res/mipmap-mdpi/weather_status_qing.png
new file mode 100644 (file)
index 0000000..cbc167d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_qing.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_qingwu.png b/app/src/main/res/mipmap-mdpi/weather_status_qingwu.png
new file mode 100644 (file)
index 0000000..db48257
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_qingwu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_qingyu.png b/app/src/main/res/mipmap-mdpi/weather_status_qingyu.png
new file mode 100644 (file)
index 0000000..50edb91
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_qingyu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_qingyun.png b/app/src/main/res/mipmap-mdpi/weather_status_qingyun.png
new file mode 100644 (file)
index 0000000..925d672
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_qingyun.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_shachenbao.png b/app/src/main/res/mipmap-mdpi/weather_status_shachenbao.png
new file mode 100644 (file)
index 0000000..76dae87
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_shachenbao.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_wu.png b/app/src/main/res/mipmap-mdpi/weather_status_wu.png
new file mode 100644 (file)
index 0000000..6294853
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_wu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_xiaoxue.png b/app/src/main/res/mipmap-mdpi/weather_status_xiaoxue.png
new file mode 100644 (file)
index 0000000..2e5f312
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_xiaoxue.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_xiaoyu.png b/app/src/main/res/mipmap-mdpi/weather_status_xiaoyu.png
new file mode 100644 (file)
index 0000000..f0b6e1d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_xiaoyu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_yangsha.png b/app/src/main/res/mipmap-mdpi/weather_status_yangsha.png
new file mode 100644 (file)
index 0000000..22c5b41
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_yangsha.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_yin.png b/app/src/main/res/mipmap-mdpi/weather_status_yin.png
new file mode 100644 (file)
index 0000000..09bbd4d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_yin.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_yinduoyun.png b/app/src/main/res/mipmap-mdpi/weather_status_yinduoyun.png
new file mode 100644 (file)
index 0000000..7a97d25
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_yinduoyun.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_yinyu.png b/app/src/main/res/mipmap-mdpi/weather_status_yinyu.png
new file mode 100644 (file)
index 0000000..e90d876
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_yinyu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_zhenyu.png b/app/src/main/res/mipmap-mdpi/weather_status_zhenyu.png
new file mode 100644 (file)
index 0000000..526af7f
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_zhenyu.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_zhongxue.png b/app/src/main/res/mipmap-mdpi/weather_status_zhongxue.png
new file mode 100644 (file)
index 0000000..54fd365
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_zhongxue.png differ
diff --git a/app/src/main/res/mipmap-mdpi/weather_status_zhongyu.png b/app/src/main/res/mipmap-mdpi/weather_status_zhongyu.png
new file mode 100644 (file)
index 0000000..d8009a3
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/weather_status_zhongyu.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..e5ed465
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..14ed0af
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..b0907ca
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..d8ae031
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644 (file)
index 0000000..2c18de9
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644 (file)
index 0000000..beed3cd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
new file mode 100644 (file)
index 0000000..822d36a
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <declare-styleable name="RingProgress">
+        <attr name="bgColor" format="color|reference" />
+        <attr name="bgShadowColor" format="color|reference"/>
+        <attr name="rotate" format="integer"/>
+        <attr name="showBackground" format="boolean"/>
+        <attr name="showBackgroundShadow" format="boolean"/>
+        <attr name="showRingCorner" format="boolean"/>
+        <attr name="ringWidthScale" format="float"/>
+        <attr name="ringSweepAngle" format="integer"/>
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644 (file)
index 0000000..4cf8613
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
+
+
+    <color name="transparent">#00000000</color>
+    <color name="mainBgColor">#000000</color>
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644 (file)
index 0000000..0b6bcc9
--- /dev/null
@@ -0,0 +1,40 @@
+<resources>
+    <string name="app_name">IcHardware</string>
+
+    <!-- TODO: Remove or change this placeholder text -->
+    <string name="hello_blank_fragment">Hello blank fragment</string>
+
+    <string name="ic_high_pressure">高压</string>
+    <string name="ic_low_pressure">低压</string>
+    <string name="ic_heart_rate">心率</string>
+
+    <string name="tips_not_device">未检测到设备</string>
+    <string name="tips_not_wear">未正确佩戴</string>
+
+    <string name="tips_quiet_please">请保持安静</string>
+    <string name="tips_start_check">准备测量</string>
+    <string name="tips_ing_check">正在测量中</string>
+    <string name="tips_stop_check">已停止测量</string>
+    <string name="tips_not_data_check">未测量到数据</string>
+
+    <string name="ped_head_name">个人设置</string>
+    <string name="ped_height_name">身高</string>
+    <string name="tips_weight_name">体重</string>
+    <string name="tips_target_name">目标</string>
+
+    <string name="tips_height_unit">厘米</string>
+    <string name="tips_weight_unit">千克</string>
+    <string name="tips_target_unit">步</string>
+
+    <string name="ped_record_name">最近记录</string>
+    <string name="tips_calories_unit">大卡</string>
+    <string name="tips_distance_unit">公里</string>
+
+    <string name="tips_error_weather_no_data">未获取到天气</string>
+
+    <string name="weather_temp_degree">%1$s℃</string>
+    <string name="weather_temp_space_degree">%1$s~%2$s℃</string>
+
+    <string name="ped_main_today">今日</string>
+    <string name="ped_main_step">步</string>
+</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644 (file)
index 0000000..daefc08
--- /dev/null
@@ -0,0 +1,33 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+    <style name="bloodTextStyle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textColor">#FFFFFF</item>
+        <item name="android:textSize">42sp</item>
+    </style>
+
+    <style name="RecordTextStyle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textColor">#FFFFFF</item>
+        <item name="android:textSize">24sp</item>
+        <item name="android:includeFontPadding">false</item>
+    </style>
+
+    <style name="WeatherTextStyle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:textColor">#FFFFFF</item>
+        <item name="android:includeFontPadding">false</item>
+    </style>
+
+</resources>
diff --git a/app/src/test/java/com/ic/hardware/ExampleUnitTest.java b/app/src/test/java/com/ic/hardware/ExampleUnitTest.java
new file mode 100644 (file)
index 0000000..7086040
--- /dev/null
@@ -0,0 +1,17 @@
+package com.ic.hardware;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644 (file)
index 0000000..fb516be
--- /dev/null
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        google()
+        jcenter()
+        
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.5.2'
+        
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+        
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644 (file)
index 0000000..199d16e
--- /dev/null
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644 (file)
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644 (file)
index 0000000..b349785
--- /dev/null
@@ -0,0 +1,6 @@
+#Tue Mar 17 17:06:55 CST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644 (file)
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644 (file)
index 0000000..f955316
--- /dev/null
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644 (file)
index 0000000..31300e9
--- /dev/null
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name='IcHardware'