From 0c89bf11bcddd39b5193bb19e28399648c59a2b8 Mon Sep 17 00:00:00 2001
From: nilupeng <qingingrunt2010@qq.com>
Date: Sat, 29 Jan 2022 11:04:25 +0000
Subject: [PATCH] 登录界面及接口

---
 app/src/main/java/com/runt/open/mvvm/base/activities/BaseActivity.java                |  129 +++
 app/src/main/java/com/runt/open/mvvm/MyApplication.java                               |   11 
 app/src/main/res/values/styles.xml                                                    |   44 +
 app/src/main/res/values/themes.xml                                                    |    5 
 app/src/main/java/com/runt/open/mvvm/retrofit/api/LoginApiCenter.java                 |   49 +
 app/src/main/java/com/runt/open/mvvm/data/BaseApiResult.java                          |    1 
 app/src/main/java/com/runt/open/mvvm/ui/web/WebViewActivity.java                      |  159 +++
 app/src/main/java/com/runt/open/mvvm/util/PhoneUtil.java                              |   77 +
 app/src/main/java/com/runt/open/mvvm/listener/CrashHandler.java                       |  260 ++++++
 app/src/main/java/com/runt/open/mvvm/ui/login/CodeTimer.java                          |   40 +
 app/src/main/java/com/runt/open/mvvm/ui/login/LoginViewModel.java                     |  132 +++
 app/src/main/java/com/runt/open/mvvm/retrofit/Interceptor/HttpLoggingInterceptor.java |    2 
 app/src/main/java/com/runt/open/mvvm/util/PreferencesUtils.java                       |  352 ++++++++
 app/src/main/res/values/strings.xml                                                   |   67 +
 app/src/main/java/com/runt/open/mvvm/MainActivity.java                                |   16 
 app/src/main/AndroidManifest.xml                                                      |    2 
 app/src/main/java/com/runt/open/mvvm/retrofit/observable/HttpObserver.java            |    7 
 app/src/main/res/color/btn_txt_normal.xml                                             |    9 
 /dev/null                                                                             |   48 -
 app/src/main/java/com/runt/open/mvvm/config/Configuration.java                        |   16 
 app/src/main/res/layout/activity_login.xml                                            |  138 +++
 app/src/main/res/layout/activity_web.xml                                              |   40 +
 app/src/main/java/com/runt/open/mvvm/data/Results.java                                |   16 
 app/src/main/java/com/runt/open/mvvm/util/MyAnimations.java                           |  477 +++++++++++
 app/src/main/java/com/runt/open/mvvm/ui/login/RegisterLoginActivity.java              |  287 +++++++
 25 files changed, 2,322 insertions(+), 62 deletions(-)

diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1b81d3c..4e8fded 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -51,6 +51,8 @@
                 <action android:name="com.zfwl.merchant.activities.MainActivity" />
             </intent-filter>
         </activity>
+        <activity android:name=".ui.login.RegisterLoginActivity" />
+        <activity android:name=".ui.web.WebViewActivity" />
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/com/runt/open/mvvm/MainActivity.java b/app/src/main/java/com/runt/open/mvvm/MainActivity.java
index 6871cf3..1297088 100644
--- a/app/src/main/java/com/runt/open/mvvm/MainActivity.java
+++ b/app/src/main/java/com/runt/open/mvvm/MainActivity.java
@@ -1,7 +1,12 @@
 package com.runt.open.mvvm;
 
 import android.Manifest;
+import android.content.Intent;
 
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultCallback;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.navigation.NavController;
 import androidx.navigation.Navigation;
 import androidx.navigation.ui.NavigationUI;
@@ -11,6 +16,7 @@
 import com.runt.open.mvvm.data.PhoneDevice;
 import com.runt.open.mvvm.databinding.ActivityMainBinding;
 import com.runt.open.mvvm.listener.ResPonse;
+import com.runt.open.mvvm.ui.login.RegisterLoginActivity;
 import com.runt.open.mvvm.ui.main.MainViewModel;
 
 public class MainActivity extends BaseActivity<ActivityMainBinding, MainViewModel> {
@@ -20,6 +26,16 @@
         NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
         NavigationUI.setupWithNavController(binding.navView, navController);
         checkPermission();
+        ActivityResultLauncher<Intent>  launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
+            @Override
+            public void onActivityResult(ActivityResult result) {
+                if(result.getResultCode() == RESULT_CODE_SUCESS){
+                    showToast("登录成功");
+                }
+            }
+        });
+        Intent intent = new Intent(mContext, RegisterLoginActivity.class);
+        launcher.launch(intent);
     }
 
     private void showPermissionDialog(){
diff --git a/app/src/main/java/com/runt/open/mvvm/MyApplication.java b/app/src/main/java/com/runt/open/mvvm/MyApplication.java
index ab1a97f..30f98b9 100644
--- a/app/src/main/java/com/runt/open/mvvm/MyApplication.java
+++ b/app/src/main/java/com/runt/open/mvvm/MyApplication.java
@@ -11,6 +11,7 @@
 import com.bytedance.sdk.openadsdk.TTAdConfig;
 import com.bytedance.sdk.openadsdk.TTAdConstant;
 import com.bytedance.sdk.openadsdk.TTAdSdk;
+import com.runt.open.mvvm.listener.CrashHandler;
 import com.runt.open.mvvm.util.MyLog;
 import com.scwang.smart.refresh.footer.ClassicsFooter;
 import com.scwang.smart.refresh.header.ClassicsHeader;
@@ -137,6 +138,16 @@
                 MyLog.e(TAG,"TTAdSdk fail");
             }
         });
+        CrashHandler crashHandler = CrashHandler.getInstance();
+        crashHandler.init(getApplicationContext(), new CrashHandler.CrashListener() {
+            @Override
+            public void onCrash() {
+                for(Activity activity : activities){
+                    activity.finish();
+                }
+                System.exit(0);
+            }
+        });
     }
 
     /**
diff --git a/app/src/main/java/com/runt/open/mvvm/base/activities/BaseActivity.java b/app/src/main/java/com/runt/open/mvvm/base/activities/BaseActivity.java
index 5d775df..1629d1c 100644
--- a/app/src/main/java/com/runt/open/mvvm/base/activities/BaseActivity.java
+++ b/app/src/main/java/com/runt/open/mvvm/base/activities/BaseActivity.java
@@ -28,10 +28,12 @@
 import com.runt.open.mvvm.base.model.BaseViewModel;
 import com.runt.open.mvvm.base.model.ViewModelFactory;
 import com.runt.open.mvvm.listener.ResPonse;
+import com.runt.open.mvvm.util.PreferencesUtils;
 
 import java.io.File;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
+import java.util.Set;
 
 import dmax.dialog.SpotsDialog;
 
@@ -48,6 +50,9 @@
     public final String[] LOCATION_PERMISSIONS = new String []{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION};
     public final String[] CAMERA_PERMISSIONS = new String[]{ FILE_PERMISSIONS[0],FILE_PERMISSIONS[1], Manifest.permission.CAMERA};
     public final String[] CAMERA_RECORD_PERMISSIONS = new String[]{ FILE_PERMISSIONS[0],FILE_PERMISSIONS[1], Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO};
+
+    public static final String PARAMS_TITLE = "title";
+    public static  final String PARAMS_URL = "url";
 
     public static final int RESULT_LOGIN = 100,RESULT_LOGIN_RECREATE = 103,RESULT_BIND = 101,RESULT_SENDEDFILES = 105,RESULT_DISSCONNECT = 104,
             RESULT_UPDATEUSER =  115,RESULT_LOGOUT = 113, REQUEST_CODE_ACTIVITY = 333;
@@ -366,4 +371,128 @@
         return false;
     }
 
+
+    public boolean getBooleanUserPrefrence(String key){
+        return PreferencesUtils.getBoolean(this,key,false,PreferencesUtils.USER);
+    }
+
+    public boolean getBooleanProjectPrefrence(String key){
+        return PreferencesUtils.getBoolean(this,key,false,PreferencesUtils.PROJECT);
+    }
+
+    public String getStringUserPrefrence(String key){
+        return PreferencesUtils.getString(this,key,"",PreferencesUtils.USER);
+    }
+
+    public String getStringProjectPrefrence(String key){
+        return PreferencesUtils.getString(this,key,"",PreferencesUtils.PROJECT);
+    }
+
+    public Integer getIntProjectPrefrence(String key){
+        return PreferencesUtils.getInt(this,key,0,PreferencesUtils.PROJECT);
+    }
+
+    public Long getLongProjectPrefrence(String key){
+        return PreferencesUtils.getLong(this,key,0,PreferencesUtils.PROJECT);
+    }
+
+    public float getFloatProjectPrefrence(String key){
+        return PreferencesUtils.getFloat(this,key,0,PreferencesUtils.PROJECT);
+    }
+
+    public Set getStringSetProjectPrefrence(String key){
+        return PreferencesUtils.getStringSet(this,key,PreferencesUtils.PROJECT);
+    }
+
+    public Integer getIntUserPrefrence(String key){
+        return PreferencesUtils.getInt(this,key,0,PreferencesUtils.USER);
+    }
+
+    public Long getLongUserPrefrence(String key){
+        return PreferencesUtils.getLong(this,key,0,PreferencesUtils.USER);
+    }
+
+    public float getFloatUserPrefrence(String key){
+        return PreferencesUtils.getFloat(this,key,0,PreferencesUtils.USER);
+    }
+
+    public Set getStringSetUserPrefrence(String key){
+        return PreferencesUtils.getStringSet(this,key,PreferencesUtils.USER);
+    }
+
+
+    public void putBooleanUserPrefrence(String key ,Boolean value){
+        PreferencesUtils.putBoolean(this,key,value,PreferencesUtils.USER);
+    }
+
+    public void putBooleanProjectPrefrence(String key,Boolean value){
+        PreferencesUtils.putBoolean(this,key,value,PreferencesUtils.PROJECT);
+    }
+
+    public void putStringUserPrefrence(String key,String value){
+        PreferencesUtils.putString(this,key,value,PreferencesUtils.USER);
+    }
+
+    public void putStringProjectPrefrence(String key,String value){
+        PreferencesUtils.putString(this,key,value,PreferencesUtils.PROJECT);
+    }
+
+    public void putIntProjectPrefrence(String key,int value){
+        PreferencesUtils.putInt(this,key,value,PreferencesUtils.PROJECT);
+    }
+
+    public void putLongProjectPrefrence(String key,long value){
+        PreferencesUtils.putLong(this,key,value,PreferencesUtils.PROJECT);
+    }
+
+    public void putFloatProjectPrefrence(String key,float value){
+        PreferencesUtils.putFloat(this,key,value,PreferencesUtils.PROJECT);
+    }
+
+    public void putStringSetProjectPrefrence(String key, Set value){
+        PreferencesUtils.putStringSet(this,key,value,PreferencesUtils.PROJECT);
+    }
+
+    public void putIntUserPrefrence(String key,int value){
+        PreferencesUtils.putInt(this,key,value,PreferencesUtils.USER);
+    }
+
+    public void putLongUserPrefrence(String key,long value){
+        PreferencesUtils.putLong(this,key,value,PreferencesUtils.USER);
+    }
+
+    public void putFloatUserPrefrence(String key,float value){
+        PreferencesUtils.putFloat(this,key,value,PreferencesUtils.USER);
+    }
+
+    public void putStringSetUserPrefrence(String key, Set value){
+        PreferencesUtils.putStringSet(this,key,value,PreferencesUtils.USER);
+    }
+
+
+    public void removeUserKey(String key){
+        PreferencesUtils.removeKey(this,key,PreferencesUtils.USER);
+    }
+
+    public void removeProjectKey(String key){
+        PreferencesUtils.removeKey(this,key,PreferencesUtils.PROJECT);
+    }
+
+    public void removeUserValue(String Value){
+        PreferencesUtils.removeValue(this,Value,PreferencesUtils.USER);
+    }
+
+    public void removeProjectValue(String Value){
+        PreferencesUtils.removeValue(this,Value,PreferencesUtils.PROJECT);
+    }
+
+
+    public void clearProjectData(){
+        PreferencesUtils.clearData(this,PreferencesUtils.PROJECT);
+    }
+    public void clearUserData(){
+        PreferencesUtils.clearData(this,PreferencesUtils.USER);
+    }
+
+
 }
diff --git a/app/src/main/java/com/runt/open/mvvm/config/Configuration.java b/app/src/main/java/com/runt/open/mvvm/config/Configuration.java
new file mode 100644
index 0000000..e865ba8
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/config/Configuration.java
@@ -0,0 +1,16 @@
+package com.runt.open.mvvm.config;
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/1/29.
+ */
+public class Configuration {
+
+    public final static String KEY_CODE= "code";
+    public static final String KEY_TOKEN = "token";
+    public static final String KEY_USERNAME = "username";
+    public static final String KEY_PHONE = "phone";
+    public static final String KEY_USERPASS = "userpass";
+    public final static String IS_LOGIN= "is_login";
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/data/BaseApiResult.java b/app/src/main/java/com/runt/open/mvvm/data/BaseApiResult.java
index 9b758bf..8ddd410 100644
--- a/app/src/main/java/com/runt/open/mvvm/data/BaseApiResult.java
+++ b/app/src/main/java/com/runt/open/mvvm/data/BaseApiResult.java
@@ -6,6 +6,7 @@
  * Created by Administrator on 2021/10/28 0028.
  */
 public class BaseApiResult<D extends Object> implements Serializable {
+
     public String msg;
     public int code = 200;
     public D data;
diff --git a/app/src/main/java/com/runt/open/mvvm/data/Results.java b/app/src/main/java/com/runt/open/mvvm/data/Results.java
new file mode 100644
index 0000000..fc55568
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/data/Results.java
@@ -0,0 +1,16 @@
+package com.runt.open.mvvm.data;
+
+import com.runt.open.mvvm.ui.login.UserBean;
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/1/29.
+ */
+public class Results {
+
+    public static class LoggedInUser extends BaseApiResult<UserBean> { }
+
+    public static class StringApiResult extends BaseApiResult<String>{ }
+
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/listener/CrashHandler.java b/app/src/main/java/com/runt/open/mvvm/listener/CrashHandler.java
new file mode 100644
index 0000000..a837be7
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/listener/CrashHandler.java
@@ -0,0 +1,260 @@
+package com.runt.open.mvvm.listener;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * My father is Object, ites purpose of   崩溃监听
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-4-13.
+ */
+
+public class CrashHandler implements Thread.UncaughtExceptionHandler {
+
+    public static final String TAG = "CrashHandler";
+
+    //系统默认的UncaughtException处理类
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+
+    CrashListener crashListener;
+
+    //CrashHandler实例
+    private static CrashHandler instance;
+    //程序的Context对象
+    private Context mContext;
+    //用来存储设备信息和异常信息
+    private Map<String, String> infos = new HashMap<String, String>();
+
+    //用于格式化日期,作为日志文件名的一部分
+    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    /** 保证只有一个CrashHandler实例 */
+    private CrashHandler() {}
+
+    /** 获取CrashHandler实例 ,单例模式 */
+    public static CrashHandler getInstance() {
+        Log.i(TAG, "getInstance");
+        if(instance == null)
+            instance = new CrashHandler();
+        return instance;
+    }
+
+    /**
+     * 初始化
+     */
+    public void init(Context context) {
+        Log.i(TAG, "init context:"+context);
+        mContext = context;
+        //获取系统默认的UncaughtException处理器
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        //设置该CrashHandler为程序的默认处理器
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * 初始化
+     */
+    public void init(Context context,CrashListener crashListener) {
+        Log.i(TAG, "init context:"+context);
+        mContext = context;
+        this.crashListener = crashListener;
+        //设置该CrashHandler为程序的默认处理器
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * 当UncaughtException发生时会转入该函数来处理
+     */
+    @Override
+    public void uncaughtException(Thread thread, Throwable ex) {
+        Log.i(TAG, "uncaughtException Throwable:"+ex);
+        if (!handleException(ex) && mDefaultHandler != null) {
+            //如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, ex);
+        } else if(crashListener != null){
+            crashListener.onCrash();
+        } else {
+            try {
+                Thread.sleep(3000);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "error : ", e);
+            }
+            //退出程序
+            android.os.Process.killProcess(android.os.Process.myPid());
+            System.exit(1);
+        }
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回false.
+     */
+    private boolean handleException(Throwable ex) {
+        Log.i(TAG, "handleException Throwable:"+ex);
+        if (ex == null) {
+            return false;
+        }
+        //收集设备参数信息
+        collectDeviceInfo(mContext);
+
+        //使用Toast来显示异常信息
+        new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_SHORT).show();
+                Looper.loop();
+            }
+        }.start();
+        //保存日志文件
+        saveCatchInfo2File(ex);
+        return true;
+    }
+
+    /**
+     * 收集设备参数信息
+     * @param ctx
+     */
+    public void collectDeviceInfo(Context ctx) {
+        Log.i(TAG, "collectDeviceInfo Context:"+ctx);
+        try {
+            PackageManager pm = ctx.getPackageManager();
+            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
+            if (pi != null) {
+                String versionName = pi.versionName == null ? "null" : pi.versionName;
+                String versionCode = pi.versionCode + "";
+                infos.put("versionName", versionName);
+                infos.put("versionCode", versionCode);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "an error occured when collect package info", e);
+        }
+        Field[] fields = Build.class.getDeclaredFields();
+        for (Field field : fields) {
+            try {
+                field.setAccessible(true);
+                infos.put(field.getName(), field.get(null).toString());
+                Log.d(TAG, field.getName() + " : " + field.get(null));
+            } catch (Exception e) {
+                Log.e(TAG, "an error occured when collect crash info", e);
+            }
+        }
+    }
+
+    /**
+     * 保存错误信息到文件中
+     *
+     * @param ex
+     * @return  返回文件名称,便于将文件传送到服务器
+     */
+    private String saveCatchInfo2File(Throwable ex) {
+        ex.printStackTrace();
+        Log.i(TAG, "saveCatchInfo2File Throwable:"+ex);
+
+        StringBuffer sb = new StringBuffer();
+        for (Map.Entry<String, String> entry : infos.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            sb.append(key + "=" + value + "\n");
+        }
+
+        Writer writer = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(writer);
+        ex.printStackTrace(printWriter);
+        Throwable cause = ex.getCause();
+        while (cause != null) {
+            cause.printStackTrace(printWriter);
+            cause = cause.getCause();
+        }
+        printWriter.close();
+        String result = writer.toString();
+        sb.append(result);
+        try {
+            long timestamp = System.currentTimeMillis();
+            String time = formatter.format(new Date());
+            String fileName = "crash-" + time + "-" + timestamp + ".log";
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                String path = "/mnt/sdcard/crash/";
+                File dir = new File(path);
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+                FileOutputStream fos = new FileOutputStream(path + fileName);
+                fos.write(sb.toString().getBytes());
+                //发送给开发人员
+                sendCrashLog2PM(path+fileName);
+                fos.close();
+            }
+            return fileName;
+        } catch (Exception e) {
+            Log.e(TAG, "an error occured while writing file...", e);
+        }
+        return null;
+    }
+
+    /**
+     * 将捕获的导致崩溃的错误信息发送给开发人员
+     *
+     * 目前只将log日志保存在sdcard 和输出到LogCat中,并未发送给后台。
+     */
+    private void sendCrashLog2PM(String fileName){
+        Log.i(TAG, "asendCrashLog2PM fileName:"+fileName);
+        if(!new File(fileName).exists()){
+            Toast.makeText(mContext, "日志文件不存在!", Toast.LENGTH_SHORT).show();
+            return;
+        }
+        FileInputStream fis = null;
+        BufferedReader reader = null;
+        String s = null;
+        try {
+            fis = new FileInputStream(fileName);
+            reader = new BufferedReader(new InputStreamReader(fis, "GBK"));
+            while(true){
+                s = reader.readLine();
+                if(s == null) break;
+                //由于目前尚未确定以何种方式发送,所以先打出log日志。
+                //Log.i("info", s.toString());
+            }
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }finally{   // 关闭流
+            try {
+                reader.close();
+                fis.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public static  interface CrashListener{
+        void onCrash();
+    }
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/retrofit/Interceptor/HttpLoggingInterceptor.java b/app/src/main/java/com/runt/open/mvvm/retrofit/Interceptor/HttpLoggingInterceptor.java
index 221f070..6d271e4 100644
--- a/app/src/main/java/com/runt/open/mvvm/retrofit/Interceptor/HttpLoggingInterceptor.java
+++ b/app/src/main/java/com/runt/open/mvvm/retrofit/Interceptor/HttpLoggingInterceptor.java
@@ -51,7 +51,7 @@
         int position = logArrays.size() +2;
         Response response;
         try {
-            //request = encryptRequest(request);//加密
+            request = encryptRequest(request);//加密
             response = chain.proceed(request);
             logArrays.addAll(getResponseLog(response));
             Log.d(TAG,"hashcode:"+hashCode);
diff --git a/app/src/main/java/com/runt/open/mvvm/retrofit/api/LoginApiCenter.java b/app/src/main/java/com/runt/open/mvvm/retrofit/api/LoginApiCenter.java
index 7e03dc2..c41d848 100644
--- a/app/src/main/java/com/runt/open/mvvm/retrofit/api/LoginApiCenter.java
+++ b/app/src/main/java/com/runt/open/mvvm/retrofit/api/LoginApiCenter.java
@@ -1,24 +1,65 @@
 package com.runt.open.mvvm.retrofit.api;
 
-import com.runt.open.mvvm.ui.login.LoggedInUser;
+import com.runt.open.mvvm.config.Configuration;
+import com.runt.open.mvvm.data.Results;
 
 import io.reactivex.Observable;
 import retrofit2.http.Field;
 import retrofit2.http.FormUrlEncoded;
 import retrofit2.http.POST;
+import retrofit2.http.Url;
 
 /**
  * Created by Administrator on 2021/11/15 0015.
  */
 public interface LoginApiCenter {
+    /**
+     * 密码登录
+     * @param phone
+     * @param pass
+     * @return
+     */
     @FormUrlEncoded
     @POST("login")
-    Observable<LoggedInUser> login(@Field("phone") String phone,@Field("pass") String pass);
+    Observable<Results.LoggedInUser> login(@Field(Configuration.KEY_PHONE) String phone, @Field("pass") String pass);
+
+    /**
+     * 验证码登录
+     * @param phone
+     * @param code
+     * @return
+     */
+    @FormUrlEncoded
+    @POST("loginCode")
+    Observable<Results.LoggedInUser> loginByCode(@Field(Configuration.KEY_PHONE) String phone, @Field(Configuration.KEY_CODE) String code);
 
 
     @FormUrlEncoded
-    @POST("login")
-    Observable<LoggedInUser> loginByCode(@Field("phone") String phone,@Field("code") String code);
+    @POST
+    Observable<Results.StringApiResult> getVerifyCode(@Url String url, @Field(Configuration.KEY_PHONE) String phone, @Field(Configuration.KEY_CODE) String code, @Field("time") String time);
+
+    /**
+     * 重置密码
+     * @param phone
+     * @param sms
+     * @param newPass
+     * @return
+     */
+    @FormUrlEncoded
+    @POST("verifySMSReSetLoginPwd")
+    Observable<Results.StringApiResult> resetLoginPwd(@Field(Configuration.KEY_PHONE) String phone,@Field("sms") String sms, @Field("pass") String newPass);
+
+
+    /**
+     * 注册
+     * @param phone
+     * @param sms
+     * @param pass
+     * @return
+     */
+    @FormUrlEncoded
+    @POST("registerCustomer")
+    Observable<Results.StringApiResult> register(@Field(Configuration.KEY_USERNAME) String phone,@Field("sms") String sms, @Field("pass") String pass);
 
 
 }
diff --git a/app/src/main/java/com/runt/open/mvvm/retrofit/observable/HttpObserver.java b/app/src/main/java/com/runt/open/mvvm/retrofit/observable/HttpObserver.java
index 7024299..3a4ac2c 100644
--- a/app/src/main/java/com/runt/open/mvvm/retrofit/observable/HttpObserver.java
+++ b/app/src/main/java/com/runt/open/mvvm/retrofit/observable/HttpObserver.java
@@ -1,5 +1,6 @@
 package com.runt.open.mvvm.retrofit.observable;
 
+import android.accounts.NetworkErrorException;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -41,14 +42,16 @@
         Log.i("subscribe","onError");
 
         try {
-            Log.e(TAG,this.getClass().getSimpleName()+" "+throwable.getMessage());
+            Log.e(TAG,this.getClass().getSimpleName()+" mes:"+throwable.getMessage());
             Class<M> entityClass = (Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
             M t = entityClass.newInstance();//实例化一个泛型
             t.code = 410;
             if( throwable instanceof SocketTimeoutException){
                 t.msg = "服务请求超时,请稍候再试";//设置错误信息
-            }else{
+            }else  if( throwable instanceof NetworkErrorException){
                 t.msg = "网络连接不畅,请检查您的网络设置";//设置错误信息
+            }else{
+                t.msg = throwable.getMessage();//设置错误信息
             }
             resultLive.setValue(t);
         } catch (ClassCastException e) {
diff --git a/app/src/main/java/com/runt/open/mvvm/ui/login/CodeTimer.java b/app/src/main/java/com/runt/open/mvvm/ui/login/CodeTimer.java
new file mode 100644
index 0000000..764b79a
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/ui/login/CodeTimer.java
@@ -0,0 +1,40 @@
+package com.runt.open.mvvm.ui.login;
+
+import android.os.CountDownTimer;
+import android.widget.TextView;
+
+import com.runt.open.mvvm.R;
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-2-23.
+ */
+public class CodeTimer extends CountDownTimer {
+
+    TextView txtGetCode;
+
+    public CodeTimer(long millisInFuture, long countDownInterval, TextView txtGetCode) {
+        super(millisInFuture, countDownInterval);
+        this.txtGetCode = txtGetCode;
+    }
+
+    public void startUp(){
+        txtGetCode.setEnabled(false);
+        txtGetCode.setTextColor(txtGetCode.getContext().getResources().getColor(R.color.txt_enable));
+        start();
+    }
+
+    @Override
+    public void onTick(long l) {
+        txtGetCode.setText(String.format("(%s)", l/1000));
+    }
+
+    @Override
+    public void onFinish() {
+        txtGetCode.setEnabled(true);
+        txtGetCode.setTextColor(txtGetCode.getContext().getResources().getColor(R.color.link));
+        txtGetCode.setText(txtGetCode.getContext().getResources().getString(R.string.get_verify_code));
+    }
+
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/ui/login/LoggedInUser.java b/app/src/main/java/com/runt/open/mvvm/ui/login/LoggedInUser.java
deleted file mode 100644
index 8a5ad19..0000000
--- a/app/src/main/java/com/runt/open/mvvm/ui/login/LoggedInUser.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.runt.open.mvvm.ui.login;
-
-import com.runt.open.mvvm.data.BaseApiResult;
-
-/**
- * Created by Administrator on 2021/11/15 0015.
- */
-public class LoggedInUser extends BaseApiResult<UserBean> {
-
-}
diff --git a/app/src/main/java/com/runt/open/mvvm/ui/login/LoginViewModel.java b/app/src/main/java/com/runt/open/mvvm/ui/login/LoginViewModel.java
index 866420b..eda4bd3 100644
--- a/app/src/main/java/com/runt/open/mvvm/ui/login/LoginViewModel.java
+++ b/app/src/main/java/com/runt/open/mvvm/ui/login/LoginViewModel.java
@@ -3,9 +3,14 @@
 import androidx.lifecycle.MutableLiveData;
 
 import com.runt.open.mvvm.base.model.BaseViewModel;
+import com.runt.open.mvvm.data.Results;
 import com.runt.open.mvvm.retrofit.api.LoginApiCenter;
 import com.runt.open.mvvm.retrofit.observable.HttpObserver;
 import com.runt.open.mvvm.retrofit.utils.RetrofitUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
 
 import io.reactivex.Observable;
 
@@ -14,17 +19,132 @@
  */
 public class LoginViewModel extends BaseViewModel {
 
-    MutableLiveData<LoggedInUser> loginResult = new MutableLiveData<>();
+    LoginApiCenter loginApi;
 
-    public MutableLiveData<LoggedInUser> getLoginResult() {
+    public LoginViewModel() {
+        loginApi = RetrofitUtils.getInstance().getRetrofit(LoginApiCenter.class);
+    }
+
+    MutableLiveData<Results.LoggedInUser> loginResult = new MutableLiveData<>();
+    MutableLiveData<Results.StringApiResult> verifyResult = new MutableLiveData<>();
+    MutableLiveData<Results.StringApiResult> resetResult = new MutableLiveData<>();
+    MutableLiveData<Results.StringApiResult> registerResult = new MutableLiveData<>();
+
+    public MutableLiveData<Results.LoggedInUser> getLoginResult() {
         return loginResult;
     }
 
-    public void login(String username, String password) {
-        // can be launched in a separate asynchronous job
-        final Observable<LoggedInUser> userObservable = RetrofitUtils.getInstance().getRetrofit(LoginApiCenter.class).login(username, password);
-        httpObserverOn(userObservable,new HttpObserver<LoggedInUser>(loginResult){});
+    public MutableLiveData<Results.StringApiResult> getVerifyResult() {
+        return verifyResult;
     }
 
+    /**
+     * 密码登录
+     * @param username
+     * @param password
+     */
+    public void login(String username, String password) {
+        // can be launched in a separate asynchronous job
+        final Observable<Results.LoggedInUser> userObservable = loginApi.login(username, password);
+        httpObserverOnLoading(userObservable,new HttpObserver<Results.LoggedInUser>(loginResult){});
+    }
 
+    /**
+     * 验证码登录
+     * @param phone
+     * @param code
+     */
+    public void loginByCode(String phone,String code){
+        httpObserverOnLoading(loginApi.loginByCode(phone,code),
+                new HttpObserver<Results.LoggedInUser>(loginResult){});
+    }
+
+    /**
+     * 重置密码
+     * @param phone
+     * @param sms
+     * @param pass
+     */
+    public void resetPwd(String phone,String sms,String pass){
+        httpObserverOnLoading(loginApi.resetLoginPwd(phone, sms, pass), new HttpObserver<Results.StringApiResult>(resetResult) {});
+    }
+
+    /**
+     * 注册
+     * @param phone
+     * @param sms
+     * @param pass
+     */
+    public void register(String phone,String sms,String pass){
+        httpObserverOnLoading(loginApi.register(phone, sms, pass), new HttpObserver<Results.StringApiResult>(resetResult) {});
+    }
+
+    /**
+     * 注册密码
+     * @param phone
+     */
+    public void getRegisterSMS(String phone){
+        getVerifyCode("getRegisterSMS",phone);
+    }
+
+    /**
+     * 忘记密码
+     * @param phone
+     */
+    public void getForgetSMS(String phone){
+        getVerifyCode("getForgetSMS",phone);
+    }
+
+    /**
+     * 登录验证码
+     * @param phone
+     */
+    public void getLoginSMS(String phone){
+        getVerifyCode("getLoginSMS",phone);
+    }
+
+    /**
+     * 获取验证码
+     * @param url    验证码地址
+     * @param phone 手机号
+     */
+    public void getVerifyCode(String url,String phone){
+        String time = new Date().getTime()+"";
+        httpObserverOnLoading(loginApi.getVerifyCode(url, phone, randomString(phone, time), time), new HttpObserver<Results.StringApiResult>(verifyResult){});
+    }
+
+    /**
+     * 随机字符串
+     * @param phone
+     * @param time
+     * @return
+     */
+    private String randomString(String phone,String time){
+        int p =  (int) Math.round(phone.length()/6.0);
+        int t = time.length()/6;
+        List<String> list = new ArrayList<String>();
+        for(int i = 0 ; i < 6 ; i ++){
+            String str = "";
+            if(i*p>phone.length()){
+                str = phone.substring((i-1)*p);
+            }else if((i+1)*p>phone.length()){
+                str = phone.substring(i*p);
+            }else{
+                str = phone.substring(i*p,(i+1)*p);
+            }
+            String num = ((Integer.parseInt(str)*Long.parseLong(time))+"") ;
+            list.add(num);
+        }
+        //return sb.toString();
+        return plusSingle2(list);
+    }
+
+    private String plusSingle2(List<String> list){
+        StringBuilder sb = new StringBuilder();
+        for(int i = 0 ; i < list.size() ; i ++){
+            sb.append(list.get(i).substring(list.get(i).length()-2<0?0:list.get(i).length()-2));
+        }
+        return sb.toString();
+
+    }
 }
diff --git a/app/src/main/java/com/runt/open/mvvm/ui/login/RegisterLoginActivity.java b/app/src/main/java/com/runt/open/mvvm/ui/login/RegisterLoginActivity.java
new file mode 100644
index 0000000..ab84fd8
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/ui/login/RegisterLoginActivity.java
@@ -0,0 +1,287 @@
+package com.runt.open.mvvm.ui.login;
+
+import android.content.Intent;
+import android.view.View;
+import android.widget.EditText;
+
+import com.google.gson.Gson;
+import com.runt.open.mvvm.R;
+import com.runt.open.mvvm.base.activities.BaseActivity;
+import com.runt.open.mvvm.config.Configuration;
+import com.runt.open.mvvm.databinding.ActivityLoginBinding;
+import com.runt.open.mvvm.listener.CustomClickListener;
+import com.runt.open.mvvm.ui.web.WebViewActivity;
+import com.runt.open.mvvm.util.AlgorithmUtils;
+import com.runt.open.mvvm.util.MyLog;
+import com.runt.open.mvvm.util.PhoneUtil;
+
+import java.util.Date;
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2022/1/29.
+ */
+public class RegisterLoginActivity extends BaseActivity<ActivityLoginBinding,LoginViewModel> {
+    final String VERIFY_CODE = "verify_code";
+    final String TAG = "RegisterLoginActivity";
+    int type = 0;//0 登录,1忘记密码,2注册,-1短信登录
+
+    @Override
+    public void initViews() {
+        binding.txtGetVerify.setOnClickListener(onclick);
+        binding.txtForgot.setOnClickListener(onclick);
+        binding.txtLogin.setOnClickListener(onclick);
+        binding.txtRegister.setOnClickListener(onclick);
+        binding.txtPrivacy.setOnClickListener(onclick);
+        long getTime = getLongProjectPrefrence(VERIFY_CODE);
+        long cha = new Date().getTime() - getTime;
+        if(cha <1000*60){
+            CodeTimer codeTimer = new CodeTimer(cha, 1000, binding.txtGetVerify);
+            codeTimer.startUp();
+        }
+        changeView();
+        binding.editPhone.setText(getStringProjectPrefrence(Configuration.KEY_USERNAME));
+        viewModel.getVerifyResult().observe(this, stringApiResult -> {
+           if(stringApiResult.code == 200){
+
+           }else{
+               showToast(stringApiResult.msg);
+           }
+        });
+        viewModel.getLoginResult().observe(this,loggedInUser -> {
+            if(loggedInUser.code == 200){
+                putBooleanProjectPrefrence(Configuration.IS_LOGIN,true);
+                putStringProjectPrefrence(Configuration.KEY_USERNAME,binding.editPhone.getText().toString());
+
+                UserBean user = new Gson().fromJson(new Gson().toJson(loggedInUser.data) ,UserBean.class);
+                UserBean.setUser(user);
+                putStringProjectPrefrence(Configuration.KEY_TOKEN, user.getToken());
+                MyLog.i("registerlogin",user.toString());
+                showToast(R.string.login_success);
+                setResult(RESULT_CODE_SUCESS);
+                finish();
+            }else{
+                showToast(loggedInUser.msg);
+            }
+        });
+    }
+
+    CustomClickListener onclick = new CustomClickListener() {
+        @Override
+        protected void onSingleClick(View view) {
+            switch (view.getId()){
+                case R.id.button:
+                    submit();
+                    break;
+                case R.id.txt_get_verify:
+
+                    String phone = binding.editPhone.getText().toString();
+                    if(!verifyPhone(phone)){//验证手机
+                        return;
+                    }
+                    if(type==2){//获取注册验证码
+                        viewModel.getRegisterSMS(phone);
+                    }else if(type ==1){
+                        viewModel.getForgetSMS( phone);
+                    }else if(type == -1){
+                        viewModel.getLoginSMS( phone);
+                    }
+                    break;
+                case R.id.txt_privacy:
+                    startActivity(new Intent(mContext, WebViewActivity.class).putExtra(PARAMS_URL,"http://www.hefan.space/privacyPolicy.html").putExtra(PARAMS_TITLE,"隐私政策"));
+                    break;
+                case R.id.txt_register:
+                    type = 2;
+                    changeView();
+                    break;
+                case R.id.txt_forgot:
+                    type = 1;
+                    changeView();
+                    break;
+                case R.id.txt_login:
+                    if(type != 0 ){
+                        type = 0;
+                    }else  {
+                        type = -1;
+                    }
+                    changeView();
+                    break;
+            }
+        }
+    };
+
+
+    /**
+     * 修改页面布局
+     */
+    private void changeView(){
+        binding.button.setEnabled(true);
+        binding.txtRegister.setVisibility(View.VISIBLE);
+        binding.checkbox.setVisibility(View.GONE);
+        binding.txtPrivacy.setVisibility(View.GONE);
+        switch (type){
+            case -1://短信登录
+                binding.editVerifyCode.setVisibility(View.VISIBLE);
+                binding.txtGetVerify.setVisibility(View.VISIBLE);
+                binding.editPass.setVisibility(View.GONE);
+                binding.editPassRepeat.setVisibility(View.GONE);
+                binding.txtForgot.setVisibility(View.VISIBLE);
+                binding.txtLogin.setText(getResources().getString(R.string.login));
+                binding.button.setText(getResources().getString(R.string.login));
+                binding.checkbox.setVisibility(View.VISIBLE);
+                binding.txtPrivacy.setVisibility(View.VISIBLE);
+                break;
+            case 0://登录
+                binding.editVerifyCode.setVisibility(View.GONE);
+                binding.txtGetVerify.setVisibility(View.GONE);
+                binding.editPass.setVisibility(View.VISIBLE);
+                binding.editPassRepeat.setVisibility(View.GONE);
+                binding.txtForgot.setVisibility(View.VISIBLE);
+                binding.txtLogin.setText(getResources().getString(R.string.msg_login));
+                binding.button.setText(getResources().getString(R.string.login));
+                break;
+            case 1://忘记密码
+                binding.txtForgot.setVisibility(View.INVISIBLE);
+                binding.editVerifyCode.setVisibility(View.VISIBLE);
+                binding.txtGetVerify.setVisibility(View.VISIBLE);
+                binding.editPass.setVisibility(View.VISIBLE);
+                binding.editPassRepeat.setVisibility(View.VISIBLE);
+                binding.txtLogin.setText(getResources().getString(R.string.login));
+                binding.button.setText(getResources().getString(R.string.str_confirm));
+                break;
+            case 2://注册
+                binding.checkbox.setVisibility(View.VISIBLE);
+                binding.txtPrivacy.setVisibility(View.VISIBLE);
+                binding.txtRegister.setVisibility(View.INVISIBLE);
+                binding.editVerifyCode.setVisibility(View.VISIBLE);
+                binding.txtGetVerify.setVisibility(View.VISIBLE);
+                binding.editPass.setVisibility(View.VISIBLE);
+                binding.editPassRepeat.setVisibility(View.VISIBLE);
+                binding.txtLogin.setText(getResources().getString(R.string.login));
+                binding.button.setText(getResources().getString(R.string.register));
+                break;
+        }
+        clearText(binding.editPassRepeat,binding.editPass,binding.editPhone,binding.editVerifyCode);
+    }
+
+    private void clearText(EditText... editTextes){
+        for(EditText editText :editTextes) {
+            editText.setText("");
+        }
+    }
+
+
+
+
+    /**
+     * 提交数据
+     */
+    public void submit(){
+        String phone = binding.editPhone.getText().toString();
+        String pass = binding.editPass .getText().toString();
+        String veriCode = binding.editVerifyCode.getText().toString();
+        String invite = binding.editPassRepeat.getText().toString();
+        if(!verifyPhone(phone)){//验证手机
+            return;
+        }
+        switch (type){
+            case -1://短信登录
+                if(veriCode.length() == 0){//验证码
+                    showToast(R.string.input_verify_code);
+                    return;
+                }
+                if(!binding.checkbox.isChecked()){
+                    showToast("请阅读并勾选《隐私条款》");
+                    return;
+                }
+                viewModel.loginByCode(phone,veriCode);
+                break;
+            case 0:
+                if(!verifyPassWord(pass)){//验证密码
+                    return;
+                }
+                viewModel.login(phone,pass);
+                break;
+            case 1:
+                if(!verifyPassWord(pass)){//验证密码
+                    return;
+                }
+                if(veriCode.length() == 0){//验证码
+                    showToast(R.string.input_verify_code);
+                    return;
+                }
+
+                //新密码
+                if(invite.length() == 0){
+                    showToast(R.string.input_pass);
+                    return;
+                } else if(!invite.equals(pass)){
+                    showToast(R.string.str_new_verify_failed);
+                    return;
+                }
+                viewModel.resetPwd(phone,veriCode,pass);
+                break;
+            case 2://注册
+                if(!verifyPassWord(pass)){//验证密码
+                    return;
+                }
+                if(veriCode.length() == 0){//验证码
+                    showToast(R.string.input_verify_code);
+                    return;
+                }
+                if(!binding.checkbox.isChecked()){
+                    showToast("请阅读并勾选《隐私条款》");
+                    return;
+                }
+                viewModel.register(phone,veriCode,pass);
+                break;
+        }
+    }
+
+    /**
+     * 验证密码
+     * @param pass
+     * @return
+     */
+    public boolean verifyPassWord(String pass){
+
+        if(pass.length() == 0){
+            showToast(R.string.input_pass);
+            return false;
+        } else if(!verifyPass(pass)){
+            showToast(R.string.str_pass_format_failed);
+            return false;
+        }
+        return  true;
+    }
+
+
+    /**
+     * 验证手机号
+     * @param phone
+     * @return
+     */
+    public boolean verifyPhone(String phone ){
+
+        if(phone.length() == 0){
+            showToast(R.string.input_phone);
+            return false;
+        }else if(phone.length()<5 || !PhoneUtil.isMobileNO(phone)){
+            showToast(R.string.str_phone_format_failed);
+            return false;
+        }
+        return true;
+
+    }
+
+
+    /**
+     * 验证密码
+     * @param pass
+     * @return
+     */
+    public boolean verifyPass(String pass){
+        return AlgorithmUtils.pwdLevel(pass)>1 && pass.length()>=6 && pass.length() <=12;
+    }
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/ui/web/WebViewActivity.java b/app/src/main/java/com/runt/open/mvvm/ui/web/WebViewActivity.java
new file mode 100644
index 0000000..d41521c
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/ui/web/WebViewActivity.java
@@ -0,0 +1,159 @@
+package com.runt.open.mvvm.ui.web;
+
+import android.os.Handler;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.LayoutAnimationController;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+import com.runt.open.mvvm.base.activities.BaseActivity;
+import com.runt.open.mvvm.base.model.BaseViewModel;
+import com.runt.open.mvvm.databinding.ActivityWebBinding;
+import com.runt.open.mvvm.util.MyAnimations;
+import com.runt.open.mvvm.util.MyLog;
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-9-16.
+ */
+public class WebViewActivity extends BaseActivity<ActivityWebBinding, BaseViewModel> {
+
+    private String url;
+    private  int linProgressWidth;
+
+    @Override
+    public void initViews() {
+        url = getIntent().getSerializableExtra(PARAMS_URL)+"";
+        setTitle(getIntent().getSerializableExtra(PARAMS_TITLE)+"");
+        initCompent();
+    }
+
+
+    int count = 100;
+    int index = 100;
+    private void initCompent(){
+        binding.browser.getSettings().setJavaScriptEnabled(true);
+        binding.browser.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
+        //跳转至拼接好的地址
+        //mBaseHandler.sendMessage(msg);//http://192.168.5.156:8080/MyFinance/gd16/1.html
+        binding.browser.loadUrl(url);
+        binding.browser.setWebViewClient(new myWebViewClient());
+        binding.browser.setWebChromeClient(new WebChromeClient(){
+
+            @Override
+            public void onProgressChanged(WebView view,final int newProgress) {
+                MyLog.i("onProgressChanged","--newProgress:--"+newProgress);
+                MyLog.i("onProgressChanged","--binding.viewProgressbar:--"+binding.viewProgressbar.getWidth());
+                final LayoutAnimationController.AnimationParameters animation= new LayoutAnimationController.AnimationParameters();   //得到一个LayoutAnimationController对象;
+                animation.index =index++ ;
+                animation.count = count++ ;
+                if (newProgress == 100) {
+                    MyAnimations.hideAnimaInSitu(binding.linProgressbar);
+                    MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),0,0,0,binding.viewProgressbar);
+                    new Handler().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            binding.linProgressbar.setVisibility(View.GONE);
+                        }
+                    },MyAnimations.ANIMA_TIME);
+                } else {
+
+                    if (View.VISIBLE != binding.linProgressbar.getVisibility()) {
+                        MyAnimations.showAnimaInSitu(binding.linProgressbar);
+                        if(linProgressWidth==0){
+                            final ViewTreeObserver vto = binding.linProgressbar.getViewTreeObserver();
+                            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                                public boolean onPreDraw() {
+                                    linProgressWidth = binding.linProgressbar.getMeasuredWidth();
+                                    binding.viewProgressbar.setTranslationX(0-linProgressWidth);
+                                    binding.linProgressbar.getViewTreeObserver().removeOnPreDrawListener(this);
+                                    return true;
+                                }
+                            });
+                        }else{
+                            binding.viewProgressbar.setTranslationX(0-linProgressWidth);
+                        }
+
+                    }
+                    if(linProgressWidth!=0){
+                        MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),0-linProgressWidth+linProgressWidth/100*newProgress,0,0,binding.viewProgressbar,MyAnimations.ANIMA_TIME*3);
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),binding.viewProgressbar.getTranslationX()+300,0,0,binding.viewProgressbar,MyAnimations.ANIMA_TIME*10);
+                            }
+                        },MyAnimations.ANIMA_TIME*3);
+                    }
+
+                }
+                super.onProgressChanged(view, newProgress);
+            }
+
+        });
+
+        binding.browser.getSettings().setSavePassword(false);
+        //Toast.makeText(mContext,"进入浏览器",Toast.LENGTH_SHORT).show();
+        String Scale = String.valueOf(binding.browser.getScale());
+        MyLog.i("Runt","--Scale:--"+Scale);
+        int screenDensity=getResources().getDisplayMetrics().densityDpi;
+        MyLog.i("Runt", "--screenDensity:--"+String.valueOf(screenDensity));  //60-160-240
+    }
+
+
+
+
+    private class myWebViewClient extends WebViewClient {
+
+        /**
+         * 每加载一张图片资源执行一次
+         */
+        @Override
+        public void onLoadResource(WebView view, String url) {
+            // TODO Auto-generated method stub
+            super.onLoadResource(view, url);
+            //MyLog.i("WebView", "onLoadResource "+url);
+        }
+
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            hideProgressBar();
+            //MyLog.i("WebView", "onPageFinished "+url);
+        }
+
+        /**
+         * 获取页面跳转的链接
+         */
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, String url) {
+            // TODO Auto-generated method stub
+            //MyLog.i("UrlLoading", "UrlLoading 正在跳转页面"+url);
+            view.loadUrl(url);
+            return true;
+        }
+
+        @Override
+        public void onReceivedError(WebView view, int errorCode,
+                                    String description, String failingUrl) {
+            // TODO Auto-generated method stub
+            super.onReceivedError(view, errorCode, description, failingUrl);
+            Toast.makeText(mContext, "加载失败,请稍候再试", Toast.LENGTH_SHORT).show();
+            hideProgressBar();
+        }
+    }
+
+    private void hideProgressBar(){
+        MyAnimations.hideAnimaInSitu(binding.linProgressbar);
+        MyAnimations.makeViewMove(binding.viewProgressbar.getTranslationX(),0,0,0,binding.viewProgressbar);
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                binding.linProgressbar.setVisibility(View.GONE);
+            }
+        },MyAnimations.ANIMA_TIME*2);
+    }
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/util/Configuration.java b/app/src/main/java/com/runt/open/mvvm/util/Configuration.java
deleted file mode 100644
index c4adb9c..0000000
--- a/app/src/main/java/com/runt/open/mvvm/util/Configuration.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.runt.open.mvvm.util;
-
-/**
- * Created by xanarry on 2016/5/22.
- */
-public class Configuration {
-
-
-    public static final int REQUEST_CODE_PIC = 303;
-
-
-    public final static String MESS_TIP_NET_ERROR = "网络连接不畅,请稍后再试!!!";
-    public final static String KEY_MES_CODE= "code";
-    public final static String KEY_MES_TIME= "time";
-    public final static String KEY_MES_MESSAGE= "msg";
-    public final static String KEY_MES_ERROR= "error";
-    public final static String KEY_CODE_SUCCESS= "0";//code 0 成功
-    public final static int KEY_CODE_INFO= 101;//修改资料
-    public final static String KEY_USER = "user";
-    public final static String KEY_ADVERT= "advert";
-    public final static String KEY_DATA= "data";
-    public final static String KEY_FILES= "files";
-    public final static String KEY_SCREEN_TYPE = "screen_type";//屏幕类型
-    public final static String KEY_SCREEN_FILES= "screen_files";//播放的资源文件列表
-    public final static String KEY_NAME= "name";
-    public final static String KEY_MARk= "mark";
-    public final static String KEY_ORDER= "order";
-    public final static String FILENAME_MK = "fileName";
-    public final static String FILEPATH = "path";
-    public final static String STR_APPLY_DATA = "apply datas";
-    public final static String STR_FILE_PLAN_FAILED = "file plan failed";
-    public final static String STR_START_FILE_PLAN = "start file plan";
-    public final static String STR_FINISHED_FILE_PLAN = "finished plan";
-    public final static String FILESIZE_MK = "fileSize";
-
-    public static final String KEY_EXTRAS = "extras";
-    public static final String KEY_USERID = "customerId";
-    public static final String KEY_TOKEN = "token";
-    public static final String KEY_USERNAME = "username";
-    public static final String KEY_PHONE = "phone";
-    public static final String KEY_USERPASS = "userpass";
-    public static final String MESSAGE_RECEIVED_ACTION = "MESSAGE_RECEIVED_ACTION";
-
-    public static  final String KEY_REGION = "region",
-            IS_LOGIN = "is_login";
-
-
-}
diff --git a/app/src/main/java/com/runt/open/mvvm/util/MyAnimations.java b/app/src/main/java/com/runt/open/mvvm/util/MyAnimations.java
new file mode 100644
index 0000000..b3faaac
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/util/MyAnimations.java
@@ -0,0 +1,477 @@
+package com.runt.open.mvvm.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.ObjectAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.widget.RelativeLayout;
+
+import com.facebook.rebound.SimpleSpringListener;
+import com.facebook.rebound.Spring;
+import com.facebook.rebound.SpringConfig;
+import com.facebook.rebound.SpringSystem;
+import com.runt.open.mvvm.R;
+
+/**
+ * Created by Administrator on 2017/11/30.
+ */
+public class MyAnimations {
+
+    public static final int ANIMA_TIME = 300;
+    public static final float MOVE_SPACE = 1;
+    public static final float SITU = 0;
+
+
+
+    /**
+     *  移动控件
+     * @param x                x初始位置
+     * @param distanceX     x移动的距离
+     * @param y             y初始位置
+     * @param distanceY     y移动的距离
+     * @param view
+     */
+    public static void makeViewMove(float x ,float  distanceX , float y ,float  distanceY, View view){
+        setAnimator("translationY", y, distanceY,view,ANIMA_TIME);
+        setAnimator("translationX", x, distanceX,view,ANIMA_TIME);
+    }
+    /**
+     *  移动控件
+     * @param x                x初始位置
+     * @param distanceX     x移动的距离
+     * @param y             y初始位置
+     * @param distanceY     y移动的距离
+     * @param view
+     */
+    public static void makeViewMove(float x , float  distanceX , float y , float  distanceY, View view, int animTime){
+        setAnimator("translationY", y, distanceY,view,animTime);
+        setAnimator("translationX", x, distanceX,view,animTime);
+    }
+
+    private static  void setAnimator(String attribute, float from, float to, View view, int animTime){
+        ObjectAnimator.ofFloat(view, attribute, from, to).setDuration(animTime).start();
+    }
+    public static void setLayoutMargin(View view, int left, int top, int right, int bottom)
+    {
+        //克隆view的width、height、margin的值生成margin对象
+        ViewGroup.MarginLayoutParams margin=new ViewGroup.MarginLayoutParams(view.getLayoutParams());
+        //设置新的边距
+        margin.setMargins(left, top, right, bottom);
+        //把新的边距生成layoutParams对象
+        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(margin);
+        //设制view的新的位置
+        view.setLayoutParams(layoutParams);
+    }
+    /**
+     * 原地不动
+     * @return
+     */
+    private static TranslateAnimation makeInSitu(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+
+    /**
+     * 从顶部显示
+     * @return
+     */
+    private static TranslateAnimation makeInFromTop(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,0-MOVE_SPACE,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+    /**
+     * 从底部显示
+     * @return
+     */
+    private static TranslateAnimation makeInFromBottom(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,MOVE_SPACE,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+    /**
+     * 从左侧显示
+     * @return
+     */
+    private static TranslateAnimation makeInFromLeft(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,0-MOVE_SPACE,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+    /**
+     * 从右侧显示
+     * @return
+     */
+    private static TranslateAnimation makeInFromRight(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,MOVE_SPACE,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+
+    /**
+     * 向上隐藏
+     * @return
+     */
+    private static TranslateAnimation makeOutToTop(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,0-MOVE_SPACE);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+    /**
+     * 向左隐藏
+     * @return
+     */
+    private static TranslateAnimation makeOutToLeft(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,0-MOVE_SPACE,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+    /**
+     * 向右隐藏
+     * @return
+     */
+    private static TranslateAnimation makeOutToRight(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,MOVE_SPACE,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,SITU);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+    /**
+     * 向下隐藏
+     * @return
+     */
+    private static TranslateAnimation makeOutToButtom(){
+        TranslateAnimation mAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF,SITU,//大于0 则从右向当前位置移动,反之则从左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从当前位置向右移动,反之则向左
+                Animation.RELATIVE_TO_SELF,SITU,//大于0 则从下方向当前位置,反之则从上方
+                Animation.RELATIVE_TO_SELF,MOVE_SPACE);//大于0从当前位置向下移动,反之则向上方
+        mAction.setDuration(ANIMA_TIME);
+        return mAction;
+    }
+
+
+
+    /***
+     * 动画显示 从右向左左显示
+     *
+     * @param view
+     */
+    public static void showAnimaRightToLeft(View view) {
+        view.setVisibility(View.VISIBLE);
+        Animation mAni;
+        mAni =  makeInFromRight();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+
+    /***
+     * 动画显示 从右向左左显示
+     *
+     * @param view
+     */
+    public static void showAnimaLeftToRight(View view) {
+        view.setVisibility(View.VISIBLE);
+        Animation mAni;
+        mAni =  makeInFromLeft();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+    /**
+     * 动画隐藏 从下往上
+     *
+     * @param view
+     */
+    public static void hideAnimaBottomToTop(View view) {
+        view.setVisibility(View.INVISIBLE);
+        Animation mAni;
+        mAni = makeOutToTop();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+    /**
+     * 动画隐藏 从左往右
+     *
+     * @param view
+     */
+    public static void hideAnimaLeftToRight(View view) {
+        view.setVisibility(View.INVISIBLE);
+        Animation mAni;
+        mAni = makeOutToRight();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+    /**
+     * 动画隐藏 从右往左
+     *
+     * @param view
+     */
+    public static void hideAnimaRightToLeft(View view) {
+        view.setVisibility(View.INVISIBLE);
+        Animation mAni;
+        mAni = makeOutToLeft();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+
+    /**
+     * 动画隐藏 原地
+     *
+     * @param view
+     */
+    public static void hideAnimaInSitu(View view) {
+        view.setVisibility(View.INVISIBLE);
+        Animation mAni;
+        mAni = makeInSitu();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+
+    /**
+     * 动画显示 原地
+     *
+     * @param view
+     */
+    public static void showAnimaInSitu(View view) {
+        view.setVisibility(View.VISIBLE);
+        Animation mAni;
+        mAni = makeInSitu();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+
+
+    /**
+     * 动画显示 从上往下走
+     *
+     * @param view
+     */
+    public static void showAnimaTopToBottom(View view) {
+        view.setVisibility(View.VISIBLE);
+        Animation mAni;
+        mAni = makeInFromTop();
+        mAni.setDuration(ANIMA_TIME);
+        view.setAnimation(mAni);
+    }
+
+    /**
+     *  冒泡式显示控件
+     * @param view
+     */
+    public static void showReBound(final View view){
+        Log.i("","showReBound view:"+view);
+        view.setVisibility(View.GONE);
+        showAnimaInSitu(view);
+        SpringSystem springSystem = SpringSystem.create();
+        final Spring spring = springSystem.createSpring();
+        spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(100,7));//qcTension拉力和qcFriction摩擦力参数
+        spring.addListener(new SimpleSpringListener() {
+            @Override
+            public void onSpringUpdate(Spring spring) {
+                float value = (float) spring.getCurrentValue();
+                float scale = value;
+                view.setScaleX(scale);
+                view.setScaleY(scale);
+            }
+        });
+        spring.setEndValue(1);//控件拉伸收缩的倍率
+    }
+
+    /**
+     * 冒泡式放大控件
+     * @param view
+     */
+    public static void showReBoundBig(final View view){
+        Log.i("","showReBound view:"+view);
+        view.setVisibility(View.GONE);
+        showAnimaInSitu(view);
+        SpringSystem springSystem = SpringSystem.create();
+        final Spring spring = springSystem.createSpring();
+        spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(100,7));//qcTension拉力和qcFriction摩擦力参数
+        spring.addListener(new SimpleSpringListener() {
+            @Override
+            public void onSpringUpdate(Spring spring) {
+                float value = (float) spring.getCurrentValue();
+                float scale = value;
+                view.setScaleX(scale);
+                view.setScaleY(scale);
+            }
+        });
+        spring.setEndValue(3);//控件拉伸收缩的倍率
+    }
+
+    /**
+     * 收缩式 隐藏
+     * @param view
+     * @param context
+     */
+    public static void hideReBound(final View view, final Context context){
+        /*showReBound(view);
+        @SuppressLint("ResourceType") Animator animator = AnimatorInflater.loadAnimator(context, R.anim.anima_make_none);
+        animator.setTarget(view);
+        animator.start();*/
+
+        final Handler handler = new Handler() {
+
+            @Override
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                float value = (float) msg.obj;
+                if(value>2){
+                    @SuppressLint("ResourceType") Animator animator = AnimatorInflater.loadAnimator(context, R.anim.anima_make_none);
+                    animator.setTarget(view);
+                    animator.start();
+                    animator.addListener(new Animator.AnimatorListener() {
+                        @Override
+                        public void onAnimationEnd(Animator animation, boolean isReverse) {
+                            MyLog.i("hideReBound","onAnimationEnd "+animation+" "+isReverse);
+                            view.setVisibility(View.GONE);
+                        }
+                        @Override
+                        public void onAnimationStart(Animator animation, boolean isReverse) {}
+                        @Override
+                        public void onAnimationStart(Animator animator) {
+                        }
+                        @Override
+                        public void onAnimationEnd(Animator animator) {
+                            MyLog.i("hideReBound","onAnimationEnd "+animator);
+                            view.setVisibility(View.GONE);
+                        }
+                        @Override
+                        public void onAnimationCancel(Animator animator) {}
+                        @Override
+                        public void onAnimationRepeat(Animator animator) { }
+                    });
+                    hideAnimaInSitu(view);
+                }else {
+                    value = 1f + (value);
+                    view.setScaleX(value);
+                    view.setScaleY(value);
+                }
+            }
+        };
+        new Thread(){
+            @Override
+            public void run() {
+                try {
+                    int sleep = 10;
+                    for(int i =0 ; i < 100 ; i+=sleep){
+                        sleep(sleep);
+                        Message msg = new Message();
+                        msg.obj = (float)i/100/3;
+                        handler.sendMessage(msg);
+                    }
+
+                    Message msg = new Message();
+                    msg.obj = 3f;
+                    handler.sendMessage(msg);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }.start();
+    }
+
+    static int time = 0;
+    public static void animaScale(final Context context, final View view, final float x, final float y){
+
+        final Handler handler = new Handler() {
+
+            @Override
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                float value = (float) msg.obj;
+                value = 1f + (value);
+                view.setScaleX(value);
+                view.setScaleY(value);
+            }
+        };
+        new Thread(){
+            @Override
+            public void run() {
+                try {
+                    int sleep = 10;
+                    for(int i =0 ; i < 100 ; i+=sleep){
+                        sleep(sleep);
+                        Message msg = new Message();
+                        msg.obj = (float)i/100/3;
+                        handler.sendMessage(msg);
+                    }
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }.start();
+    }
+
+    /**
+     *  冒泡式拉伸控件
+     * @param view
+     */
+    public static void scaleReBoundX(final View view, final int size){
+        Log.i("","scaleReBound view:"+view);
+        SpringSystem springSystem = SpringSystem.create();
+        final Spring spring = springSystem.createSpring();
+        spring.setSpringConfig(SpringConfig.fromOrigamiTensionAndFriction(100,7));//qcTension拉力和qcFriction摩擦力参数
+        spring.addListener(new SimpleSpringListener() {
+            @Override
+            public void onSpringUpdate(Spring spring) {
+                float value = (float) spring.getCurrentValue();
+                float scale = value;
+                view.setScaleX(scale);
+                view.setScaleY(scale);
+            }
+        });
+        spring.setEndValue(size);//控件拉伸收缩的倍率
+    }
+
+    public static void scalXAnima(final View view, float from, float to){
+        setAnimator("scaleX",from,to,view,ANIMA_TIME);
+    }
+}
diff --git a/app/src/main/java/com/runt/open/mvvm/util/PhoneUtil.java b/app/src/main/java/com/runt/open/mvvm/util/PhoneUtil.java
new file mode 100644
index 0000000..6c80502
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/util/PhoneUtil.java
@@ -0,0 +1,77 @@
+package com.runt.open.mvvm.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-4-14.
+ */
+public class PhoneUtil {
+    /**
+     * 验证中国大陆手机号是否合法
+     * @return
+     */
+    public static boolean isCNMobileNO(String mobile){
+        if (mobile.length() != 11)
+        {
+            return false;
+        }else{
+            /**
+             * 移动号段正则表达式
+             */
+            String pat1 = "^((13[4-9])|(147)|(15[0-2,7-9])|(178)|(18[2-4,7-8]))\\d{8}|(1705)\\d{7}$";
+            /**
+             * 联通号段正则表达式
+             */
+            String pat2  = "^((13[0-2])|(145)|(15[5-6])|(176)|(18[5,6]))\\d{8}|(1709)\\d{7}$";
+            /**
+             * 电信号段正则表达式
+             */
+            String pat3  = "^((133)|(153)|(177)|(18[0,1,9])|(149))\\d{8}$";
+            /**
+             * 虚拟运营商正则表达式
+             */
+            String pat4 = "^((170))\\d{8}|(1718)|(1719)\\d{7}$";
+
+            Pattern pattern1 = Pattern.compile(pat1);
+            Matcher match1 = pattern1.matcher(mobile);
+            boolean isMatch1 = match1.matches();
+            if(isMatch1){
+                return true;
+            }
+            Pattern pattern2 = Pattern.compile(pat2);
+            Matcher match2 = pattern2.matcher(mobile);
+            boolean isMatch2 = match2.matches();
+            if(isMatch2){
+                return true;
+            }
+            Pattern pattern3 = Pattern.compile(pat3);
+            Matcher match3 = pattern3.matcher(mobile);
+            boolean isMatch3 = match3.matches();
+            if(isMatch3){
+                return true;
+            }
+            Pattern pattern4 = Pattern.compile(pat4);
+            Matcher match4 = pattern4.matcher(mobile);
+            boolean isMatch4 = match4.matches();
+            if(isMatch4){
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public static boolean isMobileNO( String phone) {
+        //china phone
+        String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";
+        if (phone.length() != 11) {
+            return false;
+        } else {
+            Pattern p = Pattern.compile(regex);
+            Matcher m = p.matcher(phone);
+            return m.matches();
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/runt/open/mvvm/util/PreferencesUtils.java b/app/src/main/java/com/runt/open/mvvm/util/PreferencesUtils.java
new file mode 100644
index 0000000..235dabd
--- /dev/null
+++ b/app/src/main/java/com/runt/open/mvvm/util/PreferencesUtils.java
@@ -0,0 +1,352 @@
+package com.runt.open.mvvm.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.collection.ArraySet;
+
+import java.util.Set;
+
+/**
+ * PreferencesUtils, easy to get or put data
+ * <ul>
+ * <strong>Preference Name</strong>
+ * <li>you can change preference name by {@link #PREFERENCE_NAME}</li>
+ * </ul>
+ * <ul>
+ * <strong>Put Value</strong>
+ * </ul>
+ *
+ * @author <a href="http://www.trinea.cn" target="_blank">Trinea</a> 2013-3-6
+ */
+public class PreferencesUtils {
+
+    public static final String PREFERENCE_NAME="zipper";
+    public static final String PROJECT = "project";
+    public static final String USER = "user";
+    public static final String VISITOR = "visitor";
+
+    private PreferencesUtils() {
+        throw new AssertionError();
+    }
+
+    public static boolean clearData(Context context, String keyShared){
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        return settings.edit().clear().commit();
+    }
+
+
+    public static boolean clearData(Context context, String key, String keyShared){
+        putString(context,key,null,keyShared);
+        return true;
+    }
+
+
+
+    /**
+     * remove key preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static void  removeKey(Context context, String key, String keyShared){
+
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.remove(key);
+    }
+
+    /**
+     * remove value preferences
+     *
+     * @param context
+     * @param value The name of the preference to modify
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static void  removeValue(Context context, String value, String keyShared){
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.remove(value);
+    }
+
+
+    /**
+     * put string preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @param value The new value for the preference
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static boolean putString(Context context, String key, String value, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putString(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * get string preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @return The preference value if it exists, or null. Throws ClassCastException if there is a preference with this
+     *         name that is not a string
+     * @see #getString(Context, String, String)
+     */
+    public static String getString(Context context, String key, String keyShared) {
+        return getString(context, key, null,keyShared);
+    }
+
+    /**
+     * get string preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @param defaultValue Value to return if this preference does not exist
+     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
+     *         this name that is not a string
+     */
+    public static String getString(Context context, String key, String defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        return settings.getString(key, defaultValue);
+    }
+
+    /**
+     * put int preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @param value The new value for the preference
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static boolean putInt(Context context, String key, int value, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putInt(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * get int preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
+     *         name that is not a int
+     */
+    public static int getInt(Context context, String key, String keyShared) {
+        return getInt(context, key, -1,keyShared);
+    }
+
+    /**
+     * get int preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @param defaultValue Value to return if this preference does not exist
+     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
+     *         this name that is not a int
+     */
+    public static int getInt(Context context, String key, int defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        try {
+            return settings.getInt(key, defaultValue);
+        }catch (ClassCastException e){
+            try {
+                return Integer.parseInt(settings.getString(key,defaultValue+""));
+            }catch (NumberFormatException en){
+                return defaultValue;
+            }
+        }
+    }
+
+    /**
+     * put long preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @param value The new value for the preference
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static boolean putLong(Context context, String key, long value, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putLong(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * get long preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
+     *         name that is not a long
+     */
+    public static long getLong(Context context, String key, String keyShared) {
+        return getLong(context, key, -1,keyShared);
+    }
+
+    /**
+     * get long preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @param defaultValue Value to return if this preference does not exist
+     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
+     *         this name that is not a long
+     */
+    public static long getLong(Context context, String key, long defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        try {
+            return settings.getLong(key, defaultValue);
+        }catch (ClassCastException e){
+            try {
+                return Long.parseLong(settings.getString(key,defaultValue+""));
+            }catch (NumberFormatException en){
+                return defaultValue;
+            }
+        }
+    }
+
+    /**
+     * put float preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @param value The new value for the preference
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static boolean putFloat(Context context, String key, float value, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putFloat(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * get float preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @return The preference value if it exists, or -1. Throws ClassCastException if there is a preference with this
+     *         name that is not a float
+     */
+    public static float getFloat(Context context, String key, String keyShared) {
+        return getFloat(context, key, -1,keyShared);
+    }
+
+    /**
+     * get float preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @param defaultValue Value to return if this preference does not exist
+     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
+     *         this name that is not a float
+     */
+    public static float getFloat(Context context, String key, float defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        try {
+            return settings.getFloat(key, defaultValue);
+        }catch (ClassCastException e){
+            try {
+                return Float.parseFloat(settings.getString(key,defaultValue+""));
+            }catch (NumberFormatException en){
+                return defaultValue;
+            }
+        }
+    }
+
+    /**
+     * put boolean preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @param value The new value for the preference
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static boolean putBoolean(Context context, String key, boolean value, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putBoolean(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * get boolean preferences, default is false
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @return The preference value if it exists, or false. Throws ClassCastException if there is a preference with this
+     *         name that is not a boolean
+     */
+    public static boolean getBoolean(Context context, String key, String keyShared) {
+        return getBoolean(context, key, false,keyShared);
+    }
+
+    /**
+     * get boolean preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @param defaultValue Value to return if this preference does not exist
+     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
+     *         this name that is not a boolean
+     */
+    public static boolean getBoolean(Context context, String key, boolean defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        try {
+            return settings.getBoolean(key, defaultValue);
+        }catch (ClassCastException e){
+            try {
+                return Boolean.parseBoolean(settings.getString(key,defaultValue+""));
+            }catch (NumberFormatException en){
+                return defaultValue;
+            }
+        }
+    }
+
+    /**
+     * put boolean preferences
+     *
+     * @param context
+     * @param key The name of the preference to modify
+     * @param value The new value for the preference ,  the value of set ,canot be the other class out of java collection
+     * @return True if the new values were successfully written to persistent storage.
+     */
+    public static boolean putStringSet(Context context, String key, Set value, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putStringSet(key, value);
+        return editor.commit();
+    }
+
+    /**
+     * get boolean preferences, default is false
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @return The preference value if it exists, or false. Throws ClassCastException if there is a preference with this
+     *         name that is not a boolean 获取出来的值最终被转换为hashset类型
+     */
+    public static Set getStringSet(Context context, String key, String keyShared) {
+        return getStringSet(context, key,new ArraySet(),keyShared);
+    }
+
+    /**
+     * get boolean preferences
+     *
+     * @param context
+     * @param key The name of the preference to retrieve
+     * @param defaultValue Value to return if this preference does not exist
+     * @return The preference value if it exists, or defValue. Throws ClassCastException if there is a preference with
+     *         this name that is not a boolean 获取出来的值最终被转换为hashset类型
+     */
+    public static Set getStringSet(Context context, String key, Set defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        return settings.getStringSet(key, defaultValue);
+    }
+}
diff --git a/app/src/main/res/color/btn_txt_normal.xml b/app/src/main/res/color/btn_txt_normal.xml
new file mode 100644
index 0000000..05ff6ac
--- /dev/null
+++ b/app/src/main/res/color/btn_txt_normal.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:tools="http://schemas.android.com/tools"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    tools:ignore="MissingDefaultResource">
+     <item android:state_enabled="false" android:color="@color/txt_enable"/> <!-- pressed -->
+     <item android:state_pressed="true" android:color="@color/txt_cusor_color"/> <!-- pressed -->
+     <item android:state_focused="true" android:color="@color/txt_hint"/> <!-- focused -->
+     <item android:color="@color/white"/> <!-- default -->
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
new file mode 100644
index 0000000..9cc3693
--- /dev/null
+++ b/app/src/main/res/layout/activity_login.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout
+    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:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.runt.open.mvvm.widgets.TitleBarView
+        android:id="@+id/title_bar"
+        style="@style/titlebar"
+        app:leftDrawable="@mipmap/ic_arrow_back_black_24dp"
+        app:titleText="登录"
+        tools:ignore="MissingConstraints" />
+
+
+    <ImageView
+        android:id="@+id/img_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="50dp"
+        android:src="@mipmap/app_icon"
+        android:adjustViewBounds="true"
+        app:layout_constraintTop_toBottomOf="@id/title_bar"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        />
+
+    <com.runt.open.mvvm.widgets.ClearEditText
+        android:id="@+id/edit_phone"
+        style="@style/login_edit"
+        android:hint="@string/input_phone"
+        android:inputType="phone"
+        app:layout_constraintTop_toBottomOf="@id/img_icon"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        />
+
+
+    <com.runt.open.mvvm.widgets.ClearEditText
+        android:id="@+id/edit_verify_code"
+        style="@style/login_edit"
+        android:hint="@string/input_verify_code"
+        android:inputType="number"
+        app:layout_constraintTop_toBottomOf="@id/edit_phone"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"/>
+
+
+    <TextView
+        android:id="@+id/txt_get_verify"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/link"
+        android:layout_marginRight="@dimen/default_margin_lr"
+        android:text="@string/get_verify_code"
+        android:gravity="center"
+        app:layout_constraintTop_toTopOf="@id/edit_verify_code"
+        app:layout_constraintRight_toRightOf="@id/edit_verify_code"
+        app:layout_constraintBottom_toBottomOf="@id/edit_verify_code"/>
+
+
+    <com.runt.open.mvvm.widgets.ClearEditText
+        android:id="@+id/edit_pass"
+        style="@style/login_edit"
+        android:inputType="textPassword"
+        android:hint="@string/input_pass"
+        app:layout_constraintTop_toBottomOf="@id/edit_verify_code"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent" />
+
+
+    <com.runt.open.mvvm.widgets.ClearEditText
+        android:id="@+id/edit_pass_repeat"
+        style="@style/login_edit"
+        android:hint="@string/str_input_repeat"
+        android:inputType="textPassword"
+        app:layout_constraintTop_toBottomOf="@id/edit_pass"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"/>
+
+
+    <TextView
+        android:id="@+id/txt_forgot"
+        style="@style/login_link"
+        android:text="@string/forgot_pass"
+        app:layout_constraintTop_toBottomOf="@id/edit_pass_repeat"
+        app:layout_constraintLeft_toLeftOf="parent" />
+    <TextView
+        android:id="@+id/txt_register"
+        style="@style/login_link"
+        android:text="@string/register"
+        app:layout_constraintTop_toBottomOf="@id/edit_pass_repeat"
+        app:layout_constraintLeft_toRightOf="@id/txt_forgot"
+        app:layout_constraintRight_toLeftOf="@id/txt_login" />
+    <TextView
+        android:id="@+id/txt_login"
+        style="@style/login_link"
+        android:text="@string/login"
+        app:layout_constraintTop_toBottomOf="@id/edit_pass_repeat"
+        app:layout_constraintRight_toRightOf="parent"  />
+
+    <androidx.appcompat.widget.AppCompatCheckBox
+        android:id="@+id/checkbox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="@dimen/default_margin_lr"
+        android:layout_marginBottom="@dimen/default_margin_lr"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/button"/>
+    <TextView
+        android:id="@+id/txt_privacy"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/default_margin_td"
+        android:paddingBottom="@dimen/default_margin_td"
+        android:paddingRight="@dimen/default_margin_lr"
+        android:background="@color/white"
+        android:textColor="@color/link"
+        android:text=" 我已阅读并同意《隐私条款》"
+        app:layout_constraintLeft_toRightOf="@id/checkbox"
+        app:layout_constraintTop_toTopOf="@id/checkbox"
+        app:layout_constraintBottom_toBottomOf="@id/checkbox"/>
+    <Button
+        android:id="@+id/button"
+        style="@style/btn_normal"
+        android:layout_marginBottom="@dimen/default_margin_lr"
+        android:text="@string/login"
+        android:layout_marginLeft="@dimen/default_margin_lr"
+        android:layout_marginRight="@dimen/default_margin_lr"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
+
+
+
diff --git a/app/src/main/res/layout/activity_web.xml b/app/src/main/res/layout/activity_web.xml
new file mode 100644
index 0000000..c3ad397
--- /dev/null
+++ b/app/src/main/res/layout/activity_web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <com.runt.open.mvvm.widgets.TitleBarView
+        android:id="@+id/title_bar"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        style="@style/titlebar" />
+
+    <LinearLayout
+        android:id="@+id/lin_progressbar"
+        android:layout_width="match_parent"
+        android:layout_height="2dp"
+        android:background="@drawable/bg_gradient_gray"
+        app:layout_constraintTop_toBottomOf="@id/title_bar"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        android:visibility="gone">
+        <View
+            android:id="@+id/view_progressbar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:animateLayoutChanges="true"
+            android:background="@drawable/bg_gradient_blue"/>
+    </LinearLayout>
+
+    <WebView
+        android:id="@+id/browser"
+        app:layout_constraintTop_toBottomOf="@id/title_bar"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        android:layout_width="match_parent"
+        android:layout_height="0dp" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1c9793b..15ac25b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -2,4 +2,71 @@
     <string name="title_home">Home</string>
     <string name="title_dashboard">Dashboard</string>
     <string name="title_notifications">Notifications</string>
+    <string name="get_verify_code">获取验证码</string>
+    <string name="select_region">选择国家/地区</string>
+    <string name="input_phone">请输入手机号</string>
+    <string name="input_pass">请输入密码</string>
+    <string name="input_verify_code">请输入验证码</string>
+    <string name="input_invite_code">请输入邀请码</string>
+    <string name="forgot_pass">忘记密码?</string>
+    <string name="register">注册</string>
+    <string name="login">登录</string>
+    <string name="msg_login">短信登录</string>
+    <string name="str_input_old">请输入旧密码</string>
+    <string name="str_input_new">请输入新密码</string>
+    <string name="str_input_repeat">请再次输入新密码</string>
+    <string name="str_pass_format_failed">密码要求6-12位数(数字/英文大小写/符号混合)格式</string>
+    <string name="str_cannot_eques_old">新密码不能与旧密码相同</string>
+    <string name="str_new_verify_failed">新密码两次输入不相同</string>
+    <string name="str_edit_pass">修改密码</string>
+    <string name="str_confirm">确认</string>
+    <string name="str_invite">推荐码</string>
+    <string name="str_mine">个人中心</string>
+    <string name="str_setting">设置</string>
+    <string name="str_msg">消息中心</string>
+    <string name="str_version">版本号</string>
+    <string name="str_msg_detail">资讯详情</string>
+    <string name="str_copied">复制成功</string>
+    <string name="str_get_data_failed">获取数据失败</string>
+    <string name="str_phone_format_failed">输入手机格式不正确</string>
+    <string name="login_pass_failed">账号或者密码错误,请重新输入</string>
+    <string name="login_code_failed">验证码有误,请重新输入</string>
+    <string name="comming_soon">敬请期待</string>
+    <string name="notice">通知</string>
+    <string name="save_img">保存图片</string>
+    <string name="copy_url">复制链接</string>
+    <string name="toast_login">请先登录后再操作</string>
+    <string name="registed">账号已经注册了</string>
+    <string name="failed">操作失败</string>
+    <string name="none_account">账号不存在</string>
+    <string name="loading">加载中</string>
+    <string name="register_success">账号注册成功请登录</string>
+    <string name="verify_failed">验证码失效</string>
+    <string name="invite_error">错误的邀请码</string>
+    <string name="update_pass_sucess">密码修改成功</string>
+    <string name="update_pass_failed_old">旧密码错误</string>
+    <string name="update_pass_failed">密码修改失败</string>
+    <string name="login_success">登录成功</string>
+    <string name="login_failed">登录失败</string>
+    <string name="login_failed_pass">密码错误</string>
+    <string name="new_version">新版本更新</string>
+    <string name="str_cancel">取消</string>
+    <string name="logouted">登录失效,请重新登录</string>
+
+    <string name="forgot_update_pwd">点击忘记密码去修改</string>
+    <string name="download">下载</string>
+    <string name="created_at">更新</string>
+    <string name="msg_deleted">消息已被删除</string>
+    <string name="logout">退出登录</string>
+    <string name="internet_failed">网络连接失败</string>
+    <string name="str_miner_running">正在挖矿</string>
+    <string name="str_apply">申请</string>
+    <string name="str_permission">需要获取文件存储权限才能查看</string>
+    <string name="str_wait">敬请期待</string>
+    <string name="str_node_l">您当前为三级节点</string>
+    <string name="str_email">官方邮箱:</string>
+    <string name="str_website">官方网站:</string>
+    <string name="str_restart">重启APP</string>
+    <string name="str_success">成功</string>
+    <string name="str_no_up">暂无更新</string>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index db5c358..4884702 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -63,4 +63,48 @@
     <style name="TransparentDialog" parent="Theme.AppCompat.Dialog">
         <item name="android:windowBackground">@color/transparent</item>
     </style>
+
+    <style name="titlebar">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">?attr/actionBarSize</item>
+        <item name="android:background">@color/white</item>
+        <item name="titleTextColor">@color/txt_normal</item>
+        <item name="layout_constraintTop_toTopOf">parent</item>
+        <item name="layout_constraintLeft_toLeftOf">parent</item>
+        <item name="layout_constraintRight_toRightOf">parent</item>
+        <item name="android:paddingLeft">@dimen/default_margin_lr</item>
+        <item name="android:paddingRight">@dimen/default_margin_lr</item>
+    </style>
+
+
+    <style name="btn_normal">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:background">@drawable/btn_circle_blue</item>
+        <item name="android:textColor">@color/btn_txt_normal</item>
+        <item name="android:textSize">@dimen/title_size</item>
+    </style>
+
+    <style name="login_edit">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">@dimen/title_height</item>
+        <item name="android:background">@drawable/bg_circle_default</item>
+        <item name="android:layout_marginTop">@dimen/default_margin_td</item>
+        <item name="android:paddingRight">@dimen/default_margin_lr</item>
+        <item name="android:paddingLeft">@dimen/default_margin_lr</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:gravity">center_vertical</item>
+        <item name="android:layout_marginLeft">@dimen/default_margin_lr</item>
+        <item name="android:layout_marginRight">@dimen/default_margin_lr</item>
+    </style>
+
+    <style name="login_link">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:paddingTop">@dimen/default_margin_td</item>
+        <item name="android:paddingBottom">@dimen/default_margin_td</item>
+        <item name="android:paddingRight">30dp</item>
+        <item name="android:paddingLeft">30dp</item>
+        <item name="android:textColor">@color/link</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 99e3317..3765728 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -3,7 +3,7 @@
     <style name="Theme.OpemMvvm" parent="Theme.MaterialComponents.Light.NoActionBar">
         <!-- Primary brand color. -->
         <item name="colorPrimary">@color/sky</item>
-        <item name="colorPrimaryVariant">@color/red</item>
+        <item name="colorPrimaryVariant">@color/blue_sky</item>
         <item name="colorOnPrimary">@color/black</item>
         <!-- Secondary brand color. -->
         <item name="colorSecondary">@color/black_4</item>
@@ -12,6 +12,9 @@
         <!-- Status bar color. -->
         <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
         <!-- Customize your theme here. -->
+        <!--全局设置文本颜色和大小-->
+        <item name="android:textColor">@color/txt_normal</item>
+        <item name="android:textSize">@dimen/txt_size</item>
     </style>
 
 

--
Gitblit v1.9.1