From 9fbcdc146440b49ef80714c1336c226e00727da8 Mon Sep 17 00:00:00 2001
From: Administrator <123>
Date: Thu, 28 Oct 2021 10:16:30 +0000
Subject: [PATCH] 其他部分框架方法及文件新增

---
 app/src/main/java/com/duqing/missions/base/BaseActivity.java                            |  404 +++++++++++
 app/src/main/java/com/duqing/missions/data/BasePageResult.java                          |   21 
 app/src/main/java/com/duqing/missions/source/HttpPageSource.java                        |   16 
 app/src/main/java/com/duqing/missions/util/SpUtils.java                                 |  476 +++++++++++++
 app/src/main/java/com/duqing/missions/data/BaseApiResult.java                           |   23 
 app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java                    |  113 +++
 app/src/main/java/com/duqing/missions/ui/main/home/adapter/MissiontTopAdapter.java      |   18 
 app/src/main/res/layout/fragment_dashboard.xml                                          |    2 
 app/src/main/res/layout/activity_main.xml                                               |   31 
 app/src/main/java/com/duqing/missions/MainActivity.java                                 |    2 
 app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsViewModel.java |    2 
 app/build.gradle                                                                        |   10 
 app/src/main/java/com/duqing/missions/common/NullViewHolder.java                        |   18 
 app/src/main/res/navigation/mobile_navigation.xml                                       |    6 
 app/src/main/java/com/duqing/missions/ui/main/home/model/TopItemViewModel.java          |   11 
 app/src/main/AndroidManifest.xml                                                        |    1 
 app/src/main/java/com/duqing/missions/data/ApkUpGradeResult.java                        |   29 
 app/src/main/res/layout/fragment_home.xml                                               |   17 
 app/src/main/java/com/duqing/missions/MyApplication.java                                |  136 +++
 app/src/main/res/layout/fragment_notifications.xml                                      |    2 
 app/src/main/java/com/duqing/missions/util/ResPonse.java                                |   15 
 app/src/main/java/com/duqing/missions/base/BaseFragment.java                            |   52 +
 /dev/null                                                                               |   19 
 app/src/main/java/com/duqing/missions/base/BaseAdapter.java                             |  154 ++++
 app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java  |    2 
 app/src/main/java/com/duqing/missions/ui/main/Text.java                                 |    7 
 app/src/main/java/com/duqing/missions/util/DeviceUtil.java                              |  374 ++++++++++
 app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java          |    4 
 app/src/main/java/com/duqing/missions/ui/main/home/model/MissionDesc.java               |   12 
 app/src/main/java/com/duqing/missions/util/MyLog.java                                   |   42 +
 app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java                   |   47 +
 31 files changed, 2,012 insertions(+), 54 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index d9ad186..79380c7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -45,4 +45,14 @@
     //// 注意:分包之后不会有默认的Header和Footer需要手动添加!还是原来的三种方法!
     implementation  'com.scwang.smart:refresh-layout-kernel:2.0.1'      //核心必须依赖
     implementation  'com.scwang.smart:refresh-header-classics:2.0.1'    //经典刷新头
+    implementation 'com.github.d-max:spots-dialog:1.1@aar'//loading view
+    implementation 'com.google.code.gson:gson:2.8.6'
+    implementation 'com.ansen.http:okhttpencapsulation:1.0.1'//版本更新下载
+    implementation 'com.squareup.okhttp3:okhttp:3.12.1'
+    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
+    //RXjava和retrofit结合
+    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
+    implementation 'com.google.android.material:material:1.4.0'
+    implementation 'com.github.bumptech.glide:glide:4.12.0'
+    implementation 'com.permissionx.guolindev:permissionx:1.2.2'    //权限依赖
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0d606cb..468dd29 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.duqing.missions" >
 
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <application
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
diff --git a/app/src/main/java/com/duqing/missions/MainActivity.java b/app/src/main/java/com/duqing/missions/MainActivity.java
index 3d0f16a..6c2f39a 100644
--- a/app/src/main/java/com/duqing/missions/MainActivity.java
+++ b/app/src/main/java/com/duqing/missions/MainActivity.java
@@ -9,7 +9,6 @@
 
 import com.duqing.missions.base.BaseActivity;
 import com.duqing.missions.databinding.ActivityMainBinding;
-import com.google.android.material.bottomnavigation.BottomNavigationView;
 
 public class MainActivity extends BaseActivity<ActivityMainBinding> {
 
@@ -18,7 +17,6 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        BottomNavigationView navView = binding.navView;
         // Passing each menu ID as a set of Ids because each
         // menu should be considered as top level destinations.
         AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
diff --git a/app/src/main/java/com/duqing/missions/MyApplication.java b/app/src/main/java/com/duqing/missions/MyApplication.java
new file mode 100644
index 0000000..ef0eda4
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/MyApplication.java
@@ -0,0 +1,136 @@
+package com.duqing.missions;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.duqing.missions.util.MyLog;
+import com.scwang.smart.refresh.footer.ClassicsFooter;
+import com.scwang.smart.refresh.header.ClassicsHeader;
+import com.scwang.smart.refresh.layout.SmartRefreshLayout;
+import com.scwang.smart.refresh.layout.api.RefreshFooter;
+import com.scwang.smart.refresh.layout.api.RefreshHeader;
+import com.scwang.smart.refresh.layout.api.RefreshLayout;
+import com.scwang.smart.refresh.layout.listener.DefaultRefreshFooterCreator;
+import com.scwang.smart.refresh.layout.listener.DefaultRefreshHeaderCreator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class MyApplication extends Application {
+
+    final  String TAG = "MyApplication";
+    List<Activity> activities = new ArrayList<>();
+    private Activity currentActivity;//当前activity
+    private boolean isInfront; //是否前台运行
+    private static MyApplication application;
+    public static MyApplication getApplication() {
+        return application;
+    }
+
+    public Activity getCurrentActivity() {
+        return currentActivity;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        application = this;
+        //CrashReport.initCrashReport(getApplicationContext(), "8d88679ae9", false);//注册bugly
+        //设置全局的Header构建器
+        SmartRefreshLayout.setDefaultRefreshHeaderCreator(new DefaultRefreshHeaderCreator() {
+            @Override
+            public RefreshHeader createRefreshHeader(Context context, RefreshLayout layout) {
+                //layout.setPrimaryColorsId(R.color.colorPrimary, android.R.color.white);//全局设置主题颜色
+                return new ClassicsHeader(context);//.setTimeFormat(new DynamicTimeFormat("更新于 %s"));//指定为经典Header,默认是 贝塞尔雷达Header
+            }
+        });
+        //设置全局的Footer构建器
+        SmartRefreshLayout.setDefaultRefreshFooterCreator(new DefaultRefreshFooterCreator() {
+            @Override
+            public RefreshFooter createRefreshFooter(Context context, RefreshLayout layout) {
+                //指定为经典Footer,默认是 BallPulseFooter
+                return new ClassicsFooter(context).setDrawableSize(20);
+            }
+        });
+        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
+            @Override
+            public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
+                MyLog.d(TAG,"onActivityCreated "+activity.getClass().getSimpleName());
+                if(!activities.contains(activity)){
+                    activities.add(activity);
+                }
+            }
+
+            @Override
+            public void onActivityStarted(@NonNull Activity activity) {
+                MyLog.d(TAG,"onActivityStarted "+activity.getClass().getSimpleName());
+                currentActivity = activity;
+            }
+
+            @Override
+            public void onActivityResumed(@NonNull Activity activity) {
+                MyLog.d(TAG,"onActivityResumed "+activity.getClass().getSimpleName());
+                currentActivity = activity;
+                isInfront = true;
+            }
+
+            @Override
+            public void onActivityPaused(@NonNull Activity activity) {
+                MyLog.d(TAG,"onActivityPaused "+activity.getClass().getSimpleName());
+
+            }
+
+            @Override
+            public void onActivityStopped(@NonNull Activity activity) {
+                MyLog.d(TAG,"onActivityStopped "+activity.getClass().getSimpleName());
+
+            }
+
+            @Override
+            public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) {
+                MyLog.d(TAG,"onActivitySaveInstanceState "+activity.getClass().getSimpleName());
+                isInfront = false;
+
+            }
+
+            @Override
+            public void onActivityDestroyed(@NonNull Activity activity) {
+                MyLog.d(TAG,"onActivityDestroyed "+activity.getClass().getSimpleName());
+                if(activities.contains(activity)){
+                    activities.remove(activity);
+                }
+            }
+        });
+    }
+
+    /**
+     * 退出程序
+     */
+    public void quitApp(){
+        for(Activity activity:activities){
+            activity.finish();
+        }
+        System.exit(0);
+    }
+
+    public void clearActivities(){
+        for(Activity activity:activities){
+            /*if(activity instanceof LoginActivity){
+                continue;
+            }*/
+            activity.finish();
+        }
+    }
+
+    public boolean isInfront(){
+        return isInfront;
+    }
+}
diff --git a/app/src/main/java/com/duqing/missions/base/BaseActivity.java b/app/src/main/java/com/duqing/missions/base/BaseActivity.java
index cd24d4d..ef14ad3 100644
--- a/app/src/main/java/com/duqing/missions/base/BaseActivity.java
+++ b/app/src/main/java/com/duqing/missions/base/BaseActivity.java
@@ -1,14 +1,56 @@
 package com.duqing.missions.base;
 
+import android.Manifest;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Environment;
+import android.provider.Settings;
+import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
 
+import androidx.annotation.ColorRes;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.FileProvider;
 import androidx.viewbinding.ViewBinding;
 
+import com.duqing.missions.MyApplication;
+import com.duqing.missions.data.ApkUpGradeResult;
+import com.duqing.missions.util.MyLog;
+import com.duqing.missions.util.ResPonse;
+import com.duqing.missions.util.SpUtils;
+import com.permissionx.guolindev.PermissionX;
+import com.permissionx.guolindev.callback.ExplainReasonCallbackWithBeforeParam;
+import com.permissionx.guolindev.callback.ForwardToSettingsCallback;
+import com.permissionx.guolindev.callback.RequestCallback;
+import com.permissionx.guolindev.request.ExplainScope;
+import com.permissionx.guolindev.request.ForwardScope;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
+import java.util.Date;
+import java.util.List;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
 
 /**
  * Created by Administrator on 2021/10/27 0027.
@@ -16,6 +58,41 @@
 public abstract class BaseActivity<B extends ViewBinding> extends AppCompatActivity {
 
     protected  B binding;
+    protected String TAG ;
+    public final String[] FILE_PERMISSIONS = new String []{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};
+    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 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;
+    public static final int REQUEST_CODE_LOGOUT = 20009,/*退出*/
+            REQUEST_CODE_ORDER = 10010,/*订单详情*/
+            REQUEST_CODE_LOGIN = 20010,/*登录*/
+            REQUEST_CODE_SCAN = 20011/*扫描请求*/,
+            REQUEST_CODE_PIC = 20012,/*选择图片*/
+            REQUEST_CODE_PAYPASS = 20112,/*支付验证*/
+            REQUEST_CODE_PAYPASS_FOR_ALIPAY = 20110,/*支付宝修改支付验证*/
+            REQUEST_CODE_PAYPASS_FOR_REALNAME = 20111,/*支付宝修改支付验证*/
+            REQUEST_CODE_PERMISSION = 20013,/*请求权限*/
+            REQUEST_VERSION_PERMISSION = 20013,/*检查更新*/
+            REQUEST_INSTALL_PERMISSION = 20014,/*请求安装权限*/
+            REQUEST_CODE_MANAGE_GROUP = 20015,/*选择分组*/
+            REQUEST_CODE_MANAGE_DEL = 20016,/*选择删除*/
+            REQUEST_CODE_MANAGE_EDIT = 20017,/*选择编辑*/
+            REQUEST_CODE_MANAGE_ADD = 20018,/*选择添加*/
+            REQUEST_CODE_MANAGE_DRAFT = 20019,/*选择草稿编辑*/
+            REQUEST_CODE_WEPAY = 20020,/*微信支付*/
+            REQUEST_CODE_ALIPAY = 20021,/*支付宝*/
+            REQUEST_CODE_PINKAGE = 20022,/*包邮设置*/
+            REQUEST_INSTALL_APK = 20200,/*请求安装权限*/
+            REQUEST_CODE_COMMENT_REFRESH = 20222,/*请求安装权限*/
+            RESULT_CODE_DRAFT = 4041/*草稿*/,
+            RESULT_CODE_UPDATED = 4042/*更新*/,
+            RESULT_CODE_FAILD = 4044/*失败*/,
+            RESULT_CODE_SUCESS = 4046/*成功*/,
+            RESULT_CODE_CANCEL = 4043/*取消*/;
+    protected Context mContext;
 
 
     @Override
@@ -30,5 +107,332 @@
             e.printStackTrace();
         }
         setContentView(binding.getRoot());
+        mContext = this;
+        try {
+            //设置坚屏 一定要放到try catch里面,否则会崩溃
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        } catch (Exception e) {
+        }
+        TAG = this.getClass().getSimpleName();
     }
+
+
+    public void setStatusBarTransparent(boolean isBlack){
+        //透明状态栏
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+        //透明导航栏
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+    }
+
+    /**
+     * 修改状态栏颜色,支持4.4以上版本
+     * @param colorId
+     */
+    public void setStatusBarBgColor(@ColorRes int colorId) {
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        getWindow().setStatusBarColor(getResources().getColor(colorId));
+    }
+
+    /**
+     * 修改状态栏文本颜色
+     * @param isBlack
+     */
+    public void setStatusBarTextColor(boolean isBlack){
+        View decor = getWindow().getDecorView();
+        if (isBlack) {
+            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        } else {
+            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+        }
+    }
+
+    /**
+     * 隐藏虚拟按键
+     */
+    public void hideBottomUIMenu() {
+        //隐藏虚拟按键
+        if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
+            View v = getWindow().getDecorView();
+            v.setSystemUiVisibility(View.GONE);
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            //for new api versions.
+            View decorView = getWindow().getDecorView();
+            int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY  ;
+            decorView.setSystemUiVisibility(uiOptions);
+        }
+
+    }
+
+    /**
+     * 状态栏高度
+     * @return
+     */
+    public int getStatusBarHeight() {
+        int result = 0;
+        int resId = getResources().getIdentifier("status_bar_height", "dimen", "android");
+        if (resId > 0) {
+            result = getResources().getDimensionPixelOffset(resId);
+        }
+        return result;
+    }
+
+
+    long mExitTime= 0 ;
+    /**
+     * 返回键退出程序
+     */
+    public void backExit() {
+        if ((System.currentTimeMillis() - mExitTime) > 2000) {
+            Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
+            mExitTime = System.currentTimeMillis();
+        } else {
+            //此方法导致app关闭后重启
+            MyApplication.getApplication().quitApp();
+            System.exit(0);
+            //quitApp();
+        }
+    }
+    ApkUpGradeResult.AppInfo apkUpGrade;
+    ProgressDialog progressDialog ;
+
+    public void showUpdateDialog(){
+        if(apkUpGrade == null){
+            return;
+        }
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle("新版本").setMessage("有新版更新\n"+apkUpGrade.content)
+            .setPositiveButton("", new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    dialog.dismiss();
+                    okHttpDownload(apkUpGrade.url, new ResPonse<String>() {
+                        @Override
+                        public void doSuccess(String path) {
+                            apkUpGrade.remark = path;
+                            openAPK(new File(path));
+                        }
+
+                        @Override
+                        public void doError(String path) {
+                            if(apkUpGrade.isImportant==1){
+                                showUpdateDialog();
+                            }else {
+                                if(path != null) {
+                                    apkUpGrade.remark = path;
+                                    openAPK(new File(path));
+                                }
+                            }
+                        }
+                    });
+                }
+            })
+            .setNegativeButton(apkUpGrade.isImportant==1?"退出":"取消", new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    if(apkUpGrade.isImportant==1){
+                        MyApplication.getApplication().quitApp();
+                    }else {
+                        SpUtils.getInstance().putLongValOfProject(mContext,"version", new Date().getTime());//记录今天已经请求过更新  不在自动请求
+                    }
+                }
+            });
+    }
+
+    /**
+     * okhttp下载文件
+     * @param downloadUrl
+     * @param resPonse
+     */
+    protected void okHttpDownload(final String downloadUrl, final ResPonse<String> resPonse){
+        PermissionX.init(this).permissions(FILE_PERMISSIONS).onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() {
+            @Override
+            public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) {
+                scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "继续申请","我已明白");
+            }
+        }).onForwardToSettings(new ForwardToSettingsCallback() {
+            @Override
+            public void onForwardToSettings(ForwardScope scope, List<String> deniedList) {
+                scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "去设置","我已明白");
+            }
+        }).request(new RequestCallback() {
+            @Override
+            public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {
+                if(allGranted){
+
+                    OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
+
+                    Request request = new Request.Builder()
+                            .url(downloadUrl)
+                            .get()
+                            .build();
+
+                    okHttpClient.newCall(request).enqueue(new Callback() {
+                        @Override
+                        public void onFailure(Call call, IOException e) {
+                            Log.e("tag", "onFailure: " + e.getMessage());
+                        }
+
+                        @Override
+                        public void onResponse(Call call, Response response) throws IOException {
+                            ResponseBody body = response.body();        // 获取到请求体
+                            InputStream inputStream = body.byteStream();    // 转换成字节流
+                            progressDialog = new ProgressDialog(mContext);
+                            progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+                            progressDialog.setTitle("正在下载...");
+                            progressDialog.setCancelable(false);
+                            progressDialog.show();
+                            saveFile(inputStream, getSaveFilePath(downloadUrl), body.contentLength(),resPonse);
+                        }
+                    });
+                } else {
+                    Toast.makeText(mContext, "您拒绝了权限申请!", Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+    }
+
+    /**
+     * @param inputStream
+     * @param filePath           存放的地址
+     * @param l           文件的长度
+     */
+    private void saveFile(InputStream inputStream, final String filePath, final long l, final ResPonse<String> resPonse) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                progressDialog.setMax((int)l);// 设置进度条最大值
+            }
+        });
+        long count = 0;
+        try {
+            File file = new File(filePath);
+            Log.i(TAG, "文件保存路径:" + filePath+" 文件是否存在:"+file.exists());
+            if (!file.exists()) {//文件不存在
+                file.createNewFile();
+            }else{
+                file.delete();
+            }
+            // 获取到输出流,写入到的地址
+            FileOutputStream outputStream = new FileOutputStream(file);
+            int length = -1;
+            byte[] bytes = new byte[1024 * 10];
+            while ((length = inputStream.read(bytes)) != -1) {
+                // 写入文件
+                outputStream.write(bytes, 0, length);
+                count += length;
+
+                final long finalCount = count;
+                final int finalLenght = length;
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        progressDialog.setProgress((int) finalCount);     // 设置进度
+                    }
+                });
+            }
+            inputStream.close();        // 关闭输入流
+            outputStream.close();       // 关闭输出流
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    // 如果写入的进度值完毕,Toast
+                    progressDialog.dismiss();
+                    resPonse.doSuccess(filePath);
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 获取文件保存路径 sdcard根目录/download/文件名称
+     * @param fileUrl
+     * @return
+     */
+    public static String getSaveFilePath(String fileUrl){
+        String fileName=fileUrl.substring(fileUrl.lastIndexOf("/")+1,fileUrl.length());//获取文件名称
+        String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Download";
+        File appDir = new File(storePath);
+        if (!appDir.exists()) {
+            appDir.mkdirs();
+        }
+        return storePath + File.separator +fileName;
+    }
+
+    /**
+     * 打开apk文件
+     * @param file
+     */
+    public void openAPK(final File file){
+
+        //android 8及以上需要设置权限
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getPackageManager().canRequestPackageInstalls()) {
+            // 申请安装权限。
+            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle("需要允许安装权限").setMessage("应用需要手动设置,才能打开安装包升级应用").setCancelable(false)
+                    .setNegativeButton("", new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            Uri packageUri = Uri.parse("package:" + getPackageName());
+                            Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageUri);
+                            startActivityForResult(intent, REQUEST_INSTALL_PERMISSION);
+                            dialog.dismiss();
+                        }
+                    })
+                    .setPositiveButton(apkUpGrade.isImportant==1?"退出":"取消", new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            if(apkUpGrade.isImportant==1){
+                                MyApplication.getApplication().quitApp();
+                            }
+                        }
+                    }).create().show();
+            return;
+        }
+
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_VIEW);
+        Uri photoURI = null ;
+        if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N) { //判读版本是否在7.0以上
+            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            photoURI = FileProvider.getUriForFile(mContext, mContext.getApplicationContext().getPackageName() + ".provider", file);//添加这一句表示对目标应用临时授权该Uri所代表的文件
+
+        }else{
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            photoURI = Uri.fromFile(file);
+        }
+        intent.setDataAndType(photoURI,"application/vnd.android.package-archive");
+        startActivityForResult(intent,REQUEST_INSTALL_APK);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        MyLog.i(TAG,String.format("onActivityResult requestCode:%s, resultCode:%s, data:%s",requestCode, resultCode, data));
+        if(requestCode == REQUEST_INSTALL_PERMISSION){
+            openAPK(new File(apkUpGrade.remark));
+        }else if(requestCode == REQUEST_INSTALL_APK && apkUpGrade.isImportant==1){
+            /*showDialog("强制更新", "需要安装新版本才能继续使用","安装","退出", new ResPonse() {
+                @Override
+                public void doSuccess(Object obj) {
+
+                }
+
+                @Override
+                public void doError(Object obj) {
+                    super.doError(obj);
+                }
+            });*/
+        }
+    }
+
+
+    protected boolean onBackKeyDown() {
+        onBackPressed();
+        return false;
+    }
+
 }
diff --git a/app/src/main/java/com/duqing/missions/base/BaseAdapter.java b/app/src/main/java/com/duqing/missions/base/BaseAdapter.java
new file mode 100644
index 0000000..520e455
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/base/BaseAdapter.java
@@ -0,0 +1,154 @@
+package com.duqing.missions.base;
+
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewbinding.ViewBinding;
+
+import com.duqing.missions.R;
+import com.duqing.missions.common.NullViewHolder;
+import com.duqing.missions.databinding.LayoutNullBinding;
+import com.duqing.missions.util.DeviceUtil;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Administrator on 2021/10/27 0027.
+ *  T  数据类型
+ *  V 适配器视图
+ */
+public abstract class BaseAdapter<B extends ViewBinding,A extends BaseActivity,T> extends RecyclerView.Adapter {
+
+    protected List<T> mData = new ArrayList<>();
+
+    protected Drawable nullDrawable;
+    protected String nullTxt="暂无数据";
+    protected String TAG = "BaseAdapter";
+    protected  A activity;
+    protected LayoutNullBinding nullBinding;
+
+    public BaseAdapter(){
+    }
+
+    public BaseAdapter(@NonNull List<T> data){
+        mData = data;
+    }
+
+    public List<T> getData() {
+        return mData;
+    }
+
+    public void setData(@NonNull List<T> data){
+        mData = data;
+        notifyDataSetChanged();
+    }
+
+    @NonNull
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        // get genericity "B"
+        Class<B> entityClass = (Class<B>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+        try {
+           /* for(Method method: entityClass.getMethods()){
+                StringBuilder sb = new StringBuilder();
+                for(Class type : method.getParameterTypes()){
+                    sb.append(type.getSimpleName()+",");
+                }
+                Log.e(TAG,String.format("method:%s,return:%s,param:%s",method.getName(),method.getReturnType().getSimpleName(),sb.toString()));
+            }*/
+            Method method = entityClass.getMethod("inflate", LayoutInflater.class,ViewGroup.class,boolean.class);//get method from name "inflate";
+            B vBind = (B) method.invoke(entityClass,LayoutInflater.from(parent.getContext()),parent,false);//execute method to create a objct of viewbind;
+            return new ViewBindHolder(vBind);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return new NullViewHolder( LayoutNullBinding.inflate(LayoutInflater.from(parent.getContext())));
+    }
+
+    /**
+     *
+     * 创建视图
+     * @param parent
+     * @param layout  视图文件
+     * @param viewType 视图类型   1 加载正常视图   其他则加载空数据
+     * @return
+     */
+    protected View getRootView(@NonNull ViewGroup parent, @LayoutRes int layout,int viewType ){
+        //MyLog.i(TAG,"getRootView viewType:"+viewType);
+        return LayoutInflater.from(parent.getContext()).inflate(viewType==1?layout: R.layout.layout_null,parent,false);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+        //MyLog.i(TAG,"onBindViewHolder position:"+position+" "+mData.size()+" "+getItemViewType(position));
+        if(getItemViewType(position)==0){
+            return;
+        }
+        try {
+            activity = (A) holder.itemView.getContext();
+        }catch (Exception e){}
+        bindView((ViewBindHolder) holder,mData.size()==0?null:mData.get(position),position);
+    }
+
+    /**
+     * 设置最后一个底部间隔
+     * @param holder
+     * @param position
+     */
+    protected void setBottomMargin(ViewBindHolder holder, int position){
+        setBottomMargin(holder,position,23);
+    }
+
+    /**
+     * 设置最后一个底部间隔
+     * @param holder
+     * @param position  位置
+     * @param dp        间距
+     */
+    protected void setBottomMargin(RecyclerView.ViewHolder holder, int position,int dp){
+        setBottomMargin(holder,position,dp,0);
+    }
+    protected void setBottomMargin(RecyclerView.ViewHolder holder, int position, int dp, int defaultDp){
+        ViewGroup.MarginLayoutParams params1 = (ViewGroup.MarginLayoutParams) holder.itemView.getLayoutParams();
+        if(position == mData.size() -1){
+            params1.setMargins(params1.leftMargin, params1.topMargin, params1.rightMargin, DeviceUtil.convertDpToPixel(dp,holder.itemView.getContext()));
+        }else{
+            params1.setMargins(params1.leftMargin, params1.topMargin, params1.rightMargin, DeviceUtil.convertDpToPixel(defaultDp,holder.itemView.getContext()));
+        }
+    }
+    protected abstract void bindView(ViewBindHolder holder,T data,int position);
+
+
+    @Override
+    public int getItemCount() {
+        //默认显示空视图,若不显示空视图则重写该方法,返回mData.size()
+        return mData == null || mData.size() == 0 ?1:mData.size();
+    }
+
+
+    @Override
+    public int getItemViewType(int position) {
+        //当下标为0,数据集合为0 返回0(意味当前应显示空数据视图))
+        //MyLog.i(TAG,"getItemViewType position:"+position+" mdata:"+mData.size()+" "+(position ==0 && mData.size()==0));
+        return position ==0 && mData.size()==0?0:1;
+    }
+
+    public class ViewBindHolder extends RecyclerView.ViewHolder{
+
+        B binding;
+
+        public ViewBindHolder( B binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+        }
+    }
+
+}
diff --git a/app/src/main/java/com/duqing/missions/base/BaseFragment.java b/app/src/main/java/com/duqing/missions/base/BaseFragment.java
new file mode 100644
index 0000000..c4eccf8
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/base/BaseFragment.java
@@ -0,0 +1,52 @@
+package com.duqing.missions.base;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.viewbinding.ViewBinding;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public abstract class BaseFragment<A extends BaseActivity,B extends ViewBinding> extends Fragment {
+
+    protected  A activity;
+    protected  B binding;
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+        // get genericity "B"
+        Class<B> entityClass = (Class<B>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
+        try {
+            Method method = entityClass.getMethod("inflate", LayoutInflater.class,ViewGroup.class,boolean.class);//get method from name "inflate";
+            binding = (B) method.invoke(entityClass,inflater,container,false);//execute method to create a objct of viewbind;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return binding.getRoot();
+    }
+
+    @Override
+    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        activity = (A) getActivity();
+        initViews();
+    }
+
+    public abstract void initViews();
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        binding = null;
+    }
+
+}
diff --git a/app/src/main/java/com/duqing/missions/common/NullViewHolder.java b/app/src/main/java/com/duqing/missions/common/NullViewHolder.java
new file mode 100644
index 0000000..21bd137
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/common/NullViewHolder.java
@@ -0,0 +1,18 @@
+package com.duqing.missions.common;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.duqing.missions.databinding.LayoutNullBinding;
+
+/**
+ * 空数据显示
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class NullViewHolder extends RecyclerView.ViewHolder {
+    LayoutNullBinding binding;
+
+    public NullViewHolder(LayoutNullBinding binding) {
+        super(binding.getRoot());
+        this.binding = binding;
+    }
+}
diff --git a/app/src/main/java/com/duqing/missions/data/ApkUpGradeResult.java b/app/src/main/java/com/duqing/missions/data/ApkUpGradeResult.java
new file mode 100644
index 0000000..27a34a8
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/data/ApkUpGradeResult.java
@@ -0,0 +1,29 @@
+package com.duqing.missions.data;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class ApkUpGradeResult extends BaseApiResult<ApkUpGradeResult.AppInfo> {
+
+    public class AppInfo {
+        //以下为声明的参数
+        public int type, isEnable, isImportant, id, code;
+        public String url,version, content, udate, remark;
+
+
+        @Override
+        public String toString() {
+            return "ApkUpGrade{" +
+                    "type=" + type + "," +
+                    "isEnable=" + isEnable + "," +
+                    "isImportant=" + isImportant + "," +
+                    "id=" + id + "," +
+                    "url=" + url + "," +
+                    "version=" + version + "," +
+                    "content=" + content + "," +
+                    "udate=" + udate + "," +
+                    '}';
+        }
+
+    }
+}
diff --git a/app/src/main/java/com/duqing/missions/data/BaseApiResult.java b/app/src/main/java/com/duqing/missions/data/BaseApiResult.java
new file mode 100644
index 0000000..ecd9acc
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/data/BaseApiResult.java
@@ -0,0 +1,23 @@
+package com.duqing.missions.data;
+
+import java.io.Serializable;
+
+/**
+ * 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;
+
+
+    @Override
+    public String toString() {
+        return "BaseApiData{" +
+                "msg='" + msg + '\'' +
+                ", code=" + code +
+                ", data=" + data +
+                '}';
+    }
+
+}
diff --git a/app/src/main/java/com/duqing/missions/data/BasePageResult.java b/app/src/main/java/com/duqing/missions/data/BasePageResult.java
new file mode 100644
index 0000000..6ae8be0
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/data/BasePageResult.java
@@ -0,0 +1,21 @@
+package com.duqing.missions.data;
+
+import java.util.ArrayList;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class BasePageResult<T> extends BaseApiResult<String>{
+    public int pages;
+    public int total;
+    public int pageNum;
+    public ArrayList<T> rows;
+
+    @Override
+    public String toString() {
+        return "PageBean{" +
+                ", total=" + total +
+                ", rows=" + rows +
+                '}';
+    }
+}
diff --git a/app/src/main/java/com/duqing/missions/source/HttpPageSource.java b/app/src/main/java/com/duqing/missions/source/HttpPageSource.java
new file mode 100644
index 0000000..fa15ca4
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/source/HttpPageSource.java
@@ -0,0 +1,16 @@
+package com.duqing.missions.source;
+
+import com.duqing.missions.data.BasePageResult;
+
+import java.util.Observable;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public interface HttpPageSource<T extends BasePageResult> {
+
+    Observable refresh();
+
+    Observable loadMore(int page,int size);
+
+}
diff --git a/app/src/main/java/com/duqing/missions/ui/dashboard/DashboardViewModel.java b/app/src/main/java/com/duqing/missions/ui/dashboard/DashboardViewModel.java
deleted file mode 100644
index 2b45769..0000000
--- a/app/src/main/java/com/duqing/missions/ui/dashboard/DashboardViewModel.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.duqing.missions.ui.dashboard;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-public class DashboardViewModel extends ViewModel {
-
-    private MutableLiveData<String> mText;
-
-    public DashboardViewModel() {
-        mText = new MutableLiveData<>();
-        mText.setValue("This is dashboard fragment");
-    }
-
-    public LiveData<String> getText() {
-        return mText;
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/duqing/missions/ui/home/HomeFragment.java b/app/src/main/java/com/duqing/missions/ui/home/HomeFragment.java
deleted file mode 100644
index 9e8e621..0000000
--- a/app/src/main/java/com/duqing/missions/ui/home/HomeFragment.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.duqing.missions.ui.home;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.GridLayoutManager;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.duqing.missions.databinding.FragmentHomeBinding;
-import com.scwang.smart.refresh.header.ClassicsHeader;
-import com.scwang.smart.refresh.layout.SmartRefreshLayout;
-
-public class HomeFragment extends Fragment {
-
-    private HomeViewModel homeViewModel;
-    private FragmentHomeBinding binding;
-
-    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
-
-        binding = FragmentHomeBinding.inflate(inflater, container, false);
-        View root = binding.getRoot();
-        final SmartRefreshLayout smartRefresh = binding.smartRefresh;
-        smartRefresh.setRefreshHeader(new ClassicsHeader(getContext()));
-        final  TextView textView = binding.textRecommend;
-        final ImageView imgSearch = binding.imgSearch;
-        RecyclerView recyclerClassify = binding.recyclerClassify;
-        RecyclerView recyclerTop = binding.recyclerTop;
-        final RecyclerView recyclerRecommend = binding.recyclerRecommend;
-        recyclerRecommend.setLayoutManager(new LinearLayoutManager(getContext()));
-        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
-        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
-        recyclerClassify.setLayoutManager(layoutManager);
-        recyclerTop.setLayoutManager(new GridLayoutManager(getContext(),3));
-
-        homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
-            @Override
-            public void onChanged(@Nullable String s) {
-                textView.setText(s);
-            }
-        });
-        return root;
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        binding = null;
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/duqing/missions/ui/home/HomeViewModel.java b/app/src/main/java/com/duqing/missions/ui/home/HomeViewModel.java
deleted file mode 100644
index 59bff9d..0000000
--- a/app/src/main/java/com/duqing/missions/ui/home/HomeViewModel.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.duqing.missions.ui.home;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-public class HomeViewModel extends ViewModel {
-
-    private MutableLiveData<String> mText;
-
-    public HomeViewModel() {
-        mText = new MutableLiveData<>();
-        mText.setValue("This is home fragment");
-    }
-
-    public LiveData<String> getText() {
-        return mText;
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/duqing/missions/ui/main/Text.java b/app/src/main/java/com/duqing/missions/ui/main/Text.java
new file mode 100644
index 0000000..1ab6c35
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/ui/main/Text.java
@@ -0,0 +1,7 @@
+package com.duqing.missions.ui.main;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class Text {
+}
diff --git a/app/src/main/java/com/duqing/missions/ui/dashboard/DashboardFragment.java b/app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java
similarity index 91%
rename from app/src/main/java/com/duqing/missions/ui/dashboard/DashboardFragment.java
rename to app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java
index a63d963..a0d8ef3 100644
--- a/app/src/main/java/com/duqing/missions/ui/dashboard/DashboardFragment.java
+++ b/app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java
@@ -1,10 +1,9 @@
-package com.duqing.missions.ui.dashboard;
+package com.duqing.missions.ui.main.dashboard;
 
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -12,7 +11,6 @@
 import androidx.lifecycle.Observer;
 import androidx.lifecycle.ViewModelProvider;
 
-import com.duqing.missions.R;
 import com.duqing.missions.databinding.FragmentDashboardBinding;
 
 public class DashboardFragment extends Fragment {
diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java b/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java
new file mode 100644
index 0000000..665138e
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java
@@ -0,0 +1,113 @@
+package com.duqing.missions.ui.main.home;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.duqing.missions.MainActivity;
+import com.duqing.missions.base.BaseFragment;
+import com.duqing.missions.databinding.FragmentHomeBinding;
+import com.duqing.missions.ui.main.home.adapter.MissiontTopAdapter;
+import com.duqing.missions.ui.main.home.model.MissionDesc;
+import com.scwang.smart.refresh.footer.ClassicsFooter;
+import com.scwang.smart.refresh.header.ClassicsHeader;
+import com.scwang.smart.refresh.layout.SmartRefreshLayout;
+import com.scwang.smart.refresh.layout.api.RefreshLayout;
+import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener;
+
+import java.util.List;
+
+public class HomeFragment extends BaseFragment<MainActivity,FragmentHomeBinding> {
+
+    private HomeViewModel homeViewModel;
+    final String TAG = "HomeFragment";
+
+
+    @Override
+    public void initViews() {
+        homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
+        final SmartRefreshLayout smartRefresh = binding.smartRefresh;
+        smartRefresh.setRefreshHeader(new ClassicsHeader(getContext()));
+        smartRefresh.setRefreshFooter(new ClassicsFooter(getContext()));
+        final  TextView textView = binding.textRecommend;
+        final ImageView imgSearch = binding.imgSearch;
+        RecyclerView recyclerClassify = binding.recyclerClassify;
+        RecyclerView recyclerTop = binding.recyclerTop;
+        final RecyclerView recyclerRecommend = binding.recyclerRecommend;
+        recyclerRecommend.setLayoutManager(new LinearLayoutManager(getContext()));
+        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
+        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
+        recyclerClassify.setLayoutManager(layoutManager);
+        recyclerTop.setLayoutManager(new GridLayoutManager(getContext(),3));
+        smartRefresh.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() {
+            @Override
+            public void onLoadMore(@NonNull RefreshLayout refreshLayout) {
+                homeViewModel.onLoadMore();
+            }
+
+            @Override
+            public void onRefresh(@NonNull RefreshLayout refreshLayout) {
+                homeViewModel.onRefresh();
+            }
+        });
+        final MissiontTopAdapter topAdapter = new MissiontTopAdapter();
+        recyclerTop.setAdapter(topAdapter);
+        homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
+            @Override
+            public void onChanged(@Nullable String s) {
+                textView.setText(s);
+            }
+        });
+        homeViewModel.getMissions().observe(getViewLifecycleOwner(), new Observer<List<MissionDesc>>() {
+            @Override
+            public void onChanged(List<MissionDesc> missionDescs) {
+                smartRefresh.finishRefresh();
+                smartRefresh.finishLoadMore();
+                topAdapter.setData(missionDescs);
+            }
+        });
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
+        Log.e(TAG,"onCreateView");
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.e(TAG,"onResume");
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        Log.e(TAG,"onPause");
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        Log.e(TAG,"onStop");
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        Log.e(TAG,"onDestroyView");
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java b/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java
new file mode 100644
index 0000000..e76862b
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java
@@ -0,0 +1,47 @@
+package com.duqing.missions.ui.main.home;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.ViewModel;
+
+import com.duqing.missions.ui.main.home.model.MissionDesc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HomeViewModel extends ViewModel {
+
+    private MutableLiveData<String> mText;
+    private MutableLiveData<List<MissionDesc>> missions = new MutableLiveData<>();
+
+    public HomeViewModel() {
+        mText = new MutableLiveData<>();
+        mText.setValue("This is home fragment");
+    }
+
+    public LiveData<String> getText() {
+        return mText;
+    }
+
+    public MutableLiveData<List<MissionDesc>> getMissions() {
+        return missions;
+    }
+
+    public void onRefresh(){
+        List<MissionDesc> list = new ArrayList<>();
+        list.add(new MissionDesc());
+        list.add(new MissionDesc());
+        list.add(new MissionDesc());
+        list.add(new MissionDesc());
+        missions.setValue(list);
+    }
+
+    public void onLoadMore(){
+        List<MissionDesc> list = missions.getValue();
+        list.add(new MissionDesc());
+        list.add(new MissionDesc());
+        list.add(new MissionDesc());
+        list.add(new MissionDesc());
+        missions.postValue(list);
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/adapter/MissiontTopAdapter.java b/app/src/main/java/com/duqing/missions/ui/main/home/adapter/MissiontTopAdapter.java
new file mode 100644
index 0000000..de21915
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/ui/main/home/adapter/MissiontTopAdapter.java
@@ -0,0 +1,18 @@
+package com.duqing.missions.ui.main.home.adapter;
+
+import com.duqing.missions.MainActivity;
+import com.duqing.missions.base.BaseAdapter;
+import com.duqing.missions.databinding.HomeItemTopBinding;
+import com.duqing.missions.ui.main.home.model.MissionDesc;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class MissiontTopAdapter extends BaseAdapter<HomeItemTopBinding, MainActivity, MissionDesc> {
+
+
+    @Override
+    protected void bindView(ViewBindHolder holder, MissionDesc data, int position) {
+
+    }
+}
diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/model/MissionDesc.java b/app/src/main/java/com/duqing/missions/ui/main/home/model/MissionDesc.java
new file mode 100644
index 0000000..a67b8ba
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/ui/main/home/model/MissionDesc.java
@@ -0,0 +1,12 @@
+package com.duqing.missions.ui.main.home.model;
+
+/**
+ * 任务列表数据数据
+ * Created by Administrator on 2021/10/27 0027.
+ */
+public class MissionDesc {
+    public String title,type,label;
+    public double price;
+    public int participants,remaining,total;
+
+}
diff --git a/app/src/main/java/com/duqing/missions/ui/main/home/model/TopItemViewModel.java b/app/src/main/java/com/duqing/missions/ui/main/home/model/TopItemViewModel.java
new file mode 100644
index 0000000..8b2ab82
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/ui/main/home/model/TopItemViewModel.java
@@ -0,0 +1,11 @@
+package com.duqing.missions.ui.main.home.model;
+
+import androidx.lifecycle.ViewModel;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class TopItemViewModel extends ViewModel {
+
+
+}
diff --git a/app/src/main/java/com/duqing/missions/ui/notifications/NotificationsFragment.java b/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java
similarity index 95%
rename from app/src/main/java/com/duqing/missions/ui/notifications/NotificationsFragment.java
rename to app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java
index 7ca38c9..e94c5af 100644
--- a/app/src/main/java/com/duqing/missions/ui/notifications/NotificationsFragment.java
+++ b/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java
@@ -1,4 +1,4 @@
-package com.duqing.missions.ui.notifications;
+package com.duqing.missions.ui.main.notifications;
 
 import android.os.Bundle;
 import android.view.LayoutInflater;
diff --git a/app/src/main/java/com/duqing/missions/ui/notifications/NotificationsViewModel.java b/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsViewModel.java
similarity index 89%
rename from app/src/main/java/com/duqing/missions/ui/notifications/NotificationsViewModel.java
rename to app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsViewModel.java
index d28fa85..ed7c442 100644
--- a/app/src/main/java/com/duqing/missions/ui/notifications/NotificationsViewModel.java
+++ b/app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsViewModel.java
@@ -1,4 +1,4 @@
-package com.duqing.missions.ui.notifications;
+package com.duqing.missions.ui.main.notifications;
 
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
diff --git a/app/src/main/java/com/duqing/missions/util/DeviceUtil.java b/app/src/main/java/com/duqing/missions/util/DeviceUtil.java
new file mode 100644
index 0000000..3679ee8
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/util/DeviceUtil.java
@@ -0,0 +1,374 @@
+package com.duqing.missions.util;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.os.Build;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import androidx.core.app.ActivityCompat;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+
+
+/**
+ * Created by EDZ on 2018/1/30.
+ */
+public class DeviceUtil {
+    public static final String TAG = "DeviceUtil";
+
+    /**
+     * 设备信息
+     *
+     * @param context
+     */
+    public static void getDisplay(Context context) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Version code is  \n");
+        sb.append("设备的Android版本号:");//设备的Android版本号
+        sb.append(getSDKInt() + " " + getSDKVersion() + "\t");//设备的Android版本号
+        sb.append("设备型号:");//设备型号
+        sb.append(getDeviceModel() + "\t");//设备型号
+        sb.append("设备厂商:");//设备型号
+        sb.append(getDeviceBrand() + "\t");//设备型号
+        sb.append("程序版本号:" + getAppVersionCode(context) + " " + getAppVersionName(context) + "\t");//程序版本号
+        sb.append("设备唯一标识符:" + getSerialNumber(context));
+        sb.append("\n设备imei:" + getIMEI(context));
+        String str = sb.toString() + " \n";
+        str += getDisplayInfomation(context) + " \n";
+        str += getDensity(context) + " \n";
+        str += "屏幕大小:" + getScreenInch(context) + "英寸 \n";
+        str += getAndroiodScreenProperty(context) + "\n";
+        Log.i(TAG, str);
+    }
+
+    /**
+     * Double类型保留指定位数的小数,返回double类型(四舍五入)
+     * newScale 为指定的位数
+     */
+    private static double formatDouble(double d, int newScale) {
+        BigDecimal bd = new BigDecimal(d);
+        return bd.setScale(newScale, BigDecimal.ROUND_HALF_UP).doubleValue();
+    }
+
+    /**
+     * 设备型号
+     * @return
+     */
+    public static String getDeviceModel() {
+        return Build.MODEL;
+    }
+
+    /**
+     * 设备厂商
+     * @return
+     */
+    public static String getDeviceBrand() {
+        return Build.BRAND;
+    }
+
+    /**
+     * 设备的Android版本号
+     * @return
+     */
+    public static int getSDKInt() {
+        return Build.VERSION.SDK_INT;
+    }
+
+    /**
+     * 设备的Android版本号
+     * @return
+     */
+    public static String getSDKVersion() {
+        return Build.VERSION.RELEASE;
+    }
+
+    /**
+     * 程序版本号
+     * @param context
+     * @return
+     */
+    public static int getAppVersionCode(Context context) {
+        try {
+            return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return 1;
+    }
+
+    /**
+     * 程序版本号
+     *
+     * @param context
+     * @return
+     */
+    public static String getAppVersionName(Context context) {
+        try {
+            return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+    /**
+     * 屏幕像素px
+     */
+    @SuppressLint("NewApi")
+    public static String getDisplayInfomation(Context context) {
+        Point point = new Point();
+        ((Activity) context).getWindowManager().getDefaultDisplay().getSize(point);
+        Log.d(TAG, "the screen size is " + point.toString());
+        ((Activity) context).getWindowManager().getDefaultDisplay().getRealSize(point);
+        Log.d(TAG, "the screen real size is " + point.toString());
+        return point.toString();
+    }
+
+    /**
+     * 屏幕信息
+     * @param context
+     * @return
+     */
+    public static String getAndroiodScreenProperty(Context context) {
+        int width = getScreenPixel(context).widthPixels;         // 屏幕宽度(像素)
+        int height = getScreenPixel(context).heightPixels;       // 屏幕高度(像素)
+        float density = getDensity(getScreenPixel(context));         // 屏幕密度(0.75 / 1.0 / 1.5)
+        int densityDpi = getDpi(context);     // 屏幕密度dpi(120 / 160 / 240)
+        // 屏幕宽度算法:屏幕宽度(像素)/屏幕密度
+        int screenWidth = getDp(context)[0];  // 屏幕宽度(dp)
+        int screenHeight = getDp(context)[1];// 屏幕高度(dp)
+
+
+        Log.d("h_bl", "屏幕宽度(像素):" + width);
+        Log.d("h_bl", "屏幕高度(像素):" + height);
+        Log.d("h_bl", "屏幕密度(0.75 / 1.0 / 1.5):" + density);
+        Log.d("h_bl", "屏幕密度dpi(120 / 160 / 240):" + densityDpi);
+        Log.d("h_bl", "屏幕宽度(dp):" + screenWidth);  // 屏幕适配文件夹(例:layout-sw300dp),是以该属性为准则
+        Log.d("h_bl", "屏幕高度(dp):" + screenHeight);
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("屏幕宽度(像素):" + width + "\n");
+        sb.append("屏幕高度(像素):" + height + "\n");
+        sb.append("屏幕密度(0.75 / 1.0 / 1.5):" + density + "\n");
+        sb.append("屏幕密度dpi(120 / 160 / 240):" + densityDpi + "\n");
+        sb.append("屏幕宽度(dp):" + screenWidth + "\n");
+        sb.append("屏幕高度(dp):" + screenHeight + "\n");
+        return sb.toString();
+
+    }
+
+    /**
+     * 通知栏高度
+     * @param context
+     * @return
+     */
+    public static int getStatusBarHeight(Context context) {
+        Class<?> c = null;
+        Object obj = null;
+        Field field = null;
+        int x = 0, statusBarHeight = 0;
+        try {
+            c = Class.forName("com.android.internal.R$dimen");
+            obj = c.newInstance();
+            field = c.getField("status_bar_height");
+            x = Integer.parseInt(field.get(obj).toString());
+            statusBarHeight = context.getResources().getDimensionPixelSize(x);
+        } catch (Exception e1) {
+            e1.printStackTrace();
+        }
+        return statusBarHeight;
+    }
+
+    /**
+     * 屏幕密度dpi
+     */
+    public static int getDpi(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        DisplayMetrics dm = new DisplayMetrics();
+        wm.getDefaultDisplay().getMetrics(dm);
+        return dm.densityDpi;
+    }
+
+
+    /**
+     * 屏幕像素px
+     */
+    @SuppressLint("NewApi")
+    public static Point getDisplayPixel(Context context) {
+        Point point = new Point();
+        ((Activity) context).getWindowManager().getDefaultDisplay().getSize(point);
+        Log.d(TAG, "the screen size is " + point.toString());
+        ((Activity) context).getWindowManager().getDefaultDisplay().getRealSize(point);
+        Log.d(TAG, "the screen real size is " + point.toString());
+        return point;
+    }
+
+    /**
+     * 屏幕像素px
+     */
+    public static DisplayMetrics getScreenPixel(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        DisplayMetrics dm = new DisplayMetrics();
+        wm.getDefaultDisplay().getMetrics(dm);
+        return dm;
+    }
+
+    /**
+     * 屏幕dp
+     */
+    public static int[] getDp(Context context) {
+        int width = getScreenPixel(context).widthPixels;         // 屏幕宽度(像素)
+        int height = getScreenPixel(context).heightPixels;       // 屏幕高度(像素)
+        float density = getDensity(getScreenPixel(context));         // 屏幕密度(0.75 / 1.0 / 1.5)
+        // 屏幕宽度算法:屏幕宽度(像素)/屏幕密度
+        int screenWidth = (int) (width / density);  // 屏幕宽度(dp)
+        int screenHeight = (int) (height / density);// 屏幕高度(dp)
+        return new int[]{screenWidth, screenHeight};
+    }
+
+    /**
+     * 屏幕密度
+     */
+    public static float getDensity(DisplayMetrics dm) {
+        return dm.density;         // 屏幕密度(0.75 / 1.0 / 1.5);
+    }
+
+    /**
+     * 屏幕密度dpi
+     */
+    public static String getDensity(Context context) {
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        Log.d(TAG, "Density is " + displayMetrics.density + " densityDpi is " + displayMetrics.densityDpi + " height: " + displayMetrics.heightPixels +
+                " width: " + displayMetrics.widthPixels);
+        return "Density is " + displayMetrics.density + " densityDpi is " + displayMetrics.densityDpi + " height: " + displayMetrics.heightPixels +
+                " width: " + displayMetrics.widthPixels;
+    }
+
+
+    /**
+     * 屏幕尺寸inch
+     */
+    public static double getScreenInch(Context context) {
+
+        try {
+            int realWidth = 0, realHeight = 0;
+            Display display = ((Activity) context).getWindowManager().getDefaultDisplay();
+            DisplayMetrics metrics = new DisplayMetrics();
+            display.getMetrics(metrics);
+            if (Build.VERSION.SDK_INT >= 17) {
+                Point size = new Point();
+                display.getRealSize(size);
+                realWidth = size.x;
+                realHeight = size.y;
+            } else if (Build.VERSION.SDK_INT < 17
+                    && Build.VERSION.SDK_INT >= 14) {
+                Method mGetRawH = Display.class.getMethod("getRawHeight");
+                Method mGetRawW = Display.class.getMethod("getRawWidth");
+                realWidth = (Integer) mGetRawW.invoke(display);
+                realHeight = (Integer) mGetRawH.invoke(display);
+            } else {
+                realWidth = metrics.widthPixels;
+                realHeight = metrics.heightPixels;
+            }
+
+            return formatDouble(Math.sqrt((realWidth / metrics.xdpi) * (realWidth / metrics.xdpi) + (realHeight / metrics.ydpi) * (realHeight / metrics.ydpi)), 1);
+
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return 0;
+    }
+
+    /**
+     * dp获取dip
+     * @param dp
+     * @return
+     */
+    public static int convertDpToPixel(float dp, Context context) {
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        return (int) (dp * displayMetrics.density);
+    }
+
+    /***
+     * px获取dip
+     * @param pixel
+     * @return
+     */
+    public static int convertPixelToDp(int pixel, Context context) {
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        return (int) (pixel / displayMetrics.density);
+    }
+
+    public static String getSerialNumber(Context context) {
+        String serial = "";
+        try {
+            if (Build.VERSION.SDK_INT >= 28) {//9.0+
+                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
+                    Log.i(TAG, "getMEID meid: READ_PHONE_STATE" );
+                    ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_PHONE_STATE}, 1567);
+                } else {
+                    serial = Build.getSerial();
+                }
+            } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
+                serial = Build.SERIAL;
+            } else {//8.0-
+                Class<?> c = Class.forName("android.os.SystemProperties");
+                Method get = c.getMethod("get", String.class);
+                serial = (String) get.invoke(c, "ro.serialno");
+            }
+        } catch (Exception e) {
+            Log.e("e", "读取设备序列号异常:" + e.toString());
+        }
+        return serial;
+    }
+
+    public static  String getIMEI(Context context) {
+        String deviceId = "";
+        TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        if (null != tm) {
+            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
+                Log.i(TAG, "getMEID meid: READ_PHONE_STATE" );
+                ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_PHONE_STATE}, 1567);
+            } else {
+                if (tm.getDeviceId() != null) {
+                    deviceId = tm.getDeviceId();
+                } else {
+                    deviceId = Settings.Secure.getString(context.getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
+                }
+            }
+            Log.d("deviceId--->", deviceId);
+        }
+        return "";
+    }
+
+    public static  String getMEID() {
+        try {
+            Class clazz = Class.forName("android.os.SystemProperties");
+            Method method = clazz.getMethod("get", String.class, String.class);
+
+            String meid = (String) method.invoke(null, "ril.cdma.meid", "");
+            if (!TextUtils.isEmpty(meid)) {
+                Log.d(TAG, "getMEID meid: " + meid);
+                return meid;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            Log.w(TAG, "getMEID error : " + e.getMessage());
+        }
+        return "";
+    }
+}
diff --git a/app/src/main/java/com/duqing/missions/util/MyLog.java b/app/src/main/java/com/duqing/missions/util/MyLog.java
new file mode 100644
index 0000000..521c63f
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/util/MyLog.java
@@ -0,0 +1,42 @@
+package com.duqing.missions.util;
+
+import android.util.Log;
+
+import com.duqing.missions.BuildConfig;
+
+
+/**
+ * My father is Object, ites purpose of
+ *
+ * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-4-5.
+ */
+public class MyLog {
+
+
+    public static void i(String tag,String msg){
+        if(BuildConfig.DEBUG) {
+            Log.i(tag, BuildConfig.BUILD_TYPE +" "+ msg);
+        }
+    }
+
+    public static void e(String tag,String msg){
+        if(BuildConfig.DEBUG) {
+            Log.e(tag, msg);
+        }
+    }
+
+
+    public static void d(String tag,String msg){
+        if(BuildConfig.DEBUG) {
+            Log.d(tag, msg);
+        }
+    }
+
+
+    public static void v(String tag,String msg){
+        if(BuildConfig.DEBUG) {
+            Log.v(tag, msg);
+        }
+    }
+
+}
diff --git a/app/src/main/java/com/duqing/missions/util/ResPonse.java b/app/src/main/java/com/duqing/missions/util/ResPonse.java
new file mode 100644
index 0000000..e7e53b5
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/util/ResPonse.java
@@ -0,0 +1,15 @@
+package com.duqing.missions.util;
+
+import java.io.Serializable;
+
+/**
+ * Created by Administrator on 2018/5/25.
+ */
+
+public abstract class ResPonse<T> implements Serializable {
+
+    public abstract void doSuccess(T obj);
+
+    public  void doError(T obj){};
+
+}
diff --git a/app/src/main/java/com/duqing/missions/util/SpUtils.java b/app/src/main/java/com/duqing/missions/util/SpUtils.java
new file mode 100644
index 0000000..f3fd2fd
--- /dev/null
+++ b/app/src/main/java/com/duqing/missions/util/SpUtils.java
@@ -0,0 +1,476 @@
+package com.duqing.missions.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.collection.ArraySet;
+
+import java.util.Set;
+
+/**
+ * Created by Administrator on 2021/10/28 0028.
+ */
+public class SpUtils {
+
+    static SpUtils instance;
+    
+    /**
+     * 获取SP实例
+     *
+     * @return {@link SpUtils}
+     */
+    public static SpUtils getInstance() {
+        if(instance == null){
+            instance = new SpUtils();
+        }
+        return instance;
+    }
+
+    
+    public final String PROJECT = "project";
+    public final String USER = "user";
+
+
+    public boolean clearData(Context context, String keyShared){
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        return settings.edit().clear().commit();
+    }
+
+
+    public 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 Set getStringSet(Context context, String key, Set defaultValue, String keyShared) {
+        SharedPreferences settings = context.getSharedPreferences(keyShared, Context.MODE_PRIVATE);
+        return settings.getStringSet(key, defaultValue);
+    }
+
+    public boolean getBooleanValOfUser(Context context, String key){
+        return getBoolean(context,key,false,USER);
+    }
+
+    public boolean getBooleanValOfProject(Context context, String key){
+        return getBoolean(context,key,false,PROJECT);
+    }
+
+    public String getStringValOfUser(Context context, String key){
+        return getString(context,key,"",USER);
+    }
+
+    public String getStringValOfProject(Context context, String key){
+        return getString(context,key,"",PROJECT);
+    }
+
+    public int getIntValOfProject(Context context, String key){
+        return getInt(context,key,0,PROJECT);
+    }
+
+    public Long getLongValOfProject(Context context, String key){
+        return getLong(context,key,0,PROJECT);
+    }
+
+    public float getFloatValOfProject(Context context, String key){
+        return getFloat(context,key,0,PROJECT);
+    }
+
+    public Set getStringSetValOfProject(Context context, String key){
+        return getStringSet(context,key,PROJECT);
+    }
+
+    public int getIntValOfUser(Context context, String key){
+        return getInt(context,key,0,USER);
+    }
+
+    public Long getLongValOfUser(Context context, String key){
+        return getLong(context,key,0,USER);
+    }
+
+    public float getFloatValOfUser(Context context, String key){
+        return getFloat(context,key,0,USER);
+    }
+
+    public Set getStringSetValOfUser(Context context, String key){
+        return getStringSet(context,key,USER);
+    }
+
+
+    public void putBooleanValOfUser(Context context, String key ,Boolean value){
+        putBoolean(context,key,value,USER);
+    }
+
+    public void putBooleanValOfProject(Context context, String key,Boolean value){
+        putBoolean(context,key,value,PROJECT);
+    }
+
+    public void putStringValOfUser(Context context,String key,String value){
+        putString(context,key,value,USER);
+    }
+
+    public void putStringValOfProject(Context context,String key,String value){
+        putString(context,key,value,PROJECT);
+    }
+
+    public void putIntValOfProject(Context context,String key,int value){
+        putInt(context,key,value,PROJECT);
+    }
+
+    public void putLongValOfProject(Context context,String key,long value){
+        putLong(context,key,value,PROJECT);
+    }
+
+    public void putFloatValOfProject(Context context,String key,float value){
+        putFloat(context,key,value,PROJECT);
+    }
+
+    public void putStringSetValOfProject(Context context,String key,Set value){
+        putStringSet(context,key,value,PROJECT);
+    }
+
+    public void putIntValOfUser(Context context,String key,int value){
+        putInt(context,key,value,USER);
+    }
+
+    public void putLongValOfUser(Context context,String key,long value){
+        putLong(context,key,value,USER);
+    }
+
+    public void putFloatValOfUser(Context context,String key,float value){
+        putFloat(context,key,value,USER);
+    }
+
+    public void putStringSetValOfUser(Context context,String key, Set value){
+        putStringSet(context,key,value,USER);
+    }
+
+
+    public void removeUserKey(Context context, String key){
+        removeKey(context,key,USER);
+    }
+
+    public void removeProjectKey(Context context, String key){
+        removeKey(context,key,PROJECT);
+    }
+
+    public void removeUserValue(Context context, String Value){
+        removeValue(context,Value,USER);
+    }
+
+    public void removeProjectValue(Context context, String Value){
+        removeValue(context,Value,PROJECT);
+    }
+
+
+    public void clearProjectData(Context context){
+        clearData(context,PROJECT);
+    }
+    public void clearUserData(Context context){
+        clearData(context,USER);
+    }
+
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 035ff13..177d7d2 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,34 +1,27 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/container"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <com.google.android.material.bottomnavigation.BottomNavigationView
-        android:id="@+id/nav_view"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="0dp"
-        android:layout_marginEnd="0dp"
-        android:background="?android:attr/windowBackground"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:menu="@menu/bottom_nav_menu"/>
+    android:layout_height="match_parent"
+    android:orientation="vertical">
 
     <fragment
         android:id="@+id/nav_host_fragment_activity_main"
         android:name="androidx.navigation.fragment.NavHostFragment"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
+        android:layout_weight="1"
         app:defaultNavHost="true"
-        app:layout_constraintBottom_toTopOf="@id/nav_view"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
         app:navGraph="@navigation/mobile_navigation"
     />
 
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+    <com.google.android.material.bottomnavigation.BottomNavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/windowBackground"
+        app:menu="@menu/bottom_nav_menu"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index c55e1b8..f1fff12 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -5,7 +5,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    tools:context=".ui.dashboard.DashboardFragment" >
+    tools:context=".ui.main.dashboard.DashboardFragment" >
 
     <com.google.android.material.tabs.TabLayout
         android:id="@+id/tablayout"
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 47973db..6ed5bfb 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<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"
     tools:ignore="MissingConstraints"
-    tools:context=".ui.home.HomeFragment"
+    tools:context=".ui.main.home.HomeFragment"
     android:layout_width="match_parent"
     android:orientation="vertical"
     android:layout_height="match_parent">
@@ -12,11 +12,17 @@
         android:id="@+id/img_search"
         android:layout_width="39dp"
         android:layout_height="35dp"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/smart_refresh"
         android:src="@android:drawable/ic_search_category_default"/>
     <com.scwang.smart.refresh.layout.SmartRefreshLayout
         android:id="@+id/smart_refresh"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintTop_toBottomOf="@id/img_search"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
         >
 
         <androidx.constraintlayout.widget.ConstraintLayout
@@ -28,6 +34,7 @@
                 android:id="@+id/recycler_top"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:overScrollMode="never"
                 app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
                 app:spanCount="3"
                 tools:itemCount="3"
@@ -38,6 +45,7 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="10dp"
+                android:overScrollMode="never"
                 tools:listitem="@layout/home_item_classify"
                 app:layout_constraintTop_toBottomOf="@id/recycler_top"
                 app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
@@ -61,6 +69,7 @@
                 android:id="@+id/recycler_recommend"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:overScrollMode="never"
                 tools:listitem="@layout/homt_item_recommend"
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintTop_toBottomOf="@id/text_recommend"
@@ -69,4 +78,4 @@
         </androidx.constraintlayout.widget.ConstraintLayout>
     </com.scwang.smart.refresh.layout.SmartRefreshLayout>
 
-</LinearLayout>
\ No newline at end of file
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/fragment_notifications.xml
index 47368bf..734378a 100644
--- a/app/src/main/res/layout/fragment_notifications.xml
+++ b/app/src/main/res/layout/fragment_notifications.xml
@@ -6,7 +6,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white_2"
-    tools:context=".ui.notifications.NotificationsFragment">
+    tools:context=".ui.main.notifications.NotificationsFragment">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="match_parent"
diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml
index 567dacb..ebb9ec8 100644
--- a/app/src/main/res/navigation/mobile_navigation.xml
+++ b/app/src/main/res/navigation/mobile_navigation.xml
@@ -7,19 +7,19 @@
 
     <fragment
         android:id="@+id/navigation_home"
-        android:name="com.duqing.missions.ui.home.HomeFragment"
+        android:name="com.duqing.missions.ui.main.home.HomeFragment"
         android:label="@string/title_home"
         tools:layout="@layout/fragment_home" />
 
     <fragment
         android:id="@+id/navigation_dashboard"
-        android:name="com.duqing.missions.ui.dashboard.DashboardFragment"
+        android:name="com.duqing.missions.ui.main.dashboard.DashboardFragment"
         android:label="@string/title_dashboard"
         tools:layout="@layout/fragment_dashboard" />
 
     <fragment
         android:id="@+id/navigation_notifications"
-        android:name="com.duqing.missions.ui.notifications.NotificationsFragment"
+        android:name="com.duqing.missions.ui.main.notifications.NotificationsFragment"
         android:label="@string/title_notifications"
         tools:layout="@layout/fragment_notifications" />
 </navigation>
\ No newline at end of file

--
Gitblit v1.9.1