--- /dev/null
+*.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
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+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'
+}
--- /dev/null
+# 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
--- /dev/null
+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());
+ }
+}
--- /dev/null
+<?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
--- /dev/null
+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;
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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);
+
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+package com.ic.aidl.ped;
+
+parcelable RecordParcelable;
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
--- /dev/null
+// 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);
+
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+package com.ic.aidl.weather;
+
+parcelable WeatherParcelable;
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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();
+ }
+
+
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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();
+ }
+
+}
--- /dev/null
+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();
+ }
+ }
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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();
+ }
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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();
+ }
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+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());
+ }
+}
--- /dev/null
+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);
+ }
+
+}
+
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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));
+ }
+ };
+
+
+}
--- /dev/null
+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);
+ }
+ };
+}
--- /dev/null
+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);
+ }
+ };
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+package com.ic.hardware.widget.ring;
+
+public interface OnSelectRing {
+ void Selected(Ring r);
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<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>
--- /dev/null
+<?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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<?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>
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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>
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+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
--- /dev/null
+// 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
+}
--- /dev/null
+# 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
+
--- /dev/null
+#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
--- /dev/null
+#!/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" "$@"
--- /dev/null
+@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
--- /dev/null
+include ':app'
+rootProject.name='IcHardware'