Administrator
2021-10-28 9fbcdc146440b49ef80714c1336c226e00727da8
其他部分框架方法及文件新增
18 files added
3 files renamed
3 files deleted
9 files modified
2145 ■■■■■ changed files
app/build.gradle 10 ●●●●● patch | view | raw | blame | history
app/src/main/AndroidManifest.xml 1 ●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/MainActivity.java 2 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/MyApplication.java 136 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/base/BaseActivity.java 404 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/base/BaseAdapter.java 154 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/base/BaseFragment.java 52 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/common/NullViewHolder.java 18 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/data/ApkUpGradeResult.java 29 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/data/BaseApiResult.java 23 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/data/BasePageResult.java 21 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/source/HttpPageSource.java 16 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/dashboard/DashboardViewModel.java 19 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/home/HomeFragment.java 60 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/home/HomeViewModel.java 19 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/Text.java 7 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java 4 ●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java 113 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java 47 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/home/adapter/MissiontTopAdapter.java 18 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/home/model/MissionDesc.java 12 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/home/model/TopItemViewModel.java 11 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java 2 ●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsViewModel.java 2 ●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/util/DeviceUtil.java 374 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/util/MyLog.java 42 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/util/ResPonse.java 15 ●●●●● patch | view | raw | blame | history
app/src/main/java/com/duqing/missions/util/SpUtils.java 476 ●●●●● patch | view | raw | blame | history
app/src/main/res/layout/activity_main.xml 31 ●●●●● patch | view | raw | blame | history
app/src/main/res/layout/fragment_dashboard.xml 2 ●●● patch | view | raw | blame | history
app/src/main/res/layout/fragment_home.xml 17 ●●●● patch | view | raw | blame | history
app/src/main/res/layout/fragment_notifications.xml 2 ●●● patch | view | raw | blame | history
app/src/main/res/navigation/mobile_navigation.xml 6 ●●●● patch | view | raw | blame | history
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'    //权限依赖
}
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"
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(
app/src/main/java/com/duqing/missions/MyApplication.java
New file
@@ -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;
    }
}
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;
    }
}
app/src/main/java/com/duqing/missions/base/BaseAdapter.java
New file
@@ -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;
        }
    }
}
app/src/main/java/com/duqing/missions/base/BaseFragment.java
New file
@@ -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;
    }
}
app/src/main/java/com/duqing/missions/common/NullViewHolder.java
New file
@@ -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;
    }
}
app/src/main/java/com/duqing/missions/data/ApkUpGradeResult.java
New file
@@ -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 + "," +
                    '}';
        }
    }
}
app/src/main/java/com/duqing/missions/data/BaseApiResult.java
New file
@@ -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 +
                '}';
    }
}
app/src/main/java/com/duqing/missions/data/BasePageResult.java
New file
@@ -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 +
                '}';
    }
}
app/src/main/java/com/duqing/missions/source/HttpPageSource.java
New file
@@ -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);
}
app/src/main/java/com/duqing/missions/ui/dashboard/DashboardViewModel.java
File was deleted
app/src/main/java/com/duqing/missions/ui/home/HomeFragment.java
File was deleted
app/src/main/java/com/duqing/missions/ui/home/HomeViewModel.java
File was deleted
app/src/main/java/com/duqing/missions/ui/main/Text.java
New file
@@ -0,0 +1,7 @@
package com.duqing.missions.ui.main;
/**
 * Created by Administrator on 2021/10/28 0028.
 */
public class Text {
}
app/src/main/java/com/duqing/missions/ui/main/dashboard/DashboardFragment.java
File was renamed from app/src/main/java/com/duqing/missions/ui/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 {
app/src/main/java/com/duqing/missions/ui/main/home/HomeFragment.java
New file
@@ -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");
    }
}
app/src/main/java/com/duqing/missions/ui/main/home/HomeViewModel.java
New file
@@ -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);
    }
}
app/src/main/java/com/duqing/missions/ui/main/home/adapter/MissiontTopAdapter.java
New file
@@ -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) {
    }
}
app/src/main/java/com/duqing/missions/ui/main/home/model/MissionDesc.java
New file
@@ -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;
}
app/src/main/java/com/duqing/missions/ui/main/home/model/TopItemViewModel.java
New file
@@ -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 {
}
app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsFragment.java
File was renamed from app/src/main/java/com/duqing/missions/ui/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;
app/src/main/java/com/duqing/missions/ui/main/notifications/NotificationsViewModel.java
File was renamed from app/src/main/java/com/duqing/missions/ui/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;
app/src/main/java/com/duqing/missions/util/DeviceUtil.java
New file
@@ -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 "";
    }
}
app/src/main/java/com/duqing/missions/util/MyLog.java
New file
@@ -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);
        }
    }
}
app/src/main/java/com/duqing/missions/util/ResPonse.java
New file
@@ -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){};
}
app/src/main/java/com/duqing/missions/util/SpUtils.java
New file
@@ -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);
    }
}
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>
    <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>
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"
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>
</androidx.constraintlayout.widget.ConstraintLayout>
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"
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>