commit 10f7fbbd66a586532d039a37a75ad5da0b3e089a Author: fyf Date: Mon Jun 19 15:38:09 2023 +0800 第一次提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..2b75303a --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..fb7f4a8a --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 00000000..a2d7c213 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..eb2873e7 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..31622f1e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..e262835a --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ + +# Framework_Android +Android架构搭建,根据腾讯QMUI_Android框架搭建,适用于快速运用到项目中去。 +由于本项目与自己的后台服务器搭配使用,在使用时请改成自己的服务器IP地址。 + +感谢QMUI_Android团队为广大开发者提供的 UI 库 ,官网:[http://qmuiteam.com/android](http://qmuiteam.com/android) + + +### (随缘更新) + +### 开发日志 —— 2 + - 添加并重写OkGo中的JsonCallback,添加JsonConvert类,对后台baseJson进行统一管理。 + 每个项目请求后台返回的json格式都有所不同,**请根据实际项目需求修改JsonConvert类**。 本项目同时提供了两种json格式进行解析操作: + 1. json格式为`{code:0,msg:"成功",result:{…}}`。 + 根据后台返回的`code`数值来判断返回的数据是否成功。`code:0`表示成功返回,`code:101` 等其他标识表示返回失败,并返回失败的`msg`。 + 2. json格式为`{success:true,msg:"成功",result:{…}}`。 + 根据后台返回的`success`数值来判断返回的数据是否成功。`success:true`表示成功返回,`success:false` 表示返回失败,并返回失败的`msg`。 + +- 实现启动和登录功能,在app启动时通过后台服务器判断token是否失效,如果没有token或者失效,则跳转到登录页面,反之直接跳转到主界面。 +- 添加测试用户信息开关,并且在gradle配置只能在debug版本使用,当编译成release正式版时,关闭测试用户信息开关。 +- 添加SPConstants,统一管理SharedPreferences的key。 + +### 开发日志 —— 1 + +- 根据QMUI搭建viewPager+fragment+Navigation主页,添加Android7.0 provider +- 添加登录界面,界面根据软键盘弹出自适应上下移动,添加文件相关工具类 +- 添加OkGo并配置框架,添加Logger框架,添加内存泄漏检测框架,base类添加EasyPermission权限管理框架。 + +### 项目截图(持续更新) +![登录界面](https://raw.githubusercontent.com/FadedYu/Framework_Android/master/img_folder/im_login.png) +![主页](https://raw.githubusercontent.com/FadedYu/Framework_Android/master/img_folder/im_pager1.png) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 00000000..becfa464 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,90 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + + defaultConfig { + applicationId "com.bonait.bnframework" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode rootProject.ext.versionCode + versionName rootProject.ext.versionName + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + + resValue("bool","superAdminTest","false") + } + + debug { + resValue("string", "PORT_NUMBER", "8081") + resValue("bool","superAdminTest","true") + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + + //测试相关 + testImplementation 'junit:junit:4.13-beta-2' + androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + + //支持包 + implementation 'com.android.support:design:28.0.0' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation "com.android.support:recyclerview-v7:$rootProject.supportVersion" + implementation "com.android.support:design:$rootProject.supportVersion" + implementation "com.android.support:cardview-v7:$rootProject.supportVersion" + implementation "com.android.support:support-vector-drawable:$rootProject.supportVersion" + + // QMUI框架 link: http://qmuiteam.com/android + // 本App 搭建的基础框架,基本使用控件功能请看官网的功能列表 + implementation 'com.qmuiteam:qmui:1.2.0' + implementation 'com.qmuiteam:arch:0.3.1' + + // RecyclerAdapter框架 + implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.46' + + // OKGO网络协议封装框架 + implementation 'com.lzy.net:okgo:3.0.4' + implementation 'com.google.code.gson:gson:2.8.0' + + // easyPermissions权限管理 + implementation 'pub.devrel:easypermissions:2.0.1' + + // butterKnife黄油刀 + implementation "com.jakewharton:butterknife:$rootProject.butterknife" + annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.butterknife" + + // SuperTextView + implementation 'com.github.lygttpod:SuperTextView:2.1.8' + + // android-saripaar 基于规则的Android表单验证库 + implementation 'com.mobsandgeeks:android-saripaar:2.0.3' + + // litePal 数据库操作框架 + implementation 'org.litepal.android:java:3.0.0' + + // SmartShow Toast框架,解决不同机型弹出方式缺陷问题 + implementation 'com.github.the-pig-of-jungle.smart-show:toast:2.6.7' + + // debug调试app本地数据库 + debugImplementation 'com.amitshekhar.android:debug-db:1.0.6' + + // log日志框架 + implementation 'com.orhanobut:logger:2.2.0' + + // leak 内存泄漏检测 + debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' + releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3' + // Optional, if you use support library fragments: + debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' + implementation files('libs/commons-codec-1.6.jar') +} diff --git a/app/libs/commons-codec-1.6.jar b/app/libs/commons-codec-1.6.jar new file mode 100644 index 00000000..ee1bc49a Binary files /dev/null and b/app/libs/commons-codec-1.6.jar differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..95bb45a2 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/litepal.xml b/app/src/main/assets/litepal.xml new file mode 100644 index 00000000..e416801b --- /dev/null +++ b/app/src/main/assets/litepal.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/bonait/bnframework/MainApplication.java b/app/src/main/java/com/bonait/bnframework/MainApplication.java new file mode 100644 index 00000000..653049cb --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/MainApplication.java @@ -0,0 +1,151 @@ +package com.bonait.bnframework; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Application; +import android.content.Context; + +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.notification.MainNotification; +import com.bonait.bnframework.common.utils.AppUtils; +import com.bonait.bnframework.common.utils.PreferenceUtils; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.cache.CacheEntity; +import com.lzy.okgo.cache.CacheMode; +import com.lzy.okgo.interceptor.HttpLoggingInterceptor; +import com.orhanobut.logger.AndroidLogAdapter; +import com.orhanobut.logger.FormatStrategy; +import com.orhanobut.logger.Logger; +import com.orhanobut.logger.PrettyFormatStrategy; +import com.qmuiteam.qmui.arch.QMUISwipeBackActivityManager; +import com.squareup.leakcanary.LeakCanary; + +import org.litepal.LitePal; + +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +import okhttp3.OkHttpClient; + +/** + * Created by LY on 2019/3/19. + */ +public class MainApplication extends Application { + + @SuppressLint("StaticFieldLeak") + private static Context context; + + public static Context getContext() { + return context; + } + + @Override + public void onCreate() { + super.onCreate(); + context = getApplicationContext(); + + // activity生命周期管理 + ActivityLifecycleManager.get().init(this); + + // QMUI 框架初始化 + QMUISwipeBackActivityManager.init(this); + + // LitePal 数据库初始化 + LitePal.initialize(this); + + // 全局配置OkGo + initOkGo(); + + // 配置sharedPreferences + PreferenceUtils.initPreference(this, AppUtils.getAppName(this), Activity.MODE_PRIVATE); + + // 初始化通知栏消息渠道 + MainNotification.initNotificationChannel(this); + + // 内存泄漏检测 + initLeakCanary(false); + + // Log日志打印框架 + initLogCat(); + + // SmartShow Toast框架,暂时不用,先使用ToastUtils工具类的 + //SmartShow.init(this); + } + + //========================================================================// + + /** + * 内存泄漏检测,根据flag来判断要不要初始化 + */ + private void initLeakCanary(boolean flag) { + if (flag) { + // leak 内存检测注册 + if (LeakCanary.isInAnalyzerProcess(this)) { + return; + } + LeakCanary.install(this); + } + } + + /** + * 初始化log日志框架 + */ + private void initLogCat() { + FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder() + .showThreadInfo(false) // (可选)是否显示线程信息。 默认值为true + .methodCount(2) // (可选)要显示的方法行数。 默认2 + .build(); + Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) { + @Override + public boolean isLoggable(int priority, String tag) { + return BuildConfig.DEBUG; + } + }); + } + + /** + * 配置OkGo + */ + private void initOkGo() { + + //---------这里给出的是示例代码,告诉你可以这么传,实际使用的时候,根据需要传,不需要就不传-------------// + /*HttpHeaders headers = new HttpHeaders(); + headers.put("commonHeaderKey1", "commonHeaderValue1"); //header不支持中文,不允许有特殊字符 + headers.put("commonHeaderKey2", "commonHeaderValue2"); + HttpParams params = new HttpParams(); + params.put("commonParamsKey1", "commonParamsValue1"); //param支持中文,直接传,不要自己编码 + params.put("commonParamsKey2", "这里支持中文参数");*/ + //----------------------------------------------------------------------------------------// + + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + + //log相关 + HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor("OkGo"); + //log打印级别,决定了log显示的详细程度 + loggingInterceptor.setPrintLevel(HttpLoggingInterceptor.Level.BODY); + //log颜色级别,决定了log在控制台显示的颜色 + loggingInterceptor.setColorLevel(Level.INFO); + //添加OkGo默认debug日志 + builder.addInterceptor(loggingInterceptor); + + //-------------------------配置超时时间,默认60000ms,60s------------------------------// + //OkGo.DEFAULT_MILLISECONDS + //全局的连接超时时间 + builder.connectTimeout(Constants.CONNECT_TIME_OUT, TimeUnit.MILLISECONDS); + //全局的读取超时时间 + builder.readTimeout(Constants.CONNECT_TIME_OUT, TimeUnit.MILLISECONDS); + //全局的写入超时时间 + builder.writeTimeout(OkGo.DEFAULT_MILLISECONDS, TimeUnit.MILLISECONDS); + + OkGo.getInstance().init(this) //必须调用初始化 + .setOkHttpClient(builder.build()) //建议设置OkHttpClient,不设置将使用默认的 + .setCacheMode(CacheMode.NO_CACHE) //全局统一缓存模式,默认不使用缓存,可以不传 + .setCacheTime(CacheEntity.CACHE_NEVER_EXPIRE) //全局统一缓存时间,默认永不过期,可以不传 + .setRetryCount(3); //全局统一超时重连次数,默认为三次,那么最差的情况会请求4次(一次原始请求,三次重连请求),不需要可以设置为0 + //.addCommonParams(params); //全局公共参数 + + } + + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/base/BaseActivity.java b/app/src/main/java/com/bonait/bnframework/common/base/BaseActivity.java new file mode 100644 index 00000000..dd18c11b --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/base/BaseActivity.java @@ -0,0 +1,86 @@ +package com.bonait.bnframework.common.base; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.net.Uri; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.bonait.bnframework.MainApplication; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.utils.AlertDialogUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.qmuiteam.qmui.arch.QMUIActivity; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import java.util.List; + +import pub.devrel.easypermissions.EasyPermissions; + + +/** + * Created by LY on 2019/3/21. + */ +@SuppressLint("Registered") +public class BaseActivity extends QMUIActivity implements EasyPermissions.PermissionCallbacks { + + @Override + protected int backViewInitOffset() { + return QMUIDisplayHelper.dp2px(MainApplication.getContext(), 100); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == Constants.APP_SETTING_DIALOG_REQUEST_CODE) { + //在这儿,你可以再对权限进行检查,从而给出提示,或进行下一步操作 + checkPermission(); + ToastUtils.info("从设置中返回"); + } + } + + //----------------以下是请求权限base,子类activity只需要重写checkPermission()方法即可----------------// + + /** + * 检查权限,子类要申请权限,需要重写该方法 + * */ + public void checkPermission() { + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // 将结果转发给EasyPermissions + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); + } + + @Override + public void onPermissionsGranted(int requestCode, @NonNull List perms) { + } + + @Override + public void onPermissionsDenied(int requestCode, @NonNull List perms) { + + //若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不再提示',且拒绝权限。 + //跳转到设置界面去,让用户手动开启。 + if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { + + String title = "需要权限,才能正常使用:"; + String message = "如果没有请求的权限,此应用可能无法正常工作。请打开应用设置以修改应用权限。"; + AlertDialogUtils.showDialog(this, title, message, "去设置", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", MainApplication.getContext().getPackageName(), null); + intent.setData(uri); + startActivityForResult(intent, Constants.APP_SETTING_DIALOG_REQUEST_CODE); + dialog.dismiss(); + } + }); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/base/BaseFragment.java b/app/src/main/java/com/bonait/bnframework/common/base/BaseFragment.java new file mode 100644 index 00000000..4ed51f0e --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/base/BaseFragment.java @@ -0,0 +1,87 @@ +package com.bonait.bnframework.common.base; + +import android.content.Intent; +import android.net.Uri; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.bonait.bnframework.MainApplication; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.utils.AlertDialogUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.qmuiteam.qmui.arch.QMUIFragment; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import java.util.List; + +import pub.devrel.easypermissions.EasyPermissions; + +/** + * Created by LY on 2019/3/25. + */ +public abstract class BaseFragment extends QMUIFragment implements EasyPermissions.PermissionCallbacks { + + public BaseFragment() { + } + + @Override + protected int backViewInitOffset() { + return QMUIDisplayHelper.dp2px(requireContext(), 100); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == Constants.APP_SETTING_DIALOG_REQUEST_CODE) { + //在这儿,你可以再对权限进行检查,从而给出提示,或进行下一步操作 + checkPermission(); + ToastUtils.info("从设置中返回"); + } + } + + //----------------以下是请求权限base,子类activity只需要重写checkPermission()方法即可----------------// + + /** + * 检查权限,子类要申请权限,需要重写该方法 + * */ + public void checkPermission() { + } + + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // 将结果转发给EasyPermissions + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); + } + + @Override + public void onPermissionsGranted(int requestCode, @NonNull List perms) { + } + + @Override + public void onPermissionsDenied(int requestCode, @NonNull List perms) { + + //若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不再提示',且拒绝权限。 + //跳转到设置界面去,让用户手动开启。 + if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { + + String title = "需要权限,才能正常使用:"; + String message = "如果没有请求的权限,此应用可能无法正常工作。请打开应用设置以修改应用权限。"; + AlertDialogUtils.showDialog(getContext(), title, message, "去设置", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + Uri uri = Uri.fromParts("package", MainApplication.getContext().getPackageName(), null); + intent.setData(uri); + startActivityForResult(intent, Constants.APP_SETTING_DIALOG_REQUEST_CODE); + dialog.dismiss(); + } + }); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/base/BaseFragmentActivity.java b/app/src/main/java/com/bonait/bnframework/common/base/BaseFragmentActivity.java new file mode 100644 index 00000000..09387161 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/base/BaseFragmentActivity.java @@ -0,0 +1,9 @@ +package com.bonait.bnframework.common.base; + +import com.qmuiteam.qmui.arch.QMUIFragmentActivity; + +/** + * Created by LY on 2019/3/27. + */ +public abstract class BaseFragmentActivity extends QMUIFragmentActivity { +} diff --git a/app/src/main/java/com/bonait/bnframework/common/constant/Constants.java b/app/src/main/java/com/bonait/bnframework/common/constant/Constants.java new file mode 100644 index 00000000..93c84309 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/constant/Constants.java @@ -0,0 +1,31 @@ +package com.bonait.bnframework.common.constant; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.MainApplication; + +/** + * Created by LY on 2019/3/21. + */ +public interface Constants { + + String SERVICE_IP = "http://192.168.22.98:23104/GKIA"; + String APP_TOKEN = "appToken"; + + // admin测试数据使用 + boolean SKIP_TO_TEST_ACTIVITY = false; // 是否启用TestActivity + boolean superAdminTest = MainApplication.getContext().getResources().getBoolean(R.bool.superAdminTest); + + // OkGo 连接超时时间,毫秒ms + long CONNECT_TIME_OUT = 6000; + + // 申请权限跳转到设置界面code + int APP_SETTING_DIALOG_REQUEST_CODE = 1; + + // 申请权限code + int ALL_PERMISSION = 100; + + // 更新apk + int UPDATE_APP = 102; + int INSTALL_PERMISSION_CODE = 103; + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/constant/SPConstants.java b/app/src/main/java/com/bonait/bnframework/common/constant/SPConstants.java new file mode 100644 index 00000000..f83bdc34 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/constant/SPConstants.java @@ -0,0 +1,56 @@ +package com.bonait.bnframework.common.constant; + +/** + * Created by LY on 2019/4/4. + * 保存到SharedPreferences的常量名字 + * 将SharedPreferences的key统一管理 + */ +public interface SPConstants { + + /* + * 将SharedPreferences的key统一管理 + * + * */ + + /** + * App token + * */ + String TOKEN = "token"; + + /** + * 是否修改密码,Boolean + * */ + String CHANGE_PWD = "changePwd"; + + /** + * 用户账号 + * */ + String USER_NAME = "username"; + + /** + * 用户密码 + * */ + String PASSWORD = "password"; + + /** + * 用户名字 + * */ + String USER = "user"; + + /** + * 用户ID + * */ + String USER_ID = "userId"; + + /** + * 角色 + * */ + String ROLE_NAMES = "roleNames"; + + /** + * 部门 + * */ + String FIRST_DEP_ID = "firstDepId"; + + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/GsonConvert.java b/app/src/main/java/com/bonait/bnframework/common/http/GsonConvert.java new file mode 100644 index 00000000..9665ba8c --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/GsonConvert.java @@ -0,0 +1,52 @@ +package com.bonait.bnframework.common.http; + +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.stream.JsonReader; + +import java.io.Reader; +import java.lang.reflect.Type; + +/** + * Created by LY on 2019/4/1. + * Gson 转换 + */ +public class GsonConvert { + + private static Gson create() { + return GsonConvert.GsonHolder.gson; + } + + private static class GsonHolder { + private static Gson gson = new Gson(); + } + + public static T fromJson(String json, Class type) throws JsonIOException, JsonSyntaxException { + return create().fromJson(json, type); + } + + public static T fromJson(String json, Type type) { + return create().fromJson(json, type); + } + + public static T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException { + return create().fromJson(reader, typeOfT); + } + + public static T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException { + return create().fromJson(json, classOfT); + } + + public static T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException { + return create().fromJson(json, typeOfT); + } + + public static String toJson(Object src) { + return create().toJson(src); + } + + public static String toJson(Object src, Type typeOfSrc) { + return create().toJson(src, typeOfSrc); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/bitmap/BitmapDialogCallback.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/bitmap/BitmapDialogCallback.java new file mode 100644 index 00000000..deddfe6c --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/bitmap/BitmapDialogCallback.java @@ -0,0 +1,44 @@ +package com.bonait.bnframework.common.http.callback.bitmap; + +import android.content.Context; +import android.graphics.Bitmap; + +import com.lzy.okgo.callback.BitmapCallback; +import com.lzy.okgo.request.base.Request; +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog; + +/** + * Created by LY on 2019/4/2. + * + * 如果请求的数据是图片,则可以使用该回调,回调的图片进行了压缩处理,确保不发生OOM + * + * 有加载框的网络图片请求回调 + * + */ +public abstract class BitmapDialogCallback extends BitmapCallback { + + private QMUITipDialog tipDialog; + + + public BitmapDialogCallback(Context context) { + super(1000,1000); + tipDialog = new QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord("正在加载") + .create(); + } + + @Override + public void onStart(Request request) { + if (tipDialog != null && !tipDialog.isShowing()) { + tipDialog.show(); + } + } + + @Override + public void onFinish() { + if (tipDialog != null && tipDialog.isShowing()) { + tipDialog.dismiss(); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/files/FileProgressDialogCallBack.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/files/FileProgressDialogCallBack.java new file mode 100644 index 00000000..51fa957a --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/files/FileProgressDialogCallBack.java @@ -0,0 +1,145 @@ +package com.bonait.bnframework.common.http.callback.files; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.text.format.Formatter; +import android.util.Log; + +import com.bonait.bnframework.MainApplication; +import com.bonait.bnframework.common.notification.DownloadNotification; +import com.bonait.bnframework.common.utils.AlertDialogUtils; +import com.bonait.bnframework.common.utils.NetworkUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.callback.FileCallback; +import com.lzy.okgo.model.Progress; +import com.lzy.okgo.model.Response; +import com.lzy.okgo.request.base.Request; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import java.io.File; +import java.util.Locale; + +/** + * Created by LY on 2019/4/15. + * 带加载进度条的下载回调接口,默认OkGo文件回调 + */ +public abstract class FileProgressDialogCallBack extends FileCallback { + + private ProgressDialog progressDialog; + private DownloadNotification downloadNotification; + + public FileProgressDialogCallBack(Context context) { + super(null); + initDialog(context); + } + + public FileProgressDialogCallBack(Context context, String destFileName) { + super(destFileName); + initDialog(context); + } + + public FileProgressDialogCallBack(Context context, String destFileDir, String destFileName) { + super(destFileDir, destFileName); + initDialog(context); + } + + + @Override + public void onStart(Request request) { + if (progressDialog != null && !progressDialog.isShowing()) { + progressDialog.show(); + } + } + + @Override + public void onFinish() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + + if (downloadNotification != null) { + downloadNotification.downloadComplete(); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + if (downloadNotification != null) { + downloadNotification.downloadError(); + } + + ToastUtils.error(response.body().getName() + "下载失败,请重新下载!"); + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } + + @Override + public void downloadProgress(Progress progress) { + String downloadLength = Formatter.formatFileSize(MainApplication.getContext(), progress.currentSize); + String totalLength = Formatter.formatFileSize(MainApplication.getContext(), progress.totalSize); + + if (progress.totalSize > 0) { + progressDialog.setMax((int) progress.totalSize); + } + progressDialog.setProgress((int) (progress.fraction * progress.totalSize)); + progressDialog.setProgressNumberFormat(String.format(Locale.CHINA, "%s / %s", downloadLength, totalLength)); + Log.d("downloadProgress", progress.fileName + " 下载中……" + String.format(Locale.CHINA, "%s / %s", downloadLength, totalLength)); + + if (downloadNotification != null) { + downloadNotification.showProgress(progress); + } + } + + /** + * 判断网络状态。移动/WiFi,弹出警告框 + */ + private void initDialog(final Context context) { + if (NetworkUtils.isActiveNetworkMobile(context)) { + String title = "温馨提示!"; + String message = "当前网络为移动网络,可能会消耗大量移动流量数据,确定要下载吗?"; + AlertDialogUtils.showDialog(context, title, message, new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + downloadingDialog(context); + dialog.dismiss(); + } + }); + } else { + downloadingDialog(context); + } + } + + /** + * 显示进度条对话框 + * */ + private void downloadingDialog(final Context context) { + progressDialog = new ProgressDialog(context); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setCancelable(false); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setTitle("正在下载"); + + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "停止下载", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + OkGo.getInstance().cancelTag(context); + ToastUtils.info("已停止下载!"); + dialog.dismiss(); + } + }); + + progressDialog.setButton(DialogInterface.BUTTON_POSITIVE, "后台下载", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + downloadNotification = new DownloadNotification(context); + dialog.dismiss(); + } + }); + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileCallback2.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileCallback2.java new file mode 100644 index 00000000..a0e30d3f --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileCallback2.java @@ -0,0 +1,40 @@ +package com.bonait.bnframework.common.http.callback.files2; + +import com.lzy.okgo.callback.AbsCallback; + +import java.io.File; + +import okhttp3.Response; + +/** + * Created by LY on 2019/4/10. + *

与OkGo的FileCallback改动不大,如果不想使用该类,可直接使用OkGo默认的FileCallback

+ *

将文件转换类改成FileConvert2

+ * + */ +public abstract class FileCallback2 extends AbsCallback { + + //文件转换类FileConvert2 + private FileConvert2 convert; + + public FileCallback2(long totalSize) { + this(null,totalSize); + } + + public FileCallback2(String destFileName,long totalSize) { + this(null, destFileName,totalSize); + } + + public FileCallback2(String destFileDir, String destFileName,long totalSize) { + // FileConvert2 + convert = new FileConvert2(destFileDir, destFileName,totalSize); + convert.setCallback(this); + } + + @Override + public File convertResponse(Response response) throws Throwable { + File file = convert.convertResponse(response); + response.close(); + return file; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileConvert2.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileConvert2.java new file mode 100644 index 00000000..4b4210c8 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileConvert2.java @@ -0,0 +1,117 @@ +package com.bonait.bnframework.common.http.callback.files2; + +import android.os.Environment; +import android.text.TextUtils; + +import com.lzy.okgo.callback.Callback; +import com.lzy.okgo.convert.Converter; +import com.lzy.okgo.model.Progress; +import com.lzy.okgo.utils.HttpUtils; +import com.lzy.okgo.utils.IOUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * Created by LY on 2019/4/10. + * 与OkGo的FileConvert改动不大,如果不想使用该类,可直接使用OkGo默认的FileConvert + *

与OkGo的FileConvert改动不大

+ *

只是将代码的 progress.totalSize = body.contentLength();

+ *

替换成progress.totalSize = totalSize; 其中totalSize为形参,需要自己传参

+ * + */ +public class FileConvert2 implements Converter { + + public static final String DM_TARGET_FOLDER = File.separator + "download" + File.separator; //下载目标文件夹 + + private String folder; //目标文件存储的文件夹路径 + private String fileName; //目标文件存储的文件名 + private Callback callback; //下载回调 + + /** + * 文件总大小 + * */ + private long totalSize; + + public FileConvert2(long totalSize) { + this(null,totalSize); + this.totalSize = totalSize; + } + + public FileConvert2(String fileName, long totalSize) { + this(Environment.getExternalStorageDirectory() + DM_TARGET_FOLDER, fileName,totalSize); + this.totalSize = totalSize; + } + + public FileConvert2(String folder, String fileName,long totalSize) { + this.folder = folder; + this.fileName = fileName; + this.totalSize = totalSize; + } + + public void setCallback(Callback callback) { + this.callback = callback; + } + + @Override + public File convertResponse(Response response) throws Throwable { + String url = response.request().url().toString(); + if (TextUtils.isEmpty(folder)) folder = Environment.getExternalStorageDirectory() + DM_TARGET_FOLDER; + if (TextUtils.isEmpty(fileName)) fileName = HttpUtils.getNetFileName(response, url); + + File dir = new File(folder); + IOUtils.createFolder(dir); + File file = new File(dir, fileName); + IOUtils.delFileOrFolder(file); + + InputStream bodyStream = null; + byte[] buffer = new byte[8192]; + FileOutputStream fileOutputStream = null; + try { + ResponseBody body = response.body(); + if (body == null) return null; + + bodyStream = body.byteStream(); + Progress progress = new Progress(); + progress.totalSize = totalSize; + progress.fileName = fileName; + progress.filePath = file.getAbsolutePath(); + progress.status = Progress.LOADING; + progress.url = url; + progress.tag = url; + + int len; + fileOutputStream = new FileOutputStream(file); + while ((len = bodyStream.read(buffer)) != -1) { + fileOutputStream.write(buffer, 0, len); + + if (callback == null) continue; + Progress.changeProgress(progress, len, new Progress.Action() { + @Override + public void call(Progress progress) { + onProgress(progress); + } + }); + } + fileOutputStream.flush(); + return file; + } finally { + IOUtils.closeQuietly(bodyStream); + IOUtils.closeQuietly(fileOutputStream); + } + } + + private void onProgress(final Progress progress) { + HttpUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + callback.downloadProgress(progress); //进度回调的方法 + } + }); + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileProgressDialogCallBack2.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileProgressDialogCallBack2.java new file mode 100644 index 00000000..1418c3e8 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileProgressDialogCallBack2.java @@ -0,0 +1,145 @@ +package com.bonait.bnframework.common.http.callback.files2; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.text.format.Formatter; +import android.util.Log; + +import com.bonait.bnframework.MainApplication; +import com.bonait.bnframework.common.notification.DownloadNotification; +import com.bonait.bnframework.common.utils.AlertDialogUtils; +import com.bonait.bnframework.common.utils.NetworkUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.model.Progress; +import com.lzy.okgo.model.Response; +import com.lzy.okgo.request.base.Request; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import java.io.File; +import java.util.Locale; + +/** + * Created by LY on 2019/4/10. + * 带加载进度条的下载回调接口,自定义的文件回调 + */ +public abstract class FileProgressDialogCallBack2 extends FileCallback2 { + + private ProgressDialog progressDialog; + private DownloadNotification downloadNotification; + + + public FileProgressDialogCallBack2(Context context, long totalSize) { + super(totalSize); + initDialog(context, totalSize); + } + + public FileProgressDialogCallBack2(Context context, String destFileName, long totalSize) { + super(destFileName, totalSize); + initDialog(context, totalSize); + } + + public FileProgressDialogCallBack2(Context context, String destFileDir, String destFileName, long totalSize) { + super(destFileDir, destFileName, totalSize); + initDialog(context, totalSize); + } + + @Override + public void onStart(Request request) { + if (progressDialog != null && !progressDialog.isShowing()) { + progressDialog.show(); + } + } + + @Override + public void onFinish() { + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + + if (downloadNotification != null) { + downloadNotification.downloadComplete(); + } + } + + @Override + public void onError(Response response) { + super.onError(response); + if (downloadNotification != null) { + downloadNotification.downloadError(); + } + + ToastUtils.error(response.body().getName() + "下载失败,请重新下载!"); + if (progressDialog != null && progressDialog.isShowing()) { + progressDialog.dismiss(); + } + } + + @Override + public void downloadProgress(Progress progress) { + String downloadLength = Formatter.formatFileSize(MainApplication.getContext(), progress.currentSize); + String totalLength = Formatter.formatFileSize(MainApplication.getContext(), progress.totalSize); + + progressDialog.setProgress((int) (progress.fraction * progress.totalSize)); + progressDialog.setProgressNumberFormat(String.format(Locale.CHINA, "%sMb / %sMb", downloadLength, totalLength)); + Log.d("downloadProgress", progress.fileName + " 下载中……" + String.format(Locale.CHINA, "%s / %s", downloadLength, totalLength)); + + if (downloadNotification != null) { + downloadNotification.showProgress(progress); + } + } + + /** + * 判断网络状态。移动/WiFi,弹出警告框 + */ + private void initDialog(final Context context, final long totalSize) { + if (NetworkUtils.isActiveNetworkMobile(context)) { + String title = "温馨提示!"; + String message = "当前网络为移动网络,可能会消耗大量移动流量数据,确定要下载吗?"; + AlertDialogUtils.showDialog(context, title, message, new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + downloadingDialog(context, totalSize); + dialog.dismiss(); + } + }); + } else { + downloadingDialog(context, totalSize); + } + } + + /** + * 显示进度条对话框 + * */ + private void downloadingDialog(final Context context, long totalSize) { + progressDialog = new ProgressDialog(context); + progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + progressDialog.setCancelable(false); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setTitle("正在下载"); + + if (totalSize > 0) { + progressDialog.setMax((int) totalSize); + } + + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "停止下载", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + OkGo.getInstance().cancelTag(context); + ToastUtils.info("已停止下载!"); + dialog.dismiss(); + } + }); + + progressDialog.setButton(DialogInterface.BUTTON_POSITIVE, "后台下载", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + downloadNotification = new DownloadNotification(context); + dialog.dismiss(); + } + }); + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/ReadMe.txt b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/ReadMe.txt new file mode 100644 index 00000000..addf0821 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/files2/ReadMe.txt @@ -0,0 +1,21 @@ +由于一些服务器http返回头的原因:在文件下载中, +响应头为:Transfer-Encoding: chunked; +请求头为:Accept-Encoding: gzip, deflate; +且没有返回Content-Length这个响应头,或者Content-Length = -1。 +这样的http请求是以压缩gzip形式动态下载,文件下载总大小是无法预知的。所以使用一般的默认的fileCallback回调接口,进度条是呈现不出来进度的。 + +小提示:Transfer-Encoding: chunked;与 Content-Length 这两个响应头二者只能存在一个,即使两者都有,Content-Length也会默认失效。 + +//---------------------------------------------------------------------------------------------// + +由于OkGo里的OkDownload主要功能必须要添加Content-Length这个响应头,才能获取文件大小。 +(OkGo原文说明:服务端一定要返回Content-Length,注意,是一定要返回Content-Length这个响应头,如果没有,该值默认是-1, +这个值表示当前要下载的文件有多大,如果服务端不给的话,客户端在下载过程中是不可能知道我要下载的文件有多大的,所以常见的问题就是进度是负数。) + +为了应对这种情况,在这个包中重写了fileCallback和fileConvert这两个类,开放文件总大小totalSize这个参数。 +因此,需要自己传参文件总大小。(文件大小必须为实际大小byte,不然进度条会出现奇怪的问题) + +//---------------------------------------------------------------------------------------------// + +如果后台服务器有Content-Length,并且不是gzip压缩,则可以使用file包下的FileProgressDialogCallBack或者FileCallback回调。 +如果后台服务器没有Content-Length或者Content-Length = -1,且是gzip压缩,则可以使用file2包下的FileProgressDialogCallBack2或者FileCallback2回调,并传入totalSize参数。 \ No newline at end of file diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonCallback.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonCallback.java new file mode 100644 index 00000000..69631f2b --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonCallback.java @@ -0,0 +1,117 @@ +package com.bonait.bnframework.common.http.callback.json; + +import android.app.Activity; +import android.content.Intent; + +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.common.constant.SPConstants; +import com.bonait.bnframework.common.http.exception.TokenException; +import com.bonait.bnframework.common.utils.PreferenceUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.bonait.bnframework.modules.welcome.activity.WelcomeActivity; +import com.lzy.okgo.callback.AbsCallback; +import com.lzy.okgo.exception.HttpException; +import com.lzy.okgo.exception.StorageException; +import com.lzy.okgo.model.Response; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +/** + * Created by LY on 2019/4/2. + */ +public abstract class JsonCallback extends AbsCallback { + + private Type type; + private Class clazz; + + public JsonCallback() { + } + + public JsonCallback(Type type) { + this.type = type; + } + + public JsonCallback(Class clazz) { + this.clazz = clazz; + } + + /** + * 该方法是子线程处理,不能做ui相关的工作 + * 主要作用是解析网络返回的 response 对象,生产onSuccess回调中需要的数据对象 + * 这里的解析工作不同的业务逻辑基本都不一样,所以需要自己实现,实际使用根据需要修改 + */ + @Override + public T convertResponse(okhttp3.Response response) throws Throwable { + + // 不同的业务,这里的代码逻辑都不一样,如果你不修改,那么基本不可用 + + //详细自定义的原理和文档,看这里: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback + + if (type == null) { + if (clazz == null) { + Type genType = getClass().getGenericSuperclass(); + type = ((ParameterizedType) genType).getActualTypeArguments()[0]; + } else { + JsonConvert convert = new JsonConvert<>(clazz); + return convert.convertResponse(response); + } + } + + JsonConvert convert = new JsonConvert<>(type); + return convert.convertResponse(response); + } + + + @Override + public void onError(Response response) { + super.onError(response); + + Throwable exception = response.getException(); + //如果OkGo配置中Logger日志已打开,super.onError(response);就已经将printStackTrace打印出来了 + /*if (exception != null) { + exception.printStackTrace(); + }*/ + + if (exception instanceof UnknownHostException || exception instanceof ConnectException) { + ToastUtils.error("网络连接失败,请连接网络!"); + } else if (exception instanceof SocketTimeoutException) { + ToastUtils.error("网络请求超时,请稍后重试!"); + } else if (exception instanceof HttpException) { + ToastUtils.error("暂时无法连接服务器,请稍后重试!"); + } else if (exception instanceof StorageException) { + ToastUtils.error("SD卡不存在或者没有获取SD卡访问权限!"); + } else if (exception instanceof TokenException) { + // 删除token,跳转到登录页面 + skipLoginActivityAndFinish(exception); + } else if (exception instanceof IllegalStateException) { + ToastUtils.error(exception.getMessage()); + } + } + + /** + * 删除token,跳转到登录页面 + * */ + private void skipLoginActivityAndFinish(Throwable exception) { + //删除本地过期的token + PreferenceUtils.remove(SPConstants.TOKEN); + PreferenceUtils.remove(SPConstants.USER_ID); + Intent intent = new Intent("com.bonait.bnframework.modules.welcome.activity.LoginActivity.ACTION_START"); + // 获取当前Activity(栈中最后一个压入的) + Activity activity = ActivityLifecycleManager.get().currentActivity(); + + // 如果不是WelcomeActivity,显示Toast告知token已过期 + if (activity.getClass() != WelcomeActivity.class) { + ToastUtils.info(exception.getMessage()); + } + + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + // 跳转到登录页面 + activity.startActivity(intent); + activity.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonConvert.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonConvert.java new file mode 100644 index 00000000..f9a5e0fe --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonConvert.java @@ -0,0 +1,279 @@ +package com.bonait.bnframework.common.http.callback.json; + +import android.text.TextUtils; + +import com.bonait.bnframework.common.http.GsonConvert; +import com.bonait.bnframework.common.http.exception.TokenException; +import com.bonait.bnframework.common.model.BaseCodeJson; +import com.bonait.bnframework.common.model.BaseJson; +import com.bonait.bnframework.common.model.SimpleBaseJson; +import com.bonait.bnframework.common.model.SimpleCodeJson; +import com.google.gson.stream.JsonReader; +import com.lzy.okgo.convert.Converter; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.Charset; + +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.Buffer; +import okio.BufferedSource; + +/** + * Created by LY on 2019/4/2. + */ +public class JsonConvert implements Converter { + + private Type type; + private Class clazz; + + public JsonConvert() { + } + + public JsonConvert(Type type) { + this.type = type; + } + + public JsonConvert(Class clazz) { + this.clazz = clazz; + } + + + /** + * 该方法是子线程处理,不能做ui相关的工作 + * 主要作用是解析网络返回的 response 对象,生成onSuccess回调中需要的数据对象 + * 这里的解析工作不同的业务逻辑基本都不一样,所以需要自己实现。 + */ + @Override + public T convertResponse(Response response) throws Throwable { + + // 不同的业务,这里的代码逻辑都不一样,要按需修改 + + // 如果你对这里的代码原理不清楚,可以看这里的详细原理说明: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback + // 如果你对这里的代码原理不清楚,可以看这里的详细原理说明: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback + // 如果你对这里的代码原理不清楚,可以看这里的详细原理说明: https://github.com/jeasonlzy/okhttp-OkGo/wiki/JsonCallback + + + /* + * 由于项目需要,App端请求网络必需添加一个Token验证,所以登录成功之后会在OkGo全局添加一个Token的param + * 每次请求网络后台都会验证这个token是否过期,如果过期,则返回{"code":102,"msg":"token过期"} + * */ + checkIfTheTokenHasExpired(response); + + if (type == null) { + if (clazz == null) { + // 如果没有通过构造函数传进来,就自动解析父类泛型的真实类型(有局限性,继承后就无法解析到) + Type genType = getClass().getGenericSuperclass(); + type = ((ParameterizedType) genType).getActualTypeArguments()[0]; + } else { + return parseClass(response, clazz); + } + } + + if (type instanceof ParameterizedType) { + return parseParameterizedType(response, (ParameterizedType) type); + } else if (type instanceof Class) { + return parseClass(response, (Class) type); + } else { + return parseType(response, type); + } + } + + /** + * 检查Token是否过期,如果过期抛出TokenException异常并返回登录界面。 + * + * 由于写这个方法时,出现了response.body()只调用一次就会close掉,导致后面方法无法继续获取response而发生异常 + * 下面是一种解决方案,在读取buffer之前,先对buffer进行clone一下。这时候就可以拿到返回的数据,然后就可以继续调用response.body()。 + * */ + private void checkIfTheTokenHasExpired(Response response) throws Throwable { + ResponseBody body = response.body(); + if (body == null) return ; + + BufferedSource source = body.source(); + source.request(Long.MAX_VALUE); + Buffer buffer = source.buffer(); + String responseBodyString = buffer.clone().readString(Charset.forName("UTF-8")); + buffer.close(); + + JSONObject jsonObject = new JSONObject(responseBodyString); + if (jsonObject.optInt("code") == 102) { + throw new TokenException("token已过期"); + } + } + + + /** + * 根据class解析json数据 + * */ + private T parseClass(Response response, Class rawType) throws Exception { + if (rawType == null) return null; + ResponseBody body = response.body(); + if (body == null) return null; + JsonReader jsonReader = new JsonReader(body.charStream()); + + if (rawType == String.class) { + //noinspection unchecked + return (T) body.string(); + } else if (rawType == JSONObject.class) { + //noinspection unchecked + return (T) new JSONObject(body.string()); + } else if (rawType == JSONArray.class) { + //noinspection unchecked + return (T) new JSONArray(body.string()); + } else { + // 泛型格式如下: new JsonCallback<任意JavaBean>(this) + T t = GsonConvert.fromJson(jsonReader, rawType); + response.close(); + return t; + } + } + + /** + * 根据Type,解析json数据 + * */ + private T parseType(Response response, Type type) throws Exception { + if (type == null) return null; + ResponseBody body = response.body(); + if (body == null) return null; + JsonReader jsonReader = new JsonReader(body.charStream()); + + // 泛型格式如下: new JsonCallback<任意JavaBean>(this) + T t = GsonConvert.fromJson(jsonReader, type); + response.close(); + return t; + } + + private T parseParameterizedType(Response response, ParameterizedType type) throws Exception { + if (type == null) return null; + ResponseBody body = response.body(); + if (body == null) return null; + JsonReader jsonReader = new JsonReader(body.charStream()); + + // 泛型的实际类型 + Type rawType = type.getRawType(); + // 泛型的参数 + Type typeArgument = type.getActualTypeArguments()[0]; + + if (rawType == BaseJson.class) { + // success json格式 + return convertBaseJson(response,type,jsonReader,typeArgument); + } else if (rawType == BaseCodeJson.class) { + // code json格式 + return convertBaseCodeJson(response,type,jsonReader,typeArgument); + } else { + // 其他json格式 + // 泛型格式如下: new JsonCallback<外层BaseBean<内层JavaBean>>(this) + T t = GsonConvert.fromJson(jsonReader, type); + response.close(); + return t; + } + } + + /** + * 解析BaseLoginJson与其属性为泛型的参数 + * 根据BaseLoginJson解析出来的数据,实现不同的业务逻辑 + * 要点:code : 0/101/104 根据code实现不同的业务逻辑 + * + * */ + private T convertBaseCodeJson(Response response,ParameterizedType type,JsonReader jsonReader,Type typeArgument) { + + //code返回的数据,0:表示成功,其他失败的有:101,102,900 + int code; + // msg信息 + String msg; + + if (typeArgument == Void.class) { + // 泛型格式如下: new JsonCallback>(this) + SimpleCodeJson simpleCodeJson = GsonConvert.fromJson(jsonReader,SimpleCodeJson.class); + response.close(); + + code = simpleCodeJson.getCode(); + msg = simpleCodeJson.getMsg(); + + // code为0: 表示成功 + if (code == 0) { + //noinspection unchecked + return (T) simpleCodeJson.toBaseCodeJson(); + } + } else { + // 泛型格式如下: new JsonCallback>(this) + BaseCodeJson baseCodeJson = GsonConvert.fromJson(jsonReader,type); + response.close(); + + code = baseCodeJson.getCode(); + msg = baseCodeJson.getMsg(); + + // code为0: 表示成功 + if (code == 0) { + //noinspection unchecked + return (T) baseCodeJson; + } + } + + if (TextUtils.isEmpty(msg)) { + msg = "未知错误,请稍后重试!"; + } + + // 当code返回不是0时,表示错误数据,根据数据返回不同的错误信息 + if (code == 101) { + throw new IllegalStateException(msg+""); + } else if (code == 102) { + throw new TokenException(msg+""); + } else if (code == 900) { + throw new IllegalStateException(msg+""); + } else { + throw new IllegalStateException(msg+""); + } + } + + /** + * 解析BaseJson与其属性为泛型的参数 + * 根据BaseJson解析出来的数据,实现不同的业务逻辑 + * 要点:success : true/false 根据success实现不同的业务逻辑 + * + * */ + private T convertBaseJson(Response response,ParameterizedType type,JsonReader jsonReader,Type typeArgument) { + + // 一般来说服务器会和客户端约定一个数表示成功,其余的表示失败,这里根据实际情况修改 + // success为true则表示成功,false表示失败 + boolean success; + // msg信息 + String msg; + + if (typeArgument == Void.class) { + // 泛型格式如下: new JsonCallback>(this) + SimpleBaseJson simpleBaseJson = GsonConvert.fromJson(jsonReader, SimpleBaseJson.class); + response.close(); + success = simpleBaseJson.isSuccess(); + msg = simpleBaseJson.getMsg(); + // success为true时,表示成功 + if (success) { + //noinspection unchecked + return (T) simpleBaseJson.toBaseJson(); + } + + } else { + // 泛型格式如下: new JsonCallback>(this) + BaseJson baseJson = GsonConvert.fromJson(jsonReader, type); + response.close(); + success = baseJson.isSuccess(); + msg = baseJson.getMsg(); + // success为true时,表示成功 + if (success) { + //noinspection unchecked + return (T) baseJson; + } + } + + // 当success为false时,会走以下方法,显示不同的错误信息 + if (msg != null) { + throw new IllegalStateException(msg+""); + } else { + throw new IllegalStateException("请求服务器失败,请稍后重试!"); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonDialogCallback.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonDialogCallback.java new file mode 100644 index 00000000..851ce807 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonDialogCallback.java @@ -0,0 +1,57 @@ +package com.bonait.bnframework.common.http.callback.json; + +import android.content.Context; + +import com.lzy.okgo.request.base.Request; +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog; + +/** + * Created by LY on 2019/4/1. + * + * 在网络请求前显示一个loading,请求结束后取消loading + * + * 有加载框的网络请求回调 + * + */ +public abstract class JsonDialogCallback extends JsonCallback { + + private QMUITipDialog tipDialog; + + /** + * 弹出加载框入口 + * */ + public JsonDialogCallback(Context context) { + super(); + initDialog(context); + } + + /** + * 初始化加载框 + * */ + private void initDialog(Context context) { + tipDialog = new QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord("正在加载") + .create(); + } + + /** + * 请求网络开始前弹出加载框 + * */ + @Override + public void onStart(Request request) { + if (tipDialog != null && !tipDialog.isShowing()) { + tipDialog.show(); + } + } + + /** + * 请求网络结束后关闭加载框 + * */ + @Override + public void onFinish() { + if (tipDialog != null && tipDialog.isShowing()) { + tipDialog.dismiss(); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/callback/strings/StringDialogCallback.java b/app/src/main/java/com/bonait/bnframework/common/http/callback/strings/StringDialogCallback.java new file mode 100644 index 00000000..fb3bf016 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/callback/strings/StringDialogCallback.java @@ -0,0 +1,52 @@ +package com.bonait.bnframework.common.http.callback.strings; + +import android.content.Context; + +import com.lzy.okgo.callback.StringCallback; +import com.lzy.okgo.request.base.Request; +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog; + +/** + * Created by LY on 2019/4/2. + * + * 对convertResponse()按文本解析,解析的编码依据服务端响应头中的Content-Type中的编码格式,自动进行编码转换,确保不出现乱码 + * + * 有加载框的文本解析回调 + * + */ +public abstract class StringDialogCallback extends StringCallback { + + private QMUITipDialog tipDialog; + + /** + * 展示加载框 + * */ + public StringDialogCallback(Context context) { + tipDialog = new QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord("正在加载") + .create(); + } + + + /** + * 请求网络开始前弹出加载框 + * */ + @Override + public void onStart(Request request) { + if (tipDialog != null && !tipDialog.isShowing()) { + tipDialog.show(); + } + } + + /** + * 请求网络结束后关闭加载框 + * */ + @Override + public void onFinish() { + if (tipDialog != null && tipDialog.isShowing()) { + tipDialog.dismiss(); + } + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/http/exception/TokenException.java b/app/src/main/java/com/bonait/bnframework/common/http/exception/TokenException.java new file mode 100644 index 00000000..31c42a49 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/http/exception/TokenException.java @@ -0,0 +1,18 @@ +package com.bonait.bnframework.common.http.exception; + +/** + * Created by LY on 2019/4/3. + */ +public class TokenException extends RuntimeException { + + private static final long serialVersionUID = 8394950517718638426L; + + + public TokenException() { + super(); + } + + public TokenException(String message) { + super(message); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/interfaces/OnDoIntListener.java b/app/src/main/java/com/bonait/bnframework/common/interfaces/OnDoIntListener.java new file mode 100644 index 00000000..cb3c0142 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/interfaces/OnDoIntListener.java @@ -0,0 +1,9 @@ +package com.bonait.bnframework.common.interfaces; + +/** + * Created by LY on 2019/3/28. + * 动画工具类接口监听 + */ +public interface OnDoIntListener { + void doSomething(int intValue); +} diff --git a/app/src/main/java/com/bonait/bnframework/common/model/BaseCodeJson.java b/app/src/main/java/com/bonait/bnframework/common/model/BaseCodeJson.java new file mode 100644 index 00000000..f4b4c0c3 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/model/BaseCodeJson.java @@ -0,0 +1,56 @@ +package com.bonait.bnframework.common.model; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +/** + * Created by LY on 2019/4/2. + */ +public class BaseCodeJson implements Serializable { + + private static final long serialVersionUID = -2849146113542167339L; + + @SerializedName("code") + private int code; + @SerializedName("msg") + private String msg; + @SerializedName(value = "result" , alternate = {"data"}) + private T result; + + + @SerializedName("userId") + private int userId; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/model/BaseJson.java b/app/src/main/java/com/bonait/bnframework/common/model/BaseJson.java new file mode 100644 index 00000000..6a31b954 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/model/BaseJson.java @@ -0,0 +1,57 @@ +package com.bonait.bnframework.common.model; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +/** + * Created by LY on 2019/4/2. + */ +public class BaseJson implements Serializable { + + private static final long serialVersionUID = 477369592714415773L; + + @SerializedName("success") + private boolean success; + + @SerializedName("totalCounts") + private String totalCounts; + + @SerializedName(value = "result" , alternate = {"data"}) + private T result; + + private String msg; + + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getTotalCounts() { + return totalCounts; + } + + public void setTotalCounts(String totalCounts) { + this.totalCounts = totalCounts; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/model/SimpleBaseJson.java b/app/src/main/java/com/bonait/bnframework/common/model/SimpleBaseJson.java new file mode 100644 index 00000000..af19b5ff --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/model/SimpleBaseJson.java @@ -0,0 +1,39 @@ +package com.bonait.bnframework.common.model; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; + +/** + * Created by LY on 2019/4/2. + */ +public class SimpleBaseJson implements Serializable { + + @SerializedName("success") + private boolean success; + + private String msg; + + public BaseJson toBaseJson() { + BaseJson baseJson = new BaseJson(); + baseJson.setSuccess(success); + baseJson.setMsg(msg); + return baseJson; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/model/SimpleCodeJson.java b/app/src/main/java/com/bonait/bnframework/common/model/SimpleCodeJson.java new file mode 100644 index 00000000..624b96c4 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/model/SimpleCodeJson.java @@ -0,0 +1,38 @@ +package com.bonait.bnframework.common.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by LY on 2019/4/3. + */ +public class SimpleCodeJson { + + @SerializedName("code") + private int code; + @SerializedName("msg") + private String msg; + + + public BaseCodeJson toBaseCodeJson() { + BaseCodeJson baseCodeJson = new BaseCodeJson(); + baseCodeJson.setCode(code); + baseCodeJson.setMsg(msg); + return baseCodeJson; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/notification/DownloadNotification.java b/app/src/main/java/com/bonait/bnframework/common/notification/DownloadNotification.java new file mode 100644 index 00000000..06b4c543 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/notification/DownloadNotification.java @@ -0,0 +1,98 @@ +package com.bonait.bnframework.common.notification; + +import android.app.Notification; +import android.app.NotificationManager; +import android.content.Context; +import android.graphics.BitmapFactory; +import android.support.v4.app.NotificationCompat; +import android.text.format.Formatter; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.MainApplication; +import com.lzy.okgo.model.Progress; + +import java.util.Date; +import java.util.Locale; + + +/** + * Created by LY on 2019/4/18. + * 下载通知栏notification + */ +public class DownloadNotification { + + private Context context; + private int notificationId; + private String Tag = "downloading"; // 下载通知栏标识 + private String downloadFileName; // 下载文件名称 + + public DownloadNotification(Context context) { + notificationId = (int) new Date().getTime(); + this.context = context; + } + + /** + * 显示notification下载进度 + * */ + public void showProgress(Progress progress) { + downloadFileName = progress.fileName; + String downloadLength = Formatter.formatFileSize(MainApplication.getContext(), progress.currentSize); + String totalLength = Formatter.formatFileSize(MainApplication.getContext(), progress.totalSize); + + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new NotificationCompat.Builder(context, MainNotification.CHANNEL_DOWNLOADING_ID) + .setSmallIcon(R.drawable.icon_user_pic) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_user_pic)) + .setContentTitle(progress.fileName) + .setContentText("下载进度:" + String.format(Locale.CHINA, "%s / %s", downloadLength, totalLength)) + .setProgress(100, (int) (progress.fraction * 100.0), false) + .setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) + .build(); + + if (notificationManager != null) { + notificationManager.notify(Tag,notificationId,notification); + } + } + + /** + * 下载完成,关闭进度条通知栏,显示成功通知栏 + * */ + public void downloadComplete() { + int downloadCompleteId = (int) new Date().getTime(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + Notification notification = new NotificationCompat.Builder(context, MainNotification.CHANNEL_DOWNLOAD_COMPLETE_ID) + .setSmallIcon(R.drawable.icon_user_pic) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_user_pic)) + .setContentTitle(downloadFileName) + .setContentText("下载完成!") + .setPriority(NotificationCompat.PRIORITY_MAX) + .setAutoCancel(true) + .build(); + if (notificationManager != null) { + notificationManager.notify(downloadCompleteId,notification); + notificationManager.cancel(Tag,notificationId); + } + } + + /** + * 下载错误,关闭进度条通知栏,显示错误通知栏 + * */ + public void downloadError() { + int downloadCompleteId = (int) new Date().getTime(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + Notification notification = new NotificationCompat.Builder(context, MainNotification.CHANNEL_DOWNLOAD_COMPLETE_ID) + .setSmallIcon(R.drawable.icon_user_pic) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_user_pic)) + .setContentTitle(downloadFileName) + .setContentText("下载失败,请重新下载!") + .setAutoCancel(true) + .build(); + if (notificationManager != null) { + notificationManager.notify(downloadCompleteId,notification); + notificationManager.cancel(Tag,notificationId); + } + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/notification/MainNotification.java b/app/src/main/java/com/bonait/bnframework/common/notification/MainNotification.java new file mode 100644 index 00000000..033e635c --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/notification/MainNotification.java @@ -0,0 +1,85 @@ +package com.bonait.bnframework.common.notification; + +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; +import android.support.annotation.RequiresApi; + + +/** + * Created by LY on 2019/4/22. + * 初始化notification,针对Android8.0以上创建消息渠道组和消息渠道 + */ +public class MainNotification { + + public static final String GROUP_DOWNLOAD_ID = "group_download_id"; + public static final String GROUP_PUSH_MESSAGE_ID = "group_push_message_id"; + + public static final String CHANNEL_DOWNLOADING_ID = "channel_downloading_id"; + public static final String CHANNEL_DOWNLOAD_COMPLETE_ID = "channel_download_complete_id"; + public static final String CHANNEL_MESSAGE_ID = "channel_message_id"; + + /** + * 初始化Notification 消息通知渠道 + */ + public static void initNotificationChannel(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + // 创建渠道组 + createNotificationGroup(context, GROUP_DOWNLOAD_ID, "下载消息"); + createNotificationGroup(context, GROUP_PUSH_MESSAGE_ID, "推送消息"); + + // 创建渠道 + // 下载完成通知栏 + createNotificationChannel(context, + GROUP_DOWNLOAD_ID, + CHANNEL_DOWNLOAD_COMPLETE_ID, + "下载完成通知栏", + NotificationManager.IMPORTANCE_MAX); + + // 下载通知栏 + createNotificationChannel(context, + GROUP_DOWNLOAD_ID, + CHANNEL_DOWNLOADING_ID, + "下载通知栏", + NotificationManager.IMPORTANCE_LOW); + + // 常规通知栏 + createNotificationChannel(context, + GROUP_PUSH_MESSAGE_ID, + CHANNEL_MESSAGE_ID, + "常规通知栏", + NotificationManager.IMPORTANCE_MAX); + + } + } + + /** + * 创建消息渠道组 + */ + @RequiresApi(api = Build.VERSION_CODES.O) + public static void createNotificationGroup(Context context, String groupId, String groupName) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationChannelGroup group = new NotificationChannelGroup(groupId, groupName); + if (notificationManager != null) { + notificationManager.createNotificationChannelGroup(group); + } + } + + /** + * 创建消息渠道,并分配到指定组下。 + */ + @RequiresApi(api = Build.VERSION_CODES.O) + public static void createNotificationChannel(Context context, String groupId, String channelId, String channelName, int importance) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationChannel channel = new NotificationChannel(channelId, channelName, importance); + channel.canShowBadge(); + //在创建通知渠道前,指定渠道组 id + channel.setGroup(groupId); + if (notificationManager != null) { + notificationManager.createNotificationChannel(channel); + } + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/AlertDialogUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/AlertDialogUtils.java new file mode 100644 index 00000000..1ed9d6b1 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/AlertDialogUtils.java @@ -0,0 +1,104 @@ +package com.bonait.bnframework.common.utils; + +import android.content.Context; + +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +/** + * Created by LY on 2019/3/25. + */ +public class AlertDialogUtils { + + // 定义对话框主题样式 + public static final int mCurrentDialogStyle = com.qmuiteam.qmui.R.style.QMUI_Dialog; + + /** + * 对话框,只有确定按钮 + * */ + public static void showDialog(Context context,String title,String message) { + new QMUIDialog.MessageDialogBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .addAction("确定", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .create(mCurrentDialogStyle).show(); + } + + /** + * 对话框,只有确定按钮,自定义确定按钮文本 + * */ + public static void showDialog(Context context, String title, String message,String ok) { + new QMUIDialog.MessageDialogBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .addAction(ok, new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .create(mCurrentDialogStyle).show(); + } + + /** + * 对话框,有取消确定按钮 + * */ + public static void showDialog(Context context, String title, String message, QMUIDialogAction.ActionListener onClickListener) { + new QMUIDialog.MessageDialogBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction("确定", onClickListener) + .create(mCurrentDialogStyle).show(); + } + + /** + * 对话框,自定义确定按钮 + * */ + public static void showDialog(Context context, String title, String message,String ok, QMUIDialogAction.ActionListener onClickListener) { + new QMUIDialog.MessageDialogBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction(ok, onClickListener) + .create(mCurrentDialogStyle).show(); + } + + /** + * 对话框,带红色按钮 + * */ + public static void showRedButtonDialog(Context context, String title, String message,String ok, QMUIDialogAction.ActionListener onClickListener) { + new QMUIDialog.MessageDialogBuilder(context) + .setCancelable(false) + .setTitle(title) + .setMessage(message) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction(0, ok, QMUIDialogAction.ACTION_PROP_NEGATIVE, onClickListener) + .create(mCurrentDialogStyle).show(); + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/AnimationToolUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/AnimationToolUtils.java new file mode 100644 index 00000000..b2c61941 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/AnimationToolUtils.java @@ -0,0 +1,226 @@ +package com.bonait.bnframework.common.utils; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; +import android.animation.ValueAnimator; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; +import android.view.animation.AnticipateOvershootInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.OvershootInterpolator; +import android.view.animation.ScaleAnimation; + +import com.bonait.bnframework.common.interfaces.OnDoIntListener; + +/** + * Created by LY on 2019/3/28. + * 动画工具类 + */ +public class AnimationToolUtils { + public static void start(Animator animator) { + if (animator != null && !animator.isStarted()) { + animator.start(); + } + } + + public static void stop(Animator animator) { + if (animator != null && !animator.isRunning()) { + animator.end(); + } + } + + public static boolean isRunning(ValueAnimator animator) { + return animator != null && animator.isRunning(); + } + + public static boolean isStarted(ValueAnimator animator) { + return animator != null && animator.isStarted(); + } + + /** + * 颜色渐变动画 + * + * @param beforeColor 变化之前的颜色 + * @param afterColor 变化之后的颜色 + * @param listener 变化事件 + */ + public static void animationColorGradient(int beforeColor, int afterColor, final OnDoIntListener listener) { + ValueAnimator valueAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), beforeColor, afterColor).setDuration(3000); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + // textView.setTextColor((Integer) animation.getAnimatedValue()); + listener.doSomething((Integer) animation.getAnimatedValue()); + } + }); + valueAnimator.start(); + } + + /** + * 卡片翻转动画 + * + * @param beforeView + * @param afterView + */ + public static void cardFilpAnimation(final View beforeView, final View afterView) { + Interpolator accelerator = new AccelerateInterpolator(); + Interpolator decelerator = new DecelerateInterpolator(); + ObjectAnimator invisToVis = null; + ObjectAnimator visToInvis = null; + if (beforeView.getVisibility() == View.GONE) { + // 局部layout可达到字体翻转 背景不翻转 + invisToVis = ObjectAnimator.ofFloat(beforeView, + "rotationY", -90f, 0f); + visToInvis = ObjectAnimator.ofFloat(afterView, + "rotationY", 0f, 90f); + } else if (afterView.getVisibility() == View.GONE) { + invisToVis = ObjectAnimator.ofFloat(afterView, + "rotationY", -90f, 0f); + visToInvis = ObjectAnimator.ofFloat(beforeView, + "rotationY", 0f, 90f); + } + + visToInvis.setDuration(250);// 翻转速度 + visToInvis.setInterpolator(accelerator);// 在动画开始的地方速率改变比较慢,然后开始加速 + invisToVis.setDuration(250); + invisToVis.setInterpolator(decelerator); + final ObjectAnimator finalInvisToVis = invisToVis; + final ObjectAnimator finalVisToInvis = visToInvis; + visToInvis.addListener(new Animator.AnimatorListener() { + + @Override + public void onAnimationEnd(Animator arg0) { + if (beforeView.getVisibility() == View.GONE) { + afterView.setVisibility(View.GONE); + finalInvisToVis.start(); + beforeView.setVisibility(View.VISIBLE); + } else { + afterView.setVisibility(View.GONE); + finalVisToInvis.start(); + beforeView.setVisibility(View.VISIBLE); + } + } + + @Override + public void onAnimationCancel(Animator arg0) { + + } + + @Override + public void onAnimationRepeat(Animator arg0) { + + } + + @Override + public void onAnimationStart(Animator arg0) { + + } + }); + visToInvis.start(); + } + + /** + * 缩小动画 + * + * @param view + */ + public static void zoomIn(final View view, float scale, float dist) { + view.setPivotY(view.getHeight()); + view.setPivotX(view.getWidth() / 2); + AnimatorSet mAnimatorSet = new AnimatorSet(); + ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, scale); + ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.0f, scale); + ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(view, "translationY", 0.0f, -dist); + + mAnimatorSet.play(mAnimatorTranslateY).with(mAnimatorScaleX); + mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY); + mAnimatorSet.setDuration(300); + mAnimatorSet.start(); + } + + /** + * 放大动画 + * + * @param view + */ + public static void zoomOut(final View view, float scale) { + view.setPivotY(view.getHeight()); + view.setPivotX(view.getWidth() / 2); + AnimatorSet mAnimatorSet = new AnimatorSet(); + + ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(view, "scaleX", scale, 1.0f); + ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(view, "scaleY", scale, 1.0f); + ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(view, "translationY", view.getTranslationY(), 0); + + mAnimatorSet.play(mAnimatorTranslateY).with(mAnimatorScaleX); + mAnimatorSet.play(mAnimatorScaleX).with(mAnimatorScaleY); + mAnimatorSet.setDuration(300); + mAnimatorSet.start(); + } + + public static void ScaleUpDowm(View view) { + ScaleAnimation animation = new ScaleAnimation(1.0f, 1.0f, 0.0f, 1.0f); + animation.setRepeatCount(-1); + animation.setRepeatMode(Animation.RESTART); + animation.setInterpolator(new LinearInterpolator()); + animation.setDuration(1200); + view.startAnimation(animation); + } + + public static void animateHeight(int start, int end, final View view) { + ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int value = (int) animation.getAnimatedValue();//根据时间因子的变化系数进行设置高度 + ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); + layoutParams.height = value; + view.setLayoutParams(layoutParams);//设置高度 + } + }); + valueAnimator.start(); + } + + public static ObjectAnimator popup(final View view, final long duration) { + view.setAlpha(0); + view.setVisibility(View.VISIBLE); + + ObjectAnimator popup = ObjectAnimator.ofPropertyValuesHolder(view, + PropertyValuesHolder.ofFloat("alpha", 0f, 1f), + PropertyValuesHolder.ofFloat("scaleX", 0f, 1f), + PropertyValuesHolder.ofFloat("scaleY", 0f, 1f)); + popup.setDuration(duration); + popup.setInterpolator(new OvershootInterpolator()); + + return popup; + } + + public static ObjectAnimator popout(final View view, final long duration, final AnimatorListenerAdapter animatorListenerAdapter) { + ObjectAnimator popout = ObjectAnimator.ofPropertyValuesHolder(view, + PropertyValuesHolder.ofFloat("alpha", 1f, 0f), + PropertyValuesHolder.ofFloat("scaleX", 1f, 0f), + PropertyValuesHolder.ofFloat("scaleY", 1f, 0f)); + popout.setDuration(duration); + popout.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + view.setVisibility(View.GONE); + if (animatorListenerAdapter != null) { + animatorListenerAdapter.onAnimationEnd(animation); + } + } + }); + popout.setInterpolator(new AnticipateOvershootInterpolator()); + + return popout; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/AppFileUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/AppFileUtils.java new file mode 100644 index 00000000..12966c8e --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/AppFileUtils.java @@ -0,0 +1,217 @@ +package com.bonait.bnframework.common.utils; + +import android.os.Environment; + +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by LY on 2019/1/17. + */ +public class AppFileUtils { + + private static final String APP_PROJECT_DIR = "com.bonait.bnframework"; + private static final String APP_DIR = "bnframework"; + private static final String UPLOAD_IMAGE = "UploadImage"; + private static final String UPLOAD_FILE ="UploadFile"; + private static final String DOWNLOAD_IMAGE = "DownloadImage"; + private static final String DOWNLOAD_FILE ="DownloadFile"; + private static final String USER_FILE = "UserFile"; + private static final String CACHE = "Cache"; + private static final String CAMERA_V2 = "CameraV2"; + public static final String ZIP_FILE = "ZIP"; + + + /** + * 获取data目录下的图片目录 + * */ + public static String getUploadImageDir() { + String dir = getPrivateAppDir() +"/" + UPLOAD_IMAGE+"/"; + return mkdirs(dir); + } + + /** + * 获取data目录下的文件目录 + * */ + public static String getUploadFileDir() { + String dir = getPrivateAppDir() +"/" + UPLOAD_FILE+"/"; + return mkdirs(dir); + } + + /** + * 获取data目录下的文件目录 + * */ + public static String getZipFileDir() { + String dir = getPrivateAppDir() +"/" + ZIP_FILE+"/"; + return mkdirs(dir); + } + + public static String getCacheDir() { + String dir = getPrivateAppDir() + "/"+CACHE+"/"; + return mkdirs(dir); + } + + /** + * 获取上传文件目录 + * */ + public static String getPublicUploadFileDir() { + String dir = getAppDir() + "/"+UPLOAD_FILE+"/"; + return mkdirs(dir); + } + + /** + * 获取上传文件目录 + * */ + public static String getPublicCamera2Dir() { + String dir = getAppDir() + "/"+ CAMERA_V2 +"/"; + return mkdirs(dir); + } + + + /** + * 获取图片目录 + * */ + public static String getDownloadImageDir() { + String dir = getAppDir() + "/"+DOWNLOAD_IMAGE+"/"; + return mkdirs(dir); + } + + /** + * 获取文件目录 + * */ + public static String getDownloadFileDir() { + String dir = getAppDir() + "/"+DOWNLOAD_FILE+"/"; + return mkdirs(dir); + } + + /** + * 获取图片目录 + * */ + public static String getUserFileDir() { + String dir = getAppDir() + "/"+USER_FILE+"/"; + return mkdirs(dir); + } + + /** + * 复制文件到本App目录下,并返回目标文件路径 + * + * @param srcFilePath 原文件路径 + * + * @param toPathDir 复制目标的 目录 路径 + * + * @return 目标文件路径 + * */ + public static String copyToPath(final String srcFilePath, final String toPathDir) { + + // 获取格式正确的文件名字 + String str = FileUtils.getFileName(srcFilePath); + String srcFileName = stringFilter(str); + // 目标路径 + String toPath = toPathDir + srcFileName; + // 如果用户选中的是本app目录下的文件,则返回原路径 + if (toPath.equals(srcFilePath)) { + return srcFilePath; + } + //检查目标文件路径是否重名 + String toPrivateAppPath = checkToAppPathIsExists(toPath); + boolean flag = FileUtils.copyFile(srcFilePath,toPrivateAppPath); + if (flag) { + return toPrivateAppPath; + } else { + //复制失败,返回原路径 + return srcFilePath; + } + } + + /** + * 判断目标路径是否有同名文件,若同名则修改文件名,并返回文件名 + * @param oldPath 原文件路径 + * @param toPathDir 目标路径 + * @return 目标新文件路径 + * */ + public static String checkToPathAndGetNewFileName(String oldPath, final String toPathDir) { + // 获取格式正确的文件名字 + String str = FileUtils.getFileName(oldPath); + String srcFileName = stringFilter(str); + // 目标路径 + String toPath = toPathDir + srcFileName; + //检查目标文件路径是否重名 + return checkToAppPathIsExists(toPath); + } + + /** + * 检查目标路径文件是否重名,若重名则名字后面加(i); + * */ + public static String checkToAppPathIsExists(String path) { + String toNewPath = path; + + String prefix = FileUtils.getFileNameNoExtension(path);//提取文件名字,没有后缀 + String suffix = FileUtils.getFileExtension(path);//提取文件名字后缀 + + //循环判断文件是否存在,存在就加(i),知道不存在为止 + for (int i = 1; FileUtils.isFileExists(toNewPath) && i < Integer.MAX_VALUE; i++) { + toNewPath = FileUtils.getDirName(path) + prefix + "(" + i + ")."+suffix; + } + return toNewPath; + } + + /** + * @param name 文件名字 + * @return 文件保存路径 + * */ + public static String savePath(final String name) { + //获取格式正确的文件名字 + String srcFileName = AppFileUtils.stringFilter(name); + // 获取预保存地址 + String path = AppFileUtils.getDownloadFileDir() + srcFileName; + // 检查地址是否有重名,返回不重名的地址 + return AppFileUtils.checkToAppPathIsExists(path); + } + + /** + * @param name 文件名字 + * @return 文件保存路径 + * */ + public static String saveImagePath(final String name) { + //获取格式正确的文件名字 + String srcFileName = AppFileUtils.stringFilter(name); + // 获取预保存地址 + String path = AppFileUtils.getDownloadImageDir() + srcFileName; + // 检查地址是否有重名,返回不重名的地址 + return AppFileUtils.checkToAppPathIsExists(path); + } + + + /****************************内部方法调用************************************/ + + private static String getAppDir(){ + return Environment.getExternalStorageDirectory().getPath()+"/"+APP_DIR; + } + + private static String getPrivateAppDir() { + return Environment.getExternalStorageDirectory().getPath() + "/Android/data/" + APP_PROJECT_DIR +"/"+APP_DIR; + } + + private static String mkdirs(String dir) { + File file = new File(dir); + if (!file.exists()) { + file.mkdirs(); + } + return dir; + } + + + /** + * 过滤特殊字符(\/:*?"<>|) + */ + public static String stringFilter(String str) { + if (str == null) { + return null; + } + String regEx = "[\\/:*?\"<>|]"; + Pattern p = Pattern.compile(regEx); + Matcher m = p.matcher(str); + return m.replaceAll("").trim(); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/AppUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/AppUtils.java new file mode 100644 index 00000000..d8f762c8 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/AppUtils.java @@ -0,0 +1,108 @@ +package com.bonait.bnframework.common.utils; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +import org.apache.commons.codec.binary.Base64; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +/** + * Created by LY on 2019/3/21. + * App版本信息工具类 + */ +public class AppUtils { + /** + * 获取版本名称 + * + * @param context 上下文 + * + * @return 版本名称 + */ + public static String getVersionName(Context context) { + + //获取包管理器 + PackageManager pm = context.getPackageManager(); + //获取包信息 + try { + PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0); + //返回版本号 + return packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + return null; + + } + + /** + * 获取版本号 + * + * @param context 上下文 + * + * @return 版本号 + */ + public static int getVersionCode(Context context) { + + //获取包管理器 + PackageManager pm = context.getPackageManager(); + //获取包信息 + try { + PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0); + //返回版本号 + return packageInfo.versionCode; + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + return 0; + + } + + /** + * 获取App的名称 + * + * @param context 上下文 + * + * @return 名称 + */ + public static String getAppName(Context context) { + PackageManager pm = context.getPackageManager(); + //获取包信息 + try { + PackageInfo packageInfo = pm.getPackageInfo(context.getPackageName(), 0); + //获取应用 信息 + ApplicationInfo applicationInfo = packageInfo.applicationInfo; + //获取albelRes + int labelRes = applicationInfo.labelRes; + //返回App的名称 + return context.getResources().getString(labelRes); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * 密码加密,使用SHA-256加密 + * + * @param inputStr 密码 + * + * @return 加密 + */ + public static synchronized String encryptSha256(String inputStr) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte digest[] = md.digest(inputStr.getBytes(StandardCharsets.UTF_8)); + return new String(Base64.encodeBase64(digest)); + } catch (Exception e) { + return null; + } + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/Des3Utils.java b/app/src/main/java/com/bonait/bnframework/common/utils/Des3Utils.java new file mode 100644 index 00000000..60511bc0 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/Des3Utils.java @@ -0,0 +1,98 @@ +package com.bonait.bnframework.common.utils; + + +import android.text.TextUtils; +import android.util.Base64; + +import java.security.Key; + +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; + +/** + * Created by LY on 2019/4/10. + * 3DES 加密/解密 + */ +public class Des3Utils { + + // 密钥 长度等于24,且不得小于24 + private final static String secretKey = "CW0Y&S7l1BVU2zwKop@syT92"; + // 向量 可有可无 终端后台也要约定 + private final static String iv = "01234567"; + + /** + * 3DES转变 + *

法算法名称/加密模式/填充方式

+ *

加密模式有:电子密码本模式ECB、加密块链模式CBC、加密反馈模式CFB、输出反馈模式OFB

+ *

填充方式有:NoPadding、ZerosPadding、PKCS5Padding

+ */ + private static String TripleDES_Transformation = "desede/CBC/PKCS5Padding"; + + // 加密算法类型 + private static final String TripleDES_Algorithm = "desede"; + + // 加解密统一使用的编码方式 + private final static String encoding = "utf-8"; + + /** + * 3DES加密 + * + * @param plainText 普通文本 + * @return 加密Str + */ + public static String encode(String plainText) { + if (TextUtils.isEmpty(plainText)) { + return null; + } + + try { + DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes()); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(TripleDES_Algorithm); + Key desKey = keyFactory.generateSecret(spec); + + Cipher cipher = Cipher.getInstance(TripleDES_Transformation); + IvParameterSpec ips = new IvParameterSpec(iv.getBytes()); + cipher.init(Cipher.ENCRYPT_MODE, desKey, ips); + + byte[] encryptData = cipher.doFinal(plainText.getBytes(encoding)); + + return Base64.encodeToString(encryptData, Base64.DEFAULT); + + }catch (Exception e ) { + e.printStackTrace(); + return null; + } + } + + /** + * 3DES解密 + * + * @param encryptText 加密文本 + * @return 解密Str + */ + public static String decode(String encryptText) { + if (TextUtils.isEmpty(encryptText)) { + return null; + } + + try { + DESedeKeySpec spec = new DESedeKeySpec(secretKey.getBytes()); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(TripleDES_Algorithm); + Key desKey = keyFactory.generateSecret(spec); + + Cipher cipher = Cipher.getInstance(TripleDES_Transformation); + IvParameterSpec ips = new IvParameterSpec(iv.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, desKey, ips); + + byte[] decryptData = cipher.doFinal(Base64.decode(encryptText, Base64.DEFAULT)); + + return new String(decryptData, encoding); + + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/FileUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/FileUtils.java new file mode 100644 index 00000000..9014fef2 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/FileUtils.java @@ -0,0 +1,1300 @@ +package com.bonait.bnframework.common.utils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +/** + * Created by LY on 2019/1/17. + * getFileByPath : 根据文件路径获取文件 + * isFileExists : 判断文件是否存在 + * rename : 重命名文件 + * isDir : 判断是否是目录 + * isFile : 判断是否是文件 + * createOrExistsDir : 判断目录是否存在,不存在则判断是否创建成功 + * createOrExistsFile : 判断文件是否存在,不存在则判断是否创建成功 + * createFileByDeleteOldFile: 判断文件是否存在,存在则在创建之前删除 + * copyDir : 复制目录 + * copyFile : 复制文件 + * moveDir : 移动目录 + * moveFile : 移动文件 + * deleteDir : 删除目录 + * deleteFile : 删除文件 + * listFilesInDir : 获取目录下所有文件 + * listFilesInDir : 获取目录下所有文件包括子目录 + * listFilesInDirWithFilter : 获取目录下所有后缀名为suffix的文件 + * listFilesInDirWithFilter : 获取目录下所有后缀名为suffix的文件包括子目录 + * listFilesInDirWithFilter : 获取目录下所有符合filter的文件 + * listFilesInDirWithFilter : 获取目录下所有符合filter的文件包括子目录 + * searchFileInDir : 获取目录下指定文件名的文件包括子目录 + * writeFileFromIS : 将输入流写入文件 + * writeFileFromString : 将字符串写入文件 + * readFile2List : 指定编码按行读取文件到链表中 + * readFile2String : 指定编码按行读取文件到字符串中 + * readFile2Bytes : 读取文件到字符数组中 + * getFileLastModified : 获取文件最后修改的毫秒时间戳 + * getFileCharsetSimple : 简单获取文件编码格式 + * getFileLines : 获取文件行数 + * getDirSize : 获取目录大小 + * getFileSize : 获取文件大小 + * getDirLength : 获取目录长度 + * getFileLength : 获取文件长度 + * getFileMD5 : 获取文件的MD5校验码 + * getFileMD5ToString : 获取文件的MD5校验码 + * getDirName : 根据全路径获取最长目录 + * getFileName : 根据全路径获取文件名 + * getFileNameNoExtension : 根据全路径获取文件名不带拓展名 + * getFileExtension : 根据全路径获取文件的拓展名 + * + */ +public class FileUtils { + private static final String LINE_SEP = System.getProperty("line.separator"); + + private FileUtils() { + throw new UnsupportedOperationException("u can't instantiate me..."); + } + + /** + * Return the file by path. + * + * @param filePath The path of file. + * @return the file + */ + public static File getFileByPath(final String filePath) { + return isSpace(filePath) ? null : new File(filePath); + } + + /** + * Return whether the file exists. + * + * @param filePath The path of file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isFileExists(final String filePath) { + return isFileExists(getFileByPath(filePath)); + } + + /** + * Return whether the file exists. + * + * @param file The file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isFileExists(final File file) { + return file != null && file.exists(); + } + + /** + * Rename the file. + * + * @param filePath The path of file. + * @param newName The new name of file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean rename(final String filePath, final String newName) { + return rename(getFileByPath(filePath), newName); + } + + /** + * Rename the file. + * + * @param file The file. + * @param newName The new name of file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean rename(final File file, final String newName) { + // file is null then return false + if (file == null) return false; + // file doesn't exist then return false + if (!file.exists()) return false; + // the new name is space then return false + if (isSpace(newName)) return false; + // the new name equals old name then return true + if (newName.equals(file.getName())) return true; + File newFile = new File(file.getParent() + File.separator + newName); + // the new name of file exists then return false + return !newFile.exists() + && file.renameTo(newFile); + } + + /** + * Return whether it is a directory. + * + * @param dirPath The path of directory. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isDir(final String dirPath) { + return isDir(getFileByPath(dirPath)); + } + + /** + * Return whether it is a directory. + * + * @param file The file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isDir(final File file) { + return file != null && file.exists() && file.isDirectory(); + } + + /** + * Return whether it is a file. + * + * @param filePath The path of file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isFile(final String filePath) { + return isFile(getFileByPath(filePath)); + } + + /** + * Return whether it is a file. + * + * @param file The file. + * @return {@code true}: yes
{@code false}: no + */ + public static boolean isFile(final File file) { + return file != null && file.exists() && file.isFile(); + } + + /** + * Create a directory if it doesn't exist, otherwise do nothing. + * + * @param dirPath The path of directory. + * @return {@code true}: exists or creates successfully
{@code false}: otherwise + */ + public static boolean createOrExistsDir(final String dirPath) { + return createOrExistsDir(getFileByPath(dirPath)); + } + + /** + * Create a directory if it doesn't exist, otherwise do nothing. + * + * @param file The file. + * @return {@code true}: exists or creates successfully
{@code false}: otherwise + */ + public static boolean createOrExistsDir(final File file) { + return file != null && (file.exists() ? file.isDirectory() : file.mkdirs()); + } + + /** + * Create a file if it doesn't exist, otherwise do nothing. + * + * @param filePath The path of file. + * @return {@code true}: exists or creates successfully
{@code false}: otherwise + */ + public static boolean createOrExistsFile(final String filePath) { + return createOrExistsFile(getFileByPath(filePath)); + } + + /** + * Create a file if it doesn't exist, otherwise do nothing. + * + * @param file The file. + * @return {@code true}: exists or creates successfully
{@code false}: otherwise + */ + public static boolean createOrExistsFile(final File file) { + if (file == null) return false; + if (file.exists()) return file.isFile(); + if (!createOrExistsDir(file.getParentFile())) return false; + try { + return file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + /** + * Create a file if it doesn't exist, otherwise delete old file before creating. + * + * @param filePath The path of file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean createFileByDeleteOldFile(final String filePath) { + return createFileByDeleteOldFile(getFileByPath(filePath)); + } + + /** + * Create a file if it doesn't exist, otherwise delete old file before creating. + * + * @param file The file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean createFileByDeleteOldFile(final File file) { + if (file == null) return false; + // file exists and unsuccessfully delete then return false + if (file.exists() && !file.delete()) return false; + if (!createOrExistsDir(file.getParentFile())) return false; + try { + return file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + /** + * Copy the directory. + * + * @param srcDirPath The path of source directory. + * @param destDirPath The path of destination directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyDir(final String srcDirPath, + final String destDirPath) { + return copyDir(getFileByPath(srcDirPath), getFileByPath(destDirPath)); + } + + /** + * Copy the directory. + * + * @param srcDirPath The path of source directory. + * @param destDirPath The path of destination directory. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyDir(final String srcDirPath, + final String destDirPath, + final OnReplaceListener listener) { + return copyDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); + } + + /** + * Copy the directory. + * + * @param srcDir The source directory. + * @param destDir The destination directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyDir(final File srcDir, + final File destDir) { + return copyOrMoveDir(srcDir, destDir, false); + } + + /** + * Copy the directory. + * + * @param srcDir The source directory. + * @param destDir The destination directory. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyDir(final File srcDir, + final File destDir, + final OnReplaceListener listener) { + return copyOrMoveDir(srcDir, destDir, listener, false); + } + + /** + * Copy the file. + * + * @param srcFilePath The path of source file. + * @param destFilePath The path of destination file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyFile(final String srcFilePath, + final String destFilePath) { + return copyFile(getFileByPath(srcFilePath), getFileByPath(destFilePath)); + } + + /** + * Copy the file. + * + * @param srcFilePath The path of source file. + * @param destFilePath The path of destination file. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyFile(final String srcFilePath, + final String destFilePath, + final OnReplaceListener listener) { + return copyFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); + } + + /** + * Copy the file. + * + * @param srcFile The source file. + * @param destFile The destination file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyFile(final File srcFile, + final File destFile) { + return copyOrMoveFile(srcFile, destFile, false); + } + + /** + * Copy the file. + * + * @param srcFile The source file. + * @param destFile The destination file. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean copyFile(final File srcFile, + final File destFile, + final OnReplaceListener listener) { + return copyOrMoveFile(srcFile, destFile, listener, false); + } + + /** + * Move the directory. + * + * @param srcDirPath The path of source directory. + * @param destDirPath The path of destination directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveDir(final String srcDirPath, + final String destDirPath) { + return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath)); + } + + /** + * Move the directory. + * + * @param srcDirPath The path of source directory. + * @param destDirPath The path of destination directory. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveDir(final String srcDirPath, + final String destDirPath, + final OnReplaceListener listener) { + return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), listener); + } + + /** + * Move the directory. + * + * @param srcDir The source directory. + * @param destDir The destination directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveDir(final File srcDir, + final File destDir) { + return copyOrMoveDir(srcDir, destDir, true); + } + + /** + * Move the directory. + * + * @param srcDir The source directory. + * @param destDir The destination directory. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveDir(final File srcDir, + final File destDir, + final OnReplaceListener listener) { + return copyOrMoveDir(srcDir, destDir, listener, true); + } + + /** + * Move the file. + * + * @param srcFilePath The path of source file. + * @param destFilePath The path of destination file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveFile(final String srcFilePath, + final String destFilePath) { + return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath)); + } + + /** + * Move the file. + * + * @param srcFilePath The path of source file. + * @param destFilePath The path of destination file. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveFile(final String srcFilePath, + final String destFilePath, + final OnReplaceListener listener) { + return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), listener); + } + + /** + * Move the file. + * + * @param srcFile The source file. + * @param destFile The destination file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveFile(final File srcFile, + final File destFile) { + return copyOrMoveFile(srcFile, destFile, true); + } + + /** + * Move the file. + * + * @param srcFile The source file. + * @param destFile The destination file. + * @param listener The replace listener. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean moveFile(final File srcFile, + final File destFile, + final OnReplaceListener listener) { + return copyOrMoveFile(srcFile, destFile, listener, true); + } + + private static boolean copyOrMoveDir(final File srcDir, + final File destDir, + final boolean isMove) { + return copyOrMoveDir(srcDir, destDir, new OnReplaceListener() { + @Override + public boolean onReplace() { + return true; + } + }, isMove); + } + + private static boolean copyOrMoveDir(final File srcDir, + final File destDir, + final OnReplaceListener listener, + final boolean isMove) { + if (srcDir == null || destDir == null) return false; + // destDir's path locate in srcDir's path then return false + String srcPath = srcDir.getPath() + File.separator; + String destPath = destDir.getPath() + File.separator; + if (destPath.contains(srcPath)) return false; + if (!srcDir.exists() || !srcDir.isDirectory()) return false; + if (destDir.exists()) { + if (listener == null || listener.onReplace()) {// require delete the old directory + if (!deleteAllInDir(destDir)) {// unsuccessfully delete then return false + return false; + } + } else { + return true; + } + } + if (!createOrExistsDir(destDir)) return false; + File[] files = srcDir.listFiles(); + for (File file : files) { + File oneDestFile = new File(destPath + file.getName()); + if (file.isFile()) { + if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false; + } else if (file.isDirectory()) { + if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false; + } + } + return !isMove || deleteDir(srcDir); + } + + private static boolean copyOrMoveFile(final File srcFile, + final File destFile, + final boolean isMove) { + return copyOrMoveFile(srcFile, destFile, new OnReplaceListener() { + @Override + public boolean onReplace() { + return true; + } + }, isMove); + } + + private static boolean copyOrMoveFile(final File srcFile, + final File destFile, + final OnReplaceListener listener, + final boolean isMove) { + if (srcFile == null || destFile == null) return false; + // srcFile equals destFile then return false + if (srcFile.equals(destFile)) return false; + // srcFile doesn't exist or isn't a file then return false + if (!srcFile.exists() || !srcFile.isFile()) return false; + if (destFile.exists()) { + if (listener == null || listener.onReplace()) {// require delete the old file + if (!destFile.delete()) {// unsuccessfully delete then return false + return false; + } + } else { + return true; + } + } + if (!createOrExistsDir(destFile.getParentFile())) return false; + try { + return writeFileFromIS(destFile, new FileInputStream(srcFile)) + && !(isMove && !deleteFile(srcFile)); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return false; + } + } + + /** + * Delete the directory. + * + * @param filePath The path of file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean delete(final String filePath) { + return delete(getFileByPath(filePath)); + } + + /** + * Delete the directory. + * + * @param file The file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean delete(final File file) { + if (file == null) return false; + if (file.isDirectory()) { + return deleteDir(file); + } + return deleteFile(file); + } + + /** + * Delete the directory. + * + * @param dirPath The path of directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteDir(final String dirPath) { + return deleteDir(getFileByPath(dirPath)); + } + + /** + * Delete the directory. + * + * @param dir The directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteDir(final File dir) { + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + return dir.delete(); + } + + /** + * Delete the file. + * + * @param srcFilePath The path of source file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteFile(final String srcFilePath) { + return deleteFile(getFileByPath(srcFilePath)); + } + + /** + * Delete the file. + * + * @param file The file. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteFile(final File file) { + return file != null && (!file.exists() || file.isFile() && file.delete()); + } + + /** + * Delete the all in directory. + * + * @param dirPath The path of directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteAllInDir(final String dirPath) { + return deleteAllInDir(getFileByPath(dirPath)); + } + + /** + * Delete the all in directory. + * + * @param dir The directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteAllInDir(final File dir) { + return deleteFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return true; + } + }); + } + + /** + * Delete all files in directory. + * + * @param dirPath The path of directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteFilesInDir(final String dirPath) { + return deleteFilesInDir(getFileByPath(dirPath)); + } + + /** + * Delete all files in directory. + * + * @param dir The directory. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteFilesInDir(final File dir) { + return deleteFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return pathname.isFile(); + } + }); + } + + /** + * Delete all files that satisfy the filter in directory. + * + * @param dirPath The path of directory. + * @param filter The filter. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteFilesInDirWithFilter(final String dirPath, + final FileFilter filter) { + return deleteFilesInDirWithFilter(getFileByPath(dirPath), filter); + } + + /** + * Delete all files that satisfy the filter in directory. + * + * @param dir The directory. + * @param filter The filter. + * @return {@code true}: success
{@code false}: fail + */ + public static boolean deleteFilesInDirWithFilter(final File dir, final FileFilter filter) { + if (dir == null) return false; + // dir doesn't exist then return true + if (!dir.exists()) return true; + // dir isn't a directory then return false + if (!dir.isDirectory()) return false; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (filter.accept(file)) { + if (file.isFile()) { + if (!file.delete()) return false; + } else if (file.isDirectory()) { + if (!deleteDir(file)) return false; + } + } + } + } + return true; + } + + /** + * Return the files in directory. + *

Doesn't traverse subdirectories

+ * + * @param dirPath The path of directory. + * @return the files in directory + */ + public static List listFilesInDir(final String dirPath) { + return listFilesInDir(dirPath, false); + } + + /** + * Return the files in directory. + *

Doesn't traverse subdirectories

+ * + * @param dir The directory. + * @return the files in directory + */ + public static List listFilesInDir(final File dir) { + return listFilesInDir(dir, false); + } + + /** + * Return the files in directory. + * + * @param dirPath The path of directory. + * @param isRecursive True to traverse subdirectories, false otherwise. + * @return the files in directory + */ + public static List listFilesInDir(final String dirPath, final boolean isRecursive) { + return listFilesInDir(getFileByPath(dirPath), isRecursive); + } + + /** + * Return the files in directory. + * + * @param dir The directory. + * @param isRecursive True to traverse subdirectories, false otherwise. + * @return the files in directory + */ + public static List listFilesInDir(final File dir, final boolean isRecursive) { + return listFilesInDirWithFilter(dir, new FileFilter() { + @Override + public boolean accept(File pathname) { + return true; + } + }, isRecursive); + } + + /** + * Return the files that satisfy the filter in directory. + *

Doesn't traverse subdirectories

+ * + * @param dirPath The path of directory. + * @param filter The filter. + * @return the files that satisfy the filter in directory + */ + public static List listFilesInDirWithFilter(final String dirPath, + final FileFilter filter) { + return listFilesInDirWithFilter(getFileByPath(dirPath), filter, false); + } + + /** + * Return the files that satisfy the filter in directory. + *

Doesn't traverse subdirectories

+ * + * @param dir The directory. + * @param filter The filter. + * @return the files that satisfy the filter in directory + */ + public static List listFilesInDirWithFilter(final File dir, + final FileFilter filter) { + return listFilesInDirWithFilter(dir, filter, false); + } + + /** + * Return the files that satisfy the filter in directory. + * + * @param dirPath The path of directory. + * @param filter The filter. + * @param isRecursive True to traverse subdirectories, false otherwise. + * @return the files that satisfy the filter in directory + */ + public static List listFilesInDirWithFilter(final String dirPath, + final FileFilter filter, + final boolean isRecursive) { + return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive); + } + + /** + * Return the files that satisfy the filter in directory. + * + * @param dir The directory. + * @param filter The filter. + * @param isRecursive True to traverse subdirectories, false otherwise. + * @return the files that satisfy the filter in directory + */ + public static List listFilesInDirWithFilter(final File dir, + final FileFilter filter, + final boolean isRecursive) { + if (!isDir(dir)) return null; + List list = new ArrayList<>(); + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (filter.accept(file)) { + list.add(file); + } + if (isRecursive && file.isDirectory()) { + //noinspection ConstantConditions + list.addAll(listFilesInDirWithFilter(file, filter, true)); + } + } + } + return list; + } + + /** + * Return the time that the file was last modified. + * + * @param filePath The path of file. + * @return the time that the file was last modified + */ + + public static long getFileLastModified(final String filePath) { + return getFileLastModified(getFileByPath(filePath)); + } + + /** + * Return the time that the file was last modified. + * + * @param file The file. + * @return the time that the file was last modified + */ + public static long getFileLastModified(final File file) { + if (file == null) return -1; + return file.lastModified(); + } + + /** + * Return the charset of file simply. + * + * @param filePath The path of file. + * @return the charset of file simply + */ + public static String getFileCharsetSimple(final String filePath) { + return getFileCharsetSimple(getFileByPath(filePath)); + } + + /** + * Return the charset of file simply. + * + * @param file The file. + * @return the charset of file simply + */ + public static String getFileCharsetSimple(final File file) { + int p = 0; + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + p = (is.read() << 8) + is.read(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + switch (p) { + case 0xefbb: + return "UTF-8"; + case 0xfffe: + return "Unicode"; + case 0xfeff: + return "UTF-16BE"; + default: + return "GBK"; + } + } + + /** + * Return the number of lines of file. + * + * @param filePath The path of file. + * @return the number of lines of file + */ + public static int getFileLines(final String filePath) { + return getFileLines(getFileByPath(filePath)); + } + + /** + * Return the number of lines of file. + * + * @param file The file. + * @return the number of lines of file + */ + public static int getFileLines(final File file) { + int count = 1; + InputStream is = null; + try { + is = new BufferedInputStream(new FileInputStream(file)); + byte[] buffer = new byte[1024]; + int readChars; + if (LINE_SEP.endsWith("\n")) { + while ((readChars = is.read(buffer, 0, 1024)) != -1) { + for (int i = 0; i < readChars; ++i) { + if (buffer[i] == '\n') ++count; + } + } + } else { + while ((readChars = is.read(buffer, 0, 1024)) != -1) { + for (int i = 0; i < readChars; ++i) { + if (buffer[i] == '\r') ++count; + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return count; + } + + /** + * Return the size of directory. + * + * @param dirPath The path of directory. + * @return the size of directory + */ + public static String getDirSize(final String dirPath) { + return getDirSize(getFileByPath(dirPath)); + } + + /** + * Return the size of directory. + * + * @param dir The directory. + * @return the size of directory + */ + public static String getDirSize(final File dir) { + long len = getDirLength(dir); + return len == -1 ? "" : byte2FitMemorySize(len); + } + + /** + * Return the length of file. + * + * @param filePath The path of file. + * @return the length of file + */ + public static String getFileSize(final String filePath) { + long len = getFileLength(filePath); + return len == -1 ? "" : byte2FitMemorySize(len); + } + + /** + * Return the length of file. + * + * @param file The file. + * @return the length of file + */ + public static String getFileSize(final File file) { + long len = getFileLength(file); + return len == -1 ? "" : byte2FitMemorySize(len); + } + + /** + * Return the length of directory. + * + * @param dirPath The path of directory. + * @return the length of directory + */ + public static long getDirLength(final String dirPath) { + return getDirLength(getFileByPath(dirPath)); + } + + /** + * Return the length of directory. + * + * @param dir The directory. + * @return the length of directory + */ + public static long getDirLength(final File dir) { + if (!isDir(dir)) return -1; + long len = 0; + File[] files = dir.listFiles(); + if (files != null && files.length != 0) { + for (File file : files) { + if (file.isDirectory()) { + len += getDirLength(file); + } else { + len += file.length(); + } + } + } + return len; + } + + /** + * Return the length of file. + * + * @param filePath The path of file. + * @return the length of file + */ + public static long getFileLength(final String filePath) { + boolean isURL = filePath.matches("[a-zA-z]+://[^\\s]*"); + if (isURL) { + try { + HttpURLConnection conn = (HttpURLConnection) new URL(filePath).openConnection(); + conn.setRequestProperty("Accept-Encoding", "identity"); + conn.connect(); + if (conn.getResponseCode() == 200) { + return conn.getContentLength(); + } + return -1; + } catch (IOException e) { + e.printStackTrace(); + } + } + return getFileLength(getFileByPath(filePath)); + } + + /** + * Return the length of file. + * + * @param file The file. + * @return the length of file + */ + public static long getFileLength(final File file) { + if (!isFile(file)) return -1; + return file.length(); + } + + /** + * Return the MD5 of file. + * + * @param filePath The path of file. + * @return the md5 of file + */ + public static String getFileMD5ToString(final String filePath) { + File file = isSpace(filePath) ? null : new File(filePath); + return getFileMD5ToString(file); + } + + /** + * Return the MD5 of file. + * + * @param file The file. + * @return the md5 of file + */ + public static String getFileMD5ToString(final File file) { + return bytes2HexString(getFileMD5(file)); + } + + /** + * Return the MD5 of file. + * + * @param filePath The path of file. + * @return the md5 of file + */ + public static byte[] getFileMD5(final String filePath) { + return getFileMD5(getFileByPath(filePath)); + } + + /** + * Return the MD5 of file. + * + * @param file The file. + * @return the md5 of file + */ + public static byte[] getFileMD5(final File file) { + if (file == null) return null; + DigestInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(file); + MessageDigest md = MessageDigest.getInstance("MD5"); + dis = new DigestInputStream(fis, md); + byte[] buffer = new byte[1024 * 256]; + while (true) { + if (!(dis.read(buffer) > 0)) break; + } + md = dis.getMessageDigest(); + return md.digest(); + } catch (NoSuchAlgorithmException | IOException e) { + e.printStackTrace(); + } finally { + try { + if (dis != null) { + dis.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return null; + } + + /** + * Return the file's path of directory. + * + * @param file The file. + * @return the file's path of directory + */ + public static String getDirName(final File file) { + if (file == null) return ""; + return getDirName(file.getAbsolutePath()); + } + + /** + * Return the file's path of directory. + * + * @param filePath The path of file. + * @return the file's path of directory + */ + public static String getDirName(final String filePath) { + if (isSpace(filePath)) return ""; + int lastSep = filePath.lastIndexOf(File.separator); + return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1); + } + + /** + * Return the name of file. + * + * @param file The file. + * @return the name of file + */ + public static String getFileName(final File file) { + if (file == null) return ""; + return getFileName(file.getAbsolutePath()); + } + + /** + * Return the name of file. + * + * @param filePath The path of file. + * @return the name of file + */ + public static String getFileName(final String filePath) { + if (isSpace(filePath)) return ""; + int lastSep = filePath.lastIndexOf(File.separator); + return lastSep == -1 ? filePath : filePath.substring(lastSep + 1); + } + + /** + * Return the name of file without extension. + * + * @param file The file. + * @return the name of file without extension + */ + public static String getFileNameNoExtension(final File file) { + if (file == null) return ""; + return getFileNameNoExtension(file.getPath()); + } + + /** + * Return the name of file without extension. + * + * @param filePath The path of file. + * @return the name of file without extension + */ + public static String getFileNameNoExtension(final String filePath) { + if (isSpace(filePath)) return ""; + int lastPoi = filePath.lastIndexOf('.'); + int lastSep = filePath.lastIndexOf(File.separator); + if (lastSep == -1) { + return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi)); + } + if (lastPoi == -1 || lastSep > lastPoi) { + return filePath.substring(lastSep + 1); + } + return filePath.substring(lastSep + 1, lastPoi); + } + + /** + * Return the extension of file. + * + * @param file The file. + * @return the extension of file + */ + public static String getFileExtension(final File file) { + if (file == null) return ""; + return getFileExtension(file.getPath()); + } + + /** + * Return the extension of file. + * + * @param filePath The path of file. + * @return the extension of file + */ + public static String getFileExtension(final String filePath) { + if (isSpace(filePath)) return ""; + int lastPoi = filePath.lastIndexOf('.'); + int lastSep = filePath.lastIndexOf(File.separator); + if (lastPoi == -1 || lastSep >= lastPoi) return ""; + return filePath.substring(lastPoi + 1); + } + + /////////////////////////////////////////////////////////////////////////// + // interface + /////////////////////////////////////////////////////////////////////////// + + public interface OnReplaceListener { + boolean onReplace(); + } + + /////////////////////////////////////////////////////////////////////////// + // other utils methods + /////////////////////////////////////////////////////////////////////////// + + private static final char HEX_DIGITS[] = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + private static String bytes2HexString(final byte[] bytes) { + if (bytes == null) return ""; + int len = bytes.length; + if (len <= 0) return ""; + char[] ret = new char[len << 1]; + for (int i = 0, j = 0; i < len; i++) { + ret[j++] = HEX_DIGITS[bytes[i] >> 4 & 0x0f]; + ret[j++] = HEX_DIGITS[bytes[i] & 0x0f]; + } + return new String(ret); + } + + private static String byte2FitMemorySize(final long byteNum) { + if (byteNum < 0) { + return "shouldn't be less than zero!"; + } else if (byteNum < 1024) { + return String.format(Locale.getDefault(), "%.3fB", (double) byteNum); + } else if (byteNum < 1048576) { + return String.format(Locale.getDefault(), "%.3fKB", (double) byteNum / 1024); + } else if (byteNum < 1073741824) { + return String.format(Locale.getDefault(), "%.3fMB", (double) byteNum / 1048576); + } else { + return String.format(Locale.getDefault(), "%.3fGB", (double) byteNum / 1073741824); + } + } + + private static boolean isSpace(final String s) { + if (s == null) return true; + for (int i = 0, len = s.length(); i < len; ++i) { + if (!Character.isWhitespace(s.charAt(i))) { + return false; + } + } + return true; + } + + private static boolean writeFileFromIS(final File file, + final InputStream is) { + OutputStream os = null; + try { + os = new BufferedOutputStream(new FileOutputStream(file)); + byte data[] = new byte[8192]; + int len; + while ((len = is.read(data, 0, 8192)) != -1) { + os.write(data, 0, len); + } + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + try { + if (os != null) { + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/KeyboardToolUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/KeyboardToolUtils.java new file mode 100644 index 00000000..10d198fa --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/KeyboardToolUtils.java @@ -0,0 +1,157 @@ +package com.bonait.bnframework.common.utils; + +import android.app.Activity; +import android.content.Context; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +/** + * Created by LY on 2019/3/28. + * 软键盘工具类 + */ +public class KeyboardToolUtils { + /** + * 避免输入法面板遮挡 + *

在manifest.xml中activity中设置

+ *

android:windowSoftInputMode="stateVisible|adjustResize"

+ */ + + /** + * 动态隐藏软键盘 + * + * @param activity activity + */ + public static void hideSoftInput(Activity activity) { + View view = activity.getWindow().peekDecorView(); + if (view != null) { + InputMethodManager inputManger = (InputMethodManager) activity + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManger != null) { + inputManger.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + } + + /** + * 点击隐藏软键盘 + * + * @param activity + * @param view + */ + public static void hideKeyboard(Activity activity, View view) { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + + /** + * 动态隐藏软键盘 + * + * @param context 上下文 + * @param edit 输入框 + */ + public static void hideSoftInput(Context context, EditText edit) { + edit.clearFocus(); + InputMethodManager inputManger = (InputMethodManager) context + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManger != null) { + inputManger.hideSoftInputFromWindow(edit.getWindowToken(), 0); + } + } + + /** + * 点击屏幕空白区域隐藏软键盘(方法1) + *

在onTouch中处理,未获焦点则隐藏

+ *

参照以下注释代码

+ */ + public static void clickBlankArea2HideSoftInput0() { + /* + @Override + public boolean onTouchEvent (MotionEvent event){ + if (null != this.getCurrentFocus()) { + InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); + return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0); + } + return super.onTouchEvent(event); + } + */ + } + + /** + * 点击屏幕空白区域隐藏软键盘(方法2) + *

根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘

+ *

需重写dispatchTouchEvent

+ *

参照以下注释代码

+ */ + public static void clickBlankArea2HideSoftInput1() { + /* + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + View v = getCurrentFocus(); + if (isShouldHideKeyboard(v, ev)) { + hideKeyboard(v.getWindowToken()); + } + } + return super.dispatchTouchEvent(ev); + } + // 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘 + private boolean isShouldHideKeyboard(View v, MotionEvent event) { + if (v != null && (v instanceof EditText)) { + int[] l = {0, 0}; + v.getLocationInWindow(l); + int left = l[0], + top = l[1], + bottom = top + v.getHeight(), + right = left + v.getWidth(); + return !(event.getX() > left && event.getX() < right + && event.getY() > top && event.getY() < bottom); + } + return false; + } + // 获取InputMethodManager,隐藏软键盘 + private void hideKeyboard(IBinder token) { + if (token != null) { + InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + im.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS); + } + } + */ + } + + /** + * 动态显示软键盘 + * + * @param context 上下文 + * @param edit 输入框 + */ + public static void showSoftInput(Context context, EditText edit) { + edit.setFocusable(true); + edit.setFocusableInTouchMode(true); + edit.requestFocus(); + InputMethodManager inputManager = (InputMethodManager) context + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManager != null) { + inputManager.showSoftInput(edit, 0); + } + } + + /** + * 切换键盘显示与否状态 + * + * @param context 上下文 + * @param edit 输入框 + */ + public static void toggleSoftInput(Context context, EditText edit) { + edit.setFocusable(true); + edit.setFocusableInTouchMode(true); + edit.requestFocus(); + InputMethodManager inputManager = (InputMethodManager) context + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManager != null) { + inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/MediaFileUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/MediaFileUtils.java new file mode 100644 index 00000000..364cd4f0 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/MediaFileUtils.java @@ -0,0 +1,353 @@ +package com.bonait.bnframework.common.utils; + +import java.util.HashMap; +import java.util.Locale; + +/** + * Created by LY on 2019/1/24. + * 文件类型工具类 + */ +public class MediaFileUtils { + //音频文件类型 + public static final int FILE_TYPE_MP3 = 1; + public static final int FILE_TYPE_M4A = 2; + public static final int FILE_TYPE_WAV = 3; + public static final int FILE_TYPE_AMR = 4; + public static final int FILE_TYPE_AWB = 5; + public static final int FILE_TYPE_WMA = 6; + public static final int FILE_TYPE_OGG = 7; + public static final int FILE_TYPE_AAC = 8; + public static final int FILE_TYPE_MKA = 9; + public static final int FILE_TYPE_FLAC = 10; + private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3; + private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC; + + // MIDI文件类型 + public static final int FILE_TYPE_MID = 11; + public static final int FILE_TYPE_SMF = 12; + public static final int FILE_TYPE_IMY = 13; + private static final int FIRST_MIDI_FILE_TYPE = FILE_TYPE_MID; + private static final int LAST_MIDI_FILE_TYPE = FILE_TYPE_IMY; + + //视频文件类型 + public static final int FILE_TYPE_MP4 = 21; + public static final int FILE_TYPE_M4V = 22; + public static final int FILE_TYPE_3GPP = 23; + public static final int FILE_TYPE_3GPP2 = 24; + public static final int FILE_TYPE_WMV = 25; + public static final int FILE_TYPE_ASF = 26; + public static final int FILE_TYPE_MKV = 27; + public static final int FILE_TYPE_MP2TS = 28; + public static final int FILE_TYPE_AVI = 29; + public static final int FILE_TYPE_WEBM = 30; + private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4; + private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_WEBM; + + //更多视频文件类型 + public static final int FILE_TYPE_MP2PS = 200; + private static final int FIRST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS; + private static final int LAST_VIDEO_FILE_TYPE2 = FILE_TYPE_MP2PS; + + //图像文件类型 + public static final int FILE_TYPE_JPEG = 31; + public static final int FILE_TYPE_GIF = 32; + public static final int FILE_TYPE_PNG = 33; + public static final int FILE_TYPE_BMP = 34; + public static final int FILE_TYPE_WBMP = 35; + public static final int FILE_TYPE_WEBP = 36; + private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG; + private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WEBP; + + // Playlist file types + public static final int FILE_TYPE_M3U = 41; + public static final int FILE_TYPE_PLS = 42; + public static final int FILE_TYPE_WPL = 43; + public static final int FILE_TYPE_HTTPLIVE = 44; + private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U; + private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_HTTPLIVE; + // Drm文件类型 + public static final int FILE_TYPE_FL = 51; + private static final int FIRST_DRM_FILE_TYPE = FILE_TYPE_FL; + private static final int LAST_DRM_FILE_TYPE = FILE_TYPE_FL; + //其他流行的文件类型 + public static final int FILE_TYPE_TEXT = 100; + public static final int FILE_TYPE_HTML = 101; + public static final int FILE_TYPE_PDF = 102; + public static final int FILE_TYPE_XML = 103; + public static final int FILE_TYPE_MS_WORD = 104; + public static final int FILE_TYPE_MS_EXCEL = 105; + public static final int FILE_TYPE_MS_POWERPOINT = 106; + public static final int FILE_TYPE_MS_DOT = 108; + public static final int FILE_TYPE_WPS_DOCX = 109; + public static final int FILE_TYPE_WPS_DOTX = 110; + public static final int FILE_TYPE_WPS_XLSX = 111; + public static final int FILE_TYPE_WPS_XLTX = 112; + public static final int FILE_TYPE_WPS_PPTX = 113; + public static final int FILE_TYPE_WPS_POTX = 114; + public static final int FILE_TYPE_WPS_PPSX = 115; + private static final int FIRST_DOC_FILE_TYPE = FILE_TYPE_TEXT; + private static final int LAST_DOC_FILE_TYPE = FILE_TYPE_WPS_PPSX; + + public static final int FILE_TYPE_ZIP = 300; + public static final int FILE_TYPE_RAR = 301; + private static final int FIRST_COMPRESSION_FILE_TYPE = FILE_TYPE_ZIP; + private static final int LAST_COMPRESSION_FILE_TYPE = FILE_TYPE_RAR; + + + public static class MediaFileType { + public final int fileType; + public final String mimeType; + + MediaFileType(int fileType, String mimeType) { + this.fileType = fileType; + this.mimeType = mimeType; + } + } + + private static final HashMap sFileTypeMap + = new HashMap(); + private static final HashMap sMimeTypeMap + = new HashMap(); + + static void addFileType(String extension, int fileType, String mimeType) { + sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType)); + sMimeTypeMap.put(mimeType, fileType); + } + static { + addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg"); + addFileType("MPGA", FILE_TYPE_MP3, "audio/mpeg"); + addFileType("M4A", FILE_TYPE_M4A, "audio/mp4"); + addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav"); + addFileType("AMR", FILE_TYPE_AMR, "audio/amr"); + addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb"); + addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma"); + addFileType("OGG", FILE_TYPE_OGG, "audio/ogg"); + addFileType("OGG", FILE_TYPE_OGG, "application/ogg"); + addFileType("OGA", FILE_TYPE_OGG, "application/ogg"); + addFileType("AAC", FILE_TYPE_AAC, "audio/aac"); + addFileType("AAC", FILE_TYPE_AAC, "audio/aac-adts"); + addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska"); + + addFileType("MID", FILE_TYPE_MID, "audio/midi"); + addFileType("MIDI", FILE_TYPE_MID, "audio/midi"); + addFileType("XMF", FILE_TYPE_MID, "audio/midi"); + addFileType("RTTTL", FILE_TYPE_MID, "audio/midi"); + addFileType("SMF", FILE_TYPE_SMF, "audio/sp-midi"); + addFileType("IMY", FILE_TYPE_IMY, "audio/imelody"); + addFileType("RTX", FILE_TYPE_MID, "audio/midi"); + addFileType("OTA", FILE_TYPE_MID, "audio/midi"); + addFileType("MXMF", FILE_TYPE_MID, "audio/midi"); + + addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg"); + addFileType("MPG", FILE_TYPE_MP4, "video/mpeg"); + addFileType("MP4", FILE_TYPE_MP4, "video/mp4"); + addFileType("M4V", FILE_TYPE_M4V, "video/mp4"); + addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp"); + addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp"); + addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2"); + addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2"); + addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska"); + addFileType("WEBM", FILE_TYPE_WEBM, "video/webm"); + addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts"); + addFileType("AVI", FILE_TYPE_AVI, "video/avi"); + addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv"); + addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf"); + + addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg"); + addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg"); + addFileType("GIF", FILE_TYPE_GIF, "image/gif"); + addFileType("PNG", FILE_TYPE_PNG, "image/png"); + addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp"); + addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp"); + addFileType("WEBP", FILE_TYPE_WEBP, "image/webp"); + + addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl"); + addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl"); + addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls"); + addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl"); + addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl"); + addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/mpegurl"); + addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/x-mpegurl"); + addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl"); + + addFileType("TXT", FILE_TYPE_TEXT, "text/plain"); + addFileType("HTM", FILE_TYPE_HTML, "text/html"); + addFileType("HTML", FILE_TYPE_HTML, "text/html"); + addFileType("PDF", FILE_TYPE_PDF, "application/pdf"); + addFileType("XML", FILE_TYPE_XML, "text/xml"); + addFileType("DOC", FILE_TYPE_MS_WORD, "application/msword"); + addFileType("DOT", FILE_TYPE_MS_DOT, "application/msword"); + addFileType("DOCX", FILE_TYPE_WPS_DOCX, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + addFileType("DOTX", FILE_TYPE_WPS_DOTX, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"); + addFileType("XLSX", FILE_TYPE_WPS_XLSX, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + addFileType("XLTX", FILE_TYPE_WPS_XLTX, "application/vnd.openxmlformats-officedocument.spreadsheetml.template"); + addFileType("PPTX", FILE_TYPE_WPS_PPTX, "application/vnd.openxmlformats-officedocument.presentationml.presentation"); + addFileType("POTX", FILE_TYPE_WPS_POTX, "application/vnd.openxmlformats-officedocument.presentationml.template"); + addFileType("PPSX", FILE_TYPE_WPS_PPSX, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"); + + addFileType("XLS", FILE_TYPE_MS_EXCEL, "application/vnd.ms-excel"); + addFileType("PPT", FILE_TYPE_MS_POWERPOINT, "application/mspowerpoint"); + + addFileType("ZIP", FILE_TYPE_ZIP, "application/zip"); + addFileType("RAR", FILE_TYPE_RAR, "application/rar"); + + addFileType("FLAC", FILE_TYPE_FLAC, "audio/flac"); + addFileType("MPG", FILE_TYPE_MP2PS, "video/mp2p"); + addFileType("MPEG", FILE_TYPE_MP2PS, "video/mp2p"); + } + + /** + * check is audio or not + * @param fileType file type integer value + * @return if is audio type , return true;otherwise , return false + */ + public static boolean isAudioFileType(int fileType) { + return ((fileType >= FIRST_AUDIO_FILE_TYPE && + fileType <= LAST_AUDIO_FILE_TYPE) || + (fileType >= FIRST_MIDI_FILE_TYPE && + fileType <= LAST_MIDI_FILE_TYPE)); + } + + /** + * check is video or not + * @param fileType file type integer value + * @return if is video type , return true ; otherwise , return false + */ + public static boolean isVideoFileType(int fileType) { + return (fileType >= FIRST_VIDEO_FILE_TYPE && + fileType <= LAST_VIDEO_FILE_TYPE) + || (fileType >= FIRST_VIDEO_FILE_TYPE2 && + fileType <= LAST_VIDEO_FILE_TYPE2); + } + + /** + * check is image or not + * @param fileType file type integer value + * @return if is image type , return true ; otherwise , return false ; + */ + public static boolean isImageFileType(int fileType) { + return (fileType >= FIRST_IMAGE_FILE_TYPE && + fileType <= LAST_IMAGE_FILE_TYPE); + } + + /** + * check is playlist or not + * @param fileType file type integer value + * @return if is playlist type , return true ; otherwise , return false ; + */ + public static boolean isPlayListFileType(int fileType) { + return (fileType >= FIRST_PLAYLIST_FILE_TYPE && + fileType <= LAST_PLAYLIST_FILE_TYPE); + } + + /** + * check is drm or not + * @param fileType file type integer value + * @return if is drm type , return true ; otherwise , return false ; + */ + public static boolean isDrmFileType(int fileType) { + return (fileType >= FIRST_DRM_FILE_TYPE && + fileType <= LAST_DRM_FILE_TYPE); + } + + /** + * 检查是否是文本类型 + * */ + public static boolean isWordFileType(int fileType) { + return (fileType >= FIRST_DOC_FILE_TYPE && + fileType <= LAST_DOC_FILE_TYPE); + } + + /** + * 检查是否是压缩文件类型 + * */ + public static boolean isCompressionFileType(int fileType) { + return (fileType >= FIRST_COMPRESSION_FILE_TYPE && + fileType <= LAST_COMPRESSION_FILE_TYPE); + } + + /** + * get file's extension by file' path 按文件路径获取文件的扩展名 + * @param path file's path + * @return MediaFileType if the given file extension exist , or null + */ + public static MediaFileType getFileType(String path) { + int lastDot = path.lastIndexOf('.'); + if (lastDot < 0) + return null; + return sFileTypeMap.get(path.substring(lastDot + 1).toUpperCase(Locale.ROOT)); + } + + /** + * 按后缀名返回MediaFileType + * + * @param suffix 文件后缀名 + * + * @return MediaFileType if the given file extension exist , or null + * + * */ + public static MediaFileType getFileTypeBySuffix(String suffix) { + + // toUpperCase(Locale.ROOT) 转换为大写字符串 + return sFileTypeMap.get(suffix.toUpperCase(Locale.ROOT)); + } + + public static String getMimeTypeBySuffix(String suffix) { + MediaFileType mediaFileType = sFileTypeMap.get(suffix.toUpperCase(Locale.ROOT)); + return (mediaFileType == null ? null : mediaFileType.mimeType); + } + + /** + * check the given mime type is mime type media or not 检查给定的mime类型是否是mime类型的媒体 + * @param mimeType mime type to check + * @return if the given mime type is mime type media,return true ;otherwise , false + */ + public static boolean isMimeTypeMedia(String mimeType) { + int fileType = getFileTypeForMimeType(mimeType); + return isAudioFileType(fileType) || isVideoFileType(fileType) + || isImageFileType(fileType) || isPlayListFileType(fileType); + } + + /** + * generates a title based on file name + * @param path file's path + * + * @return file'name without extension + */ + public static String getFileTitle(String path) { + // extract file name after last slash + int lastSlash = path.lastIndexOf('/'); + if (lastSlash >= 0) { + lastSlash++; + if (lastSlash < path.length()) { + path = path.substring(lastSlash); + } + } + // truncate the file extension (if any) + int lastDot = path.lastIndexOf('.'); + if (lastDot > 0) { + path = path.substring(0, lastDot); + } + return path; + } + + /** + * get mine type integer value 根据mime类型获取mime类型整数值 + * @param mimeType mime type to get + * @return return mime type value if exist ;or zero value if not exist + */ + public static int getFileTypeForMimeType(String mimeType) { + Integer value = sMimeTypeMap.get(mimeType); + return (value == null ? 0 : value.intValue()); + } + + /** + * get file's mime type base on path 根据文件路径获取文件的mime类型 + * @param path file path + * @return return mime type if exist , or null + */ + public static String getMimeTypeForFile(String path) { + MediaFileType mediaFileType = getFileType(path); + return (mediaFileType == null ? null : mediaFileType.mimeType); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/NetworkUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/NetworkUtils.java new file mode 100644 index 00000000..aff7b5c2 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/NetworkUtils.java @@ -0,0 +1,109 @@ +package com.bonait.bnframework.common.utils; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.os.Build; + +/** + * Created by LY on 2019/1/4. + */ +public class NetworkUtils { + + /** + * 检测当的网络(WLAN、4G/3G/2G)状态,兼容Android 6.0以下 + * @param context Context + * @return true 表示网络可用 + */ + public static boolean isNetworkConnected(Context context) { + boolean result = false; + try { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (cm != null) { + NetworkCapabilities capabilities = cm.getNetworkCapabilities(cm.getActiveNetwork()); + if (capabilities != null) { + if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + result = true; + } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + result = true; + } + } + } + } else { + if (cm != null) { + NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + if (activeNetwork != null) { + // connected to the internet + if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI) { + result = true; + } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) { + result = true; + } + } + } + } + + } catch (Exception e) { + return false; + } + + return result; + } + + /** + * 判断是否是移动网络连接 + * */ + public static boolean isActiveNetworkMobile(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager != null) { + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_MOBILE; + } + return false; + } + + /** + * 判断是否是wifi + * */ + public static boolean isActiveNetworkWifi(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager != null) { + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI; + } + return false; + } + + /** + * @deprecated 请先使用 {@link NetworkUtils#isNetworkConnected(Context)} 方法 + * + * 检测当的网络(WLAN、4G/3G/2G)状态 + * + * @param context Context + * @return true 表示网络可用 + */ + @Deprecated + public static boolean checkNet(Context context) { + + try { + ConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivity != null) { + + NetworkInfo info = connectivity.getActiveNetworkInfo(); + if (info != null && info.isConnected()) { + + /*if (info.getState() == NetworkInfo.State.CONNECTED) { + return true; + }*/ + NetworkCapabilities networkCapabilities = connectivity.getNetworkCapabilities(connectivity.getActiveNetwork()); + return networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); + } + } + } catch (Exception e) { + return false; + } + return false; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/PreferenceUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/PreferenceUtils.java new file mode 100644 index 00000000..004d1d25 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/PreferenceUtils.java @@ -0,0 +1,139 @@ +package com.bonait.bnframework.common.utils; + +import android.content.Context; +import android.content.SharedPreferences; + +/** + * Created by LY on 2019/3/21. + */ +public class PreferenceUtils { + private static SharedPreferences sharedPreferences; + + /** + * 初始化SharedPreferences + * */ + public static void initPreference(Context context,String name,int mode) { + if (sharedPreferences == null) { + sharedPreferences = context.getSharedPreferences(name, mode); + } + } + + /** + * 存String + * @param key 键 + * @param value 值 + */ + public static void setString(String key, String value) { + sharedPreferences.edit().putString(key,value).apply(); + } + + /** + * 取String + * @param key 键 + * @param devalue 默认值 + * @return String + */ + public static String getString(String key, String devalue) { + return sharedPreferences.getString(key,devalue); + } + + /** + * 存Float + * @param key 键 + * @param value 值 + */ + public static void setFloat(String key, float value) { + sharedPreferences.edit().putFloat(key,value).apply(); + } + + /** + * 取Float + * @param key 键 + * @param devalue 默认值 + * @return Float + */ + public static Float getFloat(String key, float devalue){ + return sharedPreferences.getFloat(key,devalue); + } + + /** + * 存boolean + * @param key 键 + * @param value 值 + */ + public static void setBoolean(String key, boolean value) { + sharedPreferences.edit().putBoolean(key,value).apply(); + } + + /** + * 取boolean + * @param key 键 + * @param defValue 默认值 + * @return Boolean + */ + public static boolean getBoolean(String key, boolean defValue) { + return sharedPreferences.getBoolean(key,defValue); + } + + /** + * 存int + * @param key 键 + * @param value 值 + */ + public static void setInt(String key, int value) { + sharedPreferences.edit().putInt(key,value).apply(); + } + + /** + * 存int + * @param key 键 + * @param value 值 + */ + public static void setInt(String key, Integer value) { + sharedPreferences.edit().putInt(key,(Integer) value).apply(); + } + + /** + * 取int + * @param key 键 + * @param defValue 默认值 + * @return Int + */ + public static int getInt(String key, int defValue) { + return sharedPreferences.getInt(key,defValue); + } + + /** + * 存Long + * @param key 键 + * @param value 值 + */ + public static void setLong(String key, int value) { + sharedPreferences.edit().putLong(key,value).apply(); + } + + /** + * 取Long + * @param key 键 + * @param defValue 默认值 + * @return Long + */ + public static Long getLong(String key, int defValue) { + return sharedPreferences.getLong(key,defValue); + } + + /** + * 清空一个 + * @param key 键 + */ + public static void remove(String key) { + sharedPreferences.edit().remove(key).apply(); + } + + /** + * 清空所有 + */ + public static void removeAll() { + sharedPreferences.edit().clear().apply(); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/ToastUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/ToastUtils.java new file mode 100644 index 00000000..ddec8579 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/ToastUtils.java @@ -0,0 +1,278 @@ +package com.bonait.bnframework.common.utils; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; +import android.support.annotation.CheckResult; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.MainApplication; + +/** + * Created by LY on 2019/3/19. + */ +public class ToastUtils { + @ColorInt + private static final int DEFAULT_TEXT_COLOR = Color.parseColor("#FFFFFF"); + + @ColorInt + private static final int ERROR_COLOR = Color.parseColor("#F44336"); //#FD4C5B + + @ColorInt + private static final int INFO_COLOR = Color.parseColor("#3F51B5"); + + @ColorInt + private static final int SUCCESS_COLOR = Color.parseColor("#388E3C"); + + @ColorInt + private static final int WARNING_COLOR = Color.parseColor("#FFA900"); + + private static final String TOAST_TYPEFACE = "sans-serif-condensed"; + + private static Toast currentToast; + + //***********************普通 使用ApplicationContext 方法*********************// + private static long mExitTime; + + public static void normal(@NonNull String message) { + normal(MainApplication.getContext(), message, Toast.LENGTH_SHORT, null, false).show(); + } + + public static void normal(@NonNull String message, Drawable icon) { + normal(MainApplication.getContext(), message, Toast.LENGTH_SHORT, icon, true).show(); + } + + public static void normal(@NonNull String message, int duration) { + normal(MainApplication.getContext(), message, duration, null, false).show(); + } + + public static void normal(@NonNull String message, int duration, Drawable icon) { + normal(MainApplication.getContext(), message, duration, icon, true).show(); + } + + public static Toast normal(@NonNull String message, int duration, Drawable icon, boolean withIcon) { + return custom(MainApplication.getContext(), message, icon, DEFAULT_TEXT_COLOR, duration, withIcon); + } + + public static void warning(@NonNull String message) { + warning(MainApplication.getContext(), message, Toast.LENGTH_SHORT, true).show(); + } + + public static void warning(@NonNull String message, int duration) { + warning(MainApplication.getContext(), message, duration, true).show(); + } + + public static Toast warning(@NonNull String message, int duration, boolean withIcon) { + return custom(MainApplication.getContext(), message, getDrawable(MainApplication.getContext(), R.mipmap.ic_error_outline_white_48dp), DEFAULT_TEXT_COLOR, WARNING_COLOR, duration, withIcon, true); + } + + public static void info(@NonNull String message) { + info(MainApplication.getContext(), message, Toast.LENGTH_SHORT, true).show(); + } + + public static void info(@NonNull String message, int duration) { + info(MainApplication.getContext(), message, duration, true).show(); + } + + public static Toast info(@NonNull String message, int duration, boolean withIcon) { + return custom(MainApplication.getContext(), message, getDrawable(MainApplication.getContext(), R.mipmap.ic_info_outline_white_48dp), DEFAULT_TEXT_COLOR, INFO_COLOR, duration, withIcon, true); + } + + public static void success(@NonNull String message) { + success(MainApplication.getContext(), message, Toast.LENGTH_SHORT, true).show(); + } + + public static void success(@NonNull String message, int duration) { + success(MainApplication.getContext(), message, duration, true).show(); + } + + public static Toast success(@NonNull String message, int duration, boolean withIcon) { + return custom(MainApplication.getContext(), message, getDrawable(MainApplication.getContext(), R.mipmap.ic_check_white_48dp), DEFAULT_TEXT_COLOR, SUCCESS_COLOR, duration, withIcon, true); + } + + public static void error(@NonNull String message) { + error(MainApplication.getContext(), message, Toast.LENGTH_SHORT, true).show(); + } + //===========================================使用ApplicationContext 方法========================= + + //*******************************************常规方法******************************************** + + public static void error(@NonNull String message, int duration) { + error(MainApplication.getContext(), message, duration, true).show(); + } + + public static Toast error(@NonNull String message, int duration, boolean withIcon) { + return custom(MainApplication.getContext(), message, getDrawable(MainApplication.getContext(), R.mipmap.ic_clear_white_48dp), DEFAULT_TEXT_COLOR, ERROR_COLOR, duration, withIcon, true); + } + + @CheckResult + public static Toast normal(@NonNull Context context, @NonNull String message) { + return normal(context, message, Toast.LENGTH_SHORT, null, false); + } + + @CheckResult + public static Toast normal(@NonNull Context context, @NonNull String message, Drawable icon) { + return normal(context, message, Toast.LENGTH_SHORT, icon, true); + } + + @CheckResult + public static Toast normal(@NonNull Context context, @NonNull String message, int duration) { + return normal(context, message, duration, null, false); + } + + @CheckResult + public static Toast normal(@NonNull Context context, @NonNull String message, int duration, Drawable icon) { + return normal(context, message, duration, icon, true); + } + + @CheckResult + public static Toast normal(@NonNull Context context, @NonNull String message, int duration, Drawable icon, boolean withIcon) { + return custom(context, message, icon, DEFAULT_TEXT_COLOR, duration, withIcon); + } + + @CheckResult + public static Toast warning(@NonNull Context context, @NonNull String message) { + return warning(context, message, Toast.LENGTH_SHORT, true); + } + + @CheckResult + public static Toast warning(@NonNull Context context, @NonNull String message, int duration) { + return warning(context, message, duration, true); + } + + @CheckResult + public static Toast warning(@NonNull Context context, @NonNull String message, int duration, boolean withIcon) { + return custom(context, message, getDrawable(context, R.mipmap.ic_error_outline_white_48dp), DEFAULT_TEXT_COLOR, WARNING_COLOR, duration, withIcon, true); + } + + @CheckResult + public static Toast info(@NonNull Context context, @NonNull String message) { + return info(context, message, Toast.LENGTH_SHORT, true); + } + + @CheckResult + public static Toast info(@NonNull Context context, @NonNull String message, int duration) { + return info(context, message, duration, true); + } + + @CheckResult + public static Toast info(@NonNull Context context, @NonNull String message, int duration, boolean withIcon) { + return custom(context, message, getDrawable(context, R.mipmap.ic_info_outline_white_48dp), DEFAULT_TEXT_COLOR, INFO_COLOR, duration, withIcon, true); + } + + @CheckResult + public static Toast success(@NonNull Context context, @NonNull String message) { + return success(context, message, Toast.LENGTH_SHORT, true); + } + + @CheckResult + public static Toast success(@NonNull Context context, @NonNull String message, int duration) { + return success(context, message, duration, true); + } + + @CheckResult + public static Toast success(@NonNull Context context, @NonNull String message, int duration, boolean withIcon) { + return custom(context, message, getDrawable(context, R.mipmap.ic_check_white_48dp), DEFAULT_TEXT_COLOR, SUCCESS_COLOR, duration, withIcon, true); + } + + @CheckResult + public static Toast error(@NonNull Context context, @NonNull String message) { + return error(context, message, Toast.LENGTH_SHORT, true); + } + + //===========================================常规方法============================================ + + @CheckResult + public static Toast error(@NonNull Context context, @NonNull String message, int duration) { + return error(context, message, duration, true); + } + + @CheckResult + public static Toast error(@NonNull Context context, @NonNull String message, int duration, boolean withIcon) { + return custom(context, message, getDrawable(context, R.mipmap.ic_clear_white_48dp), DEFAULT_TEXT_COLOR, ERROR_COLOR, duration, withIcon, true); + } + + @CheckResult + public static Toast custom(@NonNull Context context, @NonNull String message, Drawable icon, @ColorInt int textColor, int duration, boolean withIcon) { + return custom(context, message, icon, textColor, -1, duration, withIcon, false); + } + + //*******************************************内需方法******************************************** + + @CheckResult + public static Toast custom(@NonNull Context context, @NonNull String message, @DrawableRes int iconRes, @ColorInt int textColor, @ColorInt int tintColor, int duration, boolean withIcon, boolean shouldTint) { + return custom(context, message, getDrawable(context, iconRes), textColor, tintColor, duration, withIcon, shouldTint); + } + + @CheckResult + public static Toast custom(@NonNull Context context, @NonNull String message, Drawable icon, @ColorInt int textColor, @ColorInt int tintColor, int duration, boolean withIcon, boolean shouldTint) { + if (currentToast == null) { + currentToast = new Toast(context); + } + final View toastLayout = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.toast_layout, null); + final ImageView toastIcon = toastLayout.findViewById(R.id.toast_icon); + final TextView toastTextView = toastLayout.findViewById(R.id.toast_text); + Drawable drawableFrame; + + if (shouldTint) { + drawableFrame = tint9PatchDrawableFrame(context, tintColor); + } else { + drawableFrame = getDrawable(context, R.mipmap.toast_frame); + } + setBackground(toastLayout, drawableFrame); + + if (withIcon) { + if (icon == null) { + throw new IllegalArgumentException("Avoid passing 'icon' as null if 'withIcon' is set to true"); + } + setBackground(toastIcon, icon); + } else { + toastIcon.setVisibility(View.GONE); + } + + toastTextView.setTextColor(textColor); + toastTextView.setText(message); + toastTextView.setTypeface(Typeface.create(TOAST_TYPEFACE, Typeface.NORMAL)); + + currentToast.setView(toastLayout); + currentToast.setDuration(duration); + return currentToast; + } + + public static final Drawable tint9PatchDrawableFrame(@NonNull Context context, @ColorInt int tintColor) { + final NinePatchDrawable toastDrawable = (NinePatchDrawable) getDrawable(context, R.mipmap.toast_frame); + toastDrawable.setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN)); + return toastDrawable; + } + //===========================================内需方法============================================ + + public static final void setBackground(@NonNull View view, Drawable drawable) { + view.setBackground(drawable); + } + + public static Drawable getDrawable(@NonNull Context context, @DrawableRes int id) { + return context.getDrawable(id); + } + + + public static boolean doubleClickExit() { + if ((System.currentTimeMillis() - mExitTime) > 2000) { + ToastUtils.normal("再按一次退出"); + mExitTime = System.currentTimeMillis(); + return false; + } + return true; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/UpdateAppUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/UpdateAppUtils.java new file mode 100644 index 00000000..e80e6c8b --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/UpdateAppUtils.java @@ -0,0 +1,148 @@ +package com.bonait.bnframework.common.utils; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.support.v4.content.FileProvider; + +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.http.callback.files.FileProgressDialogCallBack; +import com.bonait.bnframework.common.http.callback.json.JsonDialogCallback; +import com.bonait.bnframework.modules.mine.model.UpdateAppPo; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.model.Response; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import java.io.File; + +/** + * Created by LY on 2019/4/1. + */ +public class UpdateAppUtils { + + /** + * apk下载地址 + */ + private static String downloadUrl = ""; + + /** + * 获取服务器apk下载地址ID + */ + private static String serviceApkId = ""; + /** + * 当前版本号 + */ + private static int myVersionCode = 0; + /** + * 服务器的版本号码 + */ + private static int serviceVersionCode = 0; + /** + * 服务器的版本号名称 + */ + private static String serviceVersionName = ""; + /** + * 更新说明 + */ + private static String description = ""; + + /** + * 更新APP版本入口 + */ + public static void updateApp(Context context) { + //获取当前app版本号 + myVersionCode = AppUtils.getVersionCode(context); + //获取json转成Gson,并获取版本等信息 + doPost(context); + } + + /** + * 请求后台服务器,检查apk版本 + */ + private static void doPost(final Context context) { + String getNewVersionUrl = Constants.SERVICE_IP + "/iandroid/appVersionAction!getNewVersion.do"; + OkGo.post(getNewVersionUrl) + .tag(context) + .execute(new JsonDialogCallback(context) { + @Override + public void onSuccess(Response response) { + UpdateAppPo updateAppPo = response.body(); + if (updateAppPo != null) { + serviceVersionCode = updateAppPo.getVersion(); + description = updateAppPo.getDescription(); + serviceApkId = updateAppPo.getApkId(); + //获取apk下载地址 + String url = Constants.SERVICE_IP + "/file-download?fileId="; + downloadUrl = url + serviceApkId; + // 判断Apk是否是最新版本 + if (myVersionCode < serviceVersionCode) { + showUpdateDialog(context); + } else { + ToastUtils.info("当前版本已是最新版本"); + } + + } + } + }); + } + + /** + * 弹出下载对话框,里面有一些更新版本的信息 + */ + private static void showUpdateDialog(final Context context) { + new QMUIDialog.MessageDialogBuilder(context) + .setCancelable(false) + .setTitle("发现新版本") + .setMessage(description) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction("去更新", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + downloadApk(context); + dialog.dismiss(); + } + }) + .create(AlertDialogUtils.mCurrentDialogStyle) + .show(); + } + + private static void downloadApk(final Context context) { + OkGo.get(downloadUrl) + .tag(context) + .execute(new FileProgressDialogCallBack(context) { + @Override + public void onSuccess(Response response) { + File file = response.body(); + String absolutePath = file.getAbsolutePath(); + installApk(context, absolutePath); + } + }); + } + + /** + * 跳转apk安装界面 + */ + public static void installApk(Context context, String filePath) { + Intent i = new Intent(Intent.ACTION_VIEW); + File file = new File(filePath); + if (file.length() > 0 && file.exists() && file.isFile()) { + //判断是否是AndroidN以及更高的版本 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + Uri contentUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileProvider", file); + i.setDataAndType(contentUri, "application/vnd.android.package-archive"); + } else { + i.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + context.startActivity(i); + } + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/utils/UriUtils.java b/app/src/main/java/com/bonait/bnframework/common/utils/UriUtils.java new file mode 100644 index 00000000..04cab253 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/utils/UriUtils.java @@ -0,0 +1,195 @@ +package com.bonait.bnframework.common.utils; + +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.support.v4.content.FileProvider; + +import java.io.File; + +/** + * Created by LY on 2019/1/29. + */ +public class UriUtils { + private UriUtils() { + } + + /** + * 根据file文件获取该文件的uri,适配Android7.0以下版本 + * + * @param mContext 上下文 + * @param file 需要获取uri的文件 + * + * @return 该文件的uri + * */ + public static Uri getUriForFile(Context mContext, File file) { + Uri fileUri = null; + // Android 版本判断 + if (Build.VERSION.SDK_INT >= 24) { + fileUri = getUriForFile24(mContext, file); + } else { + fileUri = Uri.fromFile(file); + } + return fileUri; + } + + /** + * 根据file文件获取该文件的uri,Android7.0以上可直接调用 + * + * @param mContext 上下文 + * @param file 需要获取uri的文件 + * + * @return 该文件的uri + * */ + public static Uri getUriForFile24(Context mContext, File file) { + return FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".fileProvider", file); + } + + /** + * intent 设置uri和type + * */ + public static void setIntentDataAndType(Context mContext, + Intent intent, + String type, + File file, + boolean writeAble) { + if (Build.VERSION.SDK_INT >= 24) { + intent.setDataAndType(getUriForFile(mContext, file), type); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (writeAble) { + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + } else { + intent.setDataAndType(Uri.fromFile(file), type); + } + } + + /** + * 根据uri获取path + * */ + public static String getFilePathByUri(final Context context, final Uri uri) { + + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + + // TODO handle non-primary volumes + } + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + + return getDataColumn(context, contentUri, null, null); + } + // MediaProvider + else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[] { + split[1] + }; + + return getDataColumn(context, contentUri, selection, selectionArgs); + } + } + // MediaStore (and general) + else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(context, uri, null, null); + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + public static String getDataColumn(Context context, Uri uri, String selection, + String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = { + column + }; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, + null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/view/ClearEditTextView.java b/app/src/main/java/com/bonait/bnframework/common/view/ClearEditTextView.java new file mode 100644 index 00000000..1e985c69 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/view/ClearEditTextView.java @@ -0,0 +1,163 @@ +package com.bonait.bnframework.common.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.AppCompatEditText; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.Animation; +import android.view.animation.CycleInterpolator; +import android.view.animation.TranslateAnimation; + +import com.bonait.bnframework.R; + +/** + * Created by LY on 2019/4/24. + * + */ +public class ClearEditTextView extends AppCompatEditText implements View.OnFocusChangeListener, TextWatcher { + /** + * 删除按钮的引用 + */ + private Drawable mClearDrawable; + /** + * 控件是否有焦点 + */ + private boolean hasFocus; + + public ClearEditTextView(Context context) { + this(context,null); + // super(context); + // this.context = context; + // init(); + } + public ClearEditTextView(Context context, AttributeSet attrs){ + //这里构造方法也很重要,不加这个很多属性不能再XML里面定义 + this(context, attrs, android.R.attr.editTextStyle); + } + + public ClearEditTextView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + private void init() { + + //获取EditText的DrawableRight,假如没有设置我们就使用默认的图片 + mClearDrawable = getCompoundDrawables()[2]; + if (mClearDrawable == null) { + mClearDrawable = getResources().getDrawable(R.drawable.delete_selector,null); + } + mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); + //默认设置隐藏图标 + setClearIconVisible(false); + //设置焦点改变的监听 + setOnFocusChangeListener(this); + //设置输入框里面内容发生改变的监听 + addTextChangedListener(this); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + + if (mClearDrawable != null && event.getAction() == MotionEvent.ACTION_UP) { + int x = (int) event.getX(); + //判断触摸点是否在水平范围内 + boolean isInnerWidth = (x > (getWidth() - getTotalPaddingRight())) && + (x < (getWidth() - getPaddingRight())); + //获取删除图标的边界,返回一个Rect对象 + Rect rect = mClearDrawable.getBounds(); + //获取删除图标的高度 + int height = rect.height(); + int y = (int) event.getY(); + //计算图标底部到控件底部的距离 + int distance = (getHeight() - height) / 2; + //判断触摸点是否在竖直范围内(可能会有点误差) + //触摸点的纵坐标在distance到(distance+图标自身的高度)之内,则视为点中删除图标 + boolean isInnerHeight = (y > distance) && (y < (distance + height)); + if (isInnerHeight && isInnerWidth) { + this.setText(""); + } + } + return super.onTouchEvent(event); + } + + /** + * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去 + * + * @param visible 图标是否隐藏 + */ + private void setClearIconVisible(boolean visible) { + Drawable right = visible ? mClearDrawable : null; + setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], + right, getCompoundDrawables()[3]); + } + + /** + * 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏 + */ + @Override + public void onFocusChange(View v, boolean hasFocus) { + this.hasFocus = hasFocus; + if (hasFocus) { + setClearIconVisible(getText().length() > 0); + } else { + setClearIconVisible(false); + } + } + + /*@Override + protected void onSelectionChanged(int selStart, int selEnd) { + super.onSelectionChanged(selStart, selEnd); + //光标首次获取焦点是在最后面,之后操作就是按照点击的位置移动光标 + if (isEnabled() && hasFocus() && hasFocusable()) { + setSelection(selEnd); + } else { + setSelection(getText().length()); + } + }*/ + + /** + * 当输入框里面内容发生变化的时候回调的方法 + */ + @Override + public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { + if (hasFocus) { + setClearIconVisible(text.length() > 0); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) { + + } + + /** + * 设置晃动动画 + */ + public void setShakeAnimation() { + this.setAnimation(shakeAnimation(5)); + } + + /** + * 晃动动画 + * + * @param counts 1秒钟晃动多少下 + * @return 动画 + */ + public static Animation shakeAnimation(int counts) { + Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0); + translateAnimation.setInterpolator(new CycleInterpolator(counts)); + translateAnimation.setDuration(1000); + return translateAnimation; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/common/view/QMAutoDialogBuilderView.java b/app/src/main/java/com/bonait/bnframework/common/view/QMAutoDialogBuilderView.java new file mode 100644 index 00000000..2b7eb837 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/common/view/QMAutoDialogBuilderView.java @@ -0,0 +1,60 @@ +package com.bonait.bnframework.common.view; + +import android.content.Context; +import android.support.v4.content.ContextCompat; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.bonait.bnframework.R; +import com.qmuiteam.qmui.util.QMUIDisplayHelper; +import com.qmuiteam.qmui.util.QMUIResHelper; +import com.qmuiteam.qmui.util.QMUIViewHelper; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; + +/** + * Created by LY on 2019/3/25. + */ +public class QMAutoDialogBuilderView extends QMUIDialog.AutoResizeDialogBuilder { + + private Context mContext; + private EditText mEditText; + + public QMAutoDialogBuilderView(Context context) { + super(context); + mContext = context; + } + + public EditText getEditText() { + return mEditText; + } + + @Override + public View onBuildContent(QMUIDialog dialog, ScrollView parent) { + LinearLayout layout = new LinearLayout(mContext); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setLayoutParams(new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + int padding = QMUIDisplayHelper.dp2px(mContext, 20); + layout.setPadding(padding, padding, padding, padding); + mEditText = new EditText(mContext); + QMUIViewHelper.setBackgroundKeepingPadding(mEditText, QMUIResHelper.getAttrDrawable(mContext, R.attr.qmui_list_item_bg_with_border_bottom)); + mEditText.setHint("输入框"); + LinearLayout.LayoutParams editTextLP = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, QMUIDisplayHelper.dpToPx(50)); + editTextLP.bottomMargin = QMUIDisplayHelper.dp2px(mContext, 15); + mEditText.setLayoutParams(editTextLP); + layout.addView(mEditText); + TextView textView = new TextView(mContext); + textView.setLineSpacing(QMUIDisplayHelper.dp2px(mContext, 4), 1.0f); + textView.setText("观察聚焦输入框后,键盘升起降下时 dialog 的高度自适应变化。\n\n" + + "QMUI Android 的设计目的是用于辅助快速搭建一个具备基本设计还原效果的 Android 项目," + + "同时利用自身提供的丰富控件及兼容处理,让开发者能专注于业务需求而无需耗费精力在基础代码的设计上。" + + "不管是新项目的创建,或是已有项目的维护,均可使开发效率和项目质量得到大幅度提升。"); + textView.setTextColor(ContextCompat.getColor(mContext, R.color.app_color_description)); + textView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + layout.addView(textView); + return layout; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/manager/ActivityLifecycleManager.java b/app/src/main/java/com/bonait/bnframework/manager/ActivityLifecycleManager.java new file mode 100644 index 00000000..c1a7a016 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/manager/ActivityLifecycleManager.java @@ -0,0 +1,232 @@ +package com.bonait.bnframework.manager; + +import android.app.Activity; +import android.app.Application; +import android.content.pm.ActivityInfo; +import android.os.Bundle; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Created by LY on 2019/4/3. + */ +public class ActivityLifecycleManager implements Application.ActivityLifecycleCallbacks { + + /** + * 维护Activity 的list + */ + private static List activityList = Collections.synchronizedList(new LinkedList()); + //private List activityList = new LinkedList(); + + + /*单例模式静态内部类*/ + private static class SingletonHolder{ + private static ActivityLifecycleManager instance = new ActivityLifecycleManager(); + } + private ActivityLifecycleManager(){ + } + + public static ActivityLifecycleManager get(){ + return SingletonHolder.instance; + } + + /** + * 开启ActivityLifecycleCallbacks接口回调 + */ + public void init(Application application){ + application.registerActivityLifecycleCallbacks(get()); + } + + + + /** + * @param activity 作用说明 :添加一个activity到管理里 + */ + public void pushActivity(Activity activity) { + activityList.add(activity); + } + + /** + * @param activity 作用说明 :删除一个activity在管理里 + */ + public void popActivity(Activity activity) { + activityList.remove(activity); + } + + /** + * get current Activity 获取当前Activity(栈中最后一个压入的) + */ + public Activity currentActivity() { + if (activityList == null|| activityList.isEmpty()) { + return null; + } + return activityList.get(activityList.size()-1); + } + + /** + * 结束当前Activity(栈中最后一个压入的) + */ + public void finishCurrentActivity() { + if (activityList == null|| activityList.isEmpty()) { + return; + } + Activity activity = activityList.get(activityList.size()-1); + finishActivity(activity); + } + + /** + * 结束指定的Activity + */ + public void finishActivity(Activity activity) { + if (activityList == null|| activityList.isEmpty()) { + return; + } + if (activity != null) { + activityList.remove(activity); + activity.finish(); + activity = null; + } + } + + /** + * 结束指定类名的Activity + */ + public void finishActivity(Class cls) { + if (activityList == null|| activityList.isEmpty()) { + return; + } + for (Activity activity : activityList) { + if (activity.getClass().equals(cls)) { + finishActivity(activity); + } + } + } + + /** + * 按照指定类名找到activity + * + */ + public Activity findActivity(Class cls) { + Activity targetActivity = null; + if (activityList != null) { + for (Activity activity : activityList) { + if (activity.getClass().equals(cls)) { + targetActivity = activity; + break; + } + } + } + return targetActivity; + } + + /** + * @return 作用说明 :获取当前最顶部activity的实例 + */ + public Activity getTopActivity() { + Activity mBaseActivity; + synchronized (activityList) { + final int size = activityList.size() - 1; + if (size < 0) { + return null; + } + mBaseActivity = activityList.get(size); + } + return mBaseActivity; + + } + + /** + * @return 作用说明 :获取当前最顶部的acitivity 名字 + */ + public String getTopActivityName() { + Activity mBaseActivity; + synchronized (activityList) { + final int size = activityList.size() - 1; + if (size < 0) { + return null; + } + mBaseActivity = activityList.get(size); + } + return mBaseActivity.getClass().getName(); + } + + /** + * 结束所有Activity + */ + public void finishAllActivity() { + if (activityList == null) { + return; + } + for (Activity activity : activityList) { + activity.finish(); + } + activityList.clear(); + } + + /** + * 退出应用程序 + */ + public void appExit() { + try { + finishAllActivity(); + System.exit(0); + } catch (Exception e) { + e.getStackTrace(); + } + } + + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + /* + * 监听到 Activity创建事件 将该 Activity 加入list + */ + pushActivity(activity); + // 设置禁止随屏幕旋转界面 + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + @Override + public void onActivityStarted(Activity activity) { + + } + + @Override + public void onActivityResumed(Activity activity) { + + } + + @Override + public void onActivityPaused(Activity activity) { + + } + + @Override + public void onActivityStopped(Activity activity) { + + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + if (null==activityList||activityList.isEmpty()) { + return; + } + if (activityList.contains(activity)) { + /* + * 监听到 Activity销毁事件 将该Activity 从list中移除 + */ + popActivity(activity); + } + + //横竖屏切换或配置改变时, Activity 会被重新创建实例, 但 Bundle 中的基础数据会被保存下来,移除该数据是为了保证重新创建的实例可以正常工作 + // 暂时没用到保存toolbar + //activity.getIntent().removeExtra("isInitToolbar"); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigation2Activity.java b/app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigation2Activity.java new file mode 100644 index 00000000..211f333c --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigation2Activity.java @@ -0,0 +1,129 @@ +package com.bonait.bnframework.modules.home.activity; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.BottomNavigationView; +import android.support.v4.view.ViewPager; +import android.view.KeyEvent; +import android.view.MenuItem; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.common.base.BaseActivity; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.bonait.bnframework.modules.home.fragment.Home1Fragment; +import com.bonait.bnframework.modules.home.fragment.Home2Fragment; +import com.bonait.bnframework.modules.home.adapter.FragmentAdapter; +import com.bonait.bnframework.modules.mine.fragment.MyFragment; +import com.lzy.okgo.OkGo; +import com.qmuiteam.qmui.widget.QMUIViewPager; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class BottomNavigation2Activity extends BaseActivity { + + + @BindView(R.id.navigation) + BottomNavigationView bottomNavigationView; + @BindView(R.id.viewpager) + QMUIViewPager viewPager; + + private MenuItem menuItem; + private long exitTime = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bottom_navigation2); + ButterKnife.bind(this); + + initFragment(); + viewPager.addOnPageChangeListener(pageChangeListener); + // 设置viewPager缓存多少个fragment + viewPager.setOffscreenPageLimit(3); + bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); + } + + /** + * viewPager里添加fragment + */ + private void initFragment() { + FragmentAdapter fragmentAdapter = new FragmentAdapter(getSupportFragmentManager()); + fragmentAdapter.addFragment(new Home1Fragment()); + fragmentAdapter.addFragment(new Home2Fragment()); + fragmentAdapter.addFragment(new MyFragment()); + viewPager.setAdapter(fragmentAdapter); + } + + //-------------------------配置viewPager与fragment关联----------------------------// + + /** + * 配置bottom底部菜单栏监听器,手指点击底部菜单监听 + */ + private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener + = new BottomNavigationView.OnNavigationItemSelectedListener() { + + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem item) { + switch (item.getItemId()) { + case R.id.bottom_navigation_1: + viewPager.setCurrentItem(0); + return true; + case R.id.bottom_navigation_2: + viewPager.setCurrentItem(1); + return true; + case R.id.bottom_navigation_3: + viewPager.setCurrentItem(2); + return true; + } + return false; + } + }; + + + /** + * 配置ViewPager监听器,手指滑动监听 + */ + private ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + MenuItem menuItem = bottomNavigationView.getMenu().getItem(position); + } + + @Override + public void onPageSelected(int position) { + menuItem = bottomNavigationView.getMenu().getItem(position); + menuItem.setChecked(true); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }; + + @Override + protected boolean canDragBack() { + return viewPager.getCurrentItem() == 0; + } + + /** + * 重写返回键,实现双击退出程序效果 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (System.currentTimeMillis() - exitTime > 2000) { + ToastUtils.normal("再按一次退出程序"); + exitTime = System.currentTimeMillis(); + } else { + OkGo.getInstance().cancelAll(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + ActivityLifecycleManager.get().appExit(); + } + return true; + } + return super.onKeyDown(keyCode, event); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigationActivity.java b/app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigationActivity.java new file mode 100644 index 00000000..be538b53 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigationActivity.java @@ -0,0 +1,243 @@ +package com.bonait.bnframework.modules.home.activity; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewPager; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.common.base.BaseActivity; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.bonait.bnframework.modules.home.fragment.Home1Fragment; +import com.bonait.bnframework.modules.home.fragment.Home2Fragment; +import com.bonait.bnframework.modules.mine.fragment.MyFragment; +import com.lzy.okgo.OkGo; +import com.qmuiteam.qmui.util.QMUIResHelper; +import com.qmuiteam.qmui.widget.QMUIPagerAdapter; +import com.qmuiteam.qmui.widget.QMUITabSegment; +import com.qmuiteam.qmui.widget.QMUIViewPager; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class BottomNavigationActivity extends BaseActivity { + + @BindView(R.id.main_view_pager) + QMUIViewPager mViewPager; + @BindView(R.id.main_tabs) + QMUITabSegment mTabSegment; + + private Context context; + private long exitTime = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bottom_navigation); + ButterKnife.bind(this); + + context = this; + + // 初始化tabs + initTabs(); + // 初始化viewPager,并填充fragment + initPagers(); + } + + /** + * 初始化tab + * */ + private void initTabs() { + int normalColor = QMUIResHelper.getAttrColor(context, R.attr.qmui_config_color_gray_6); + int selectColor = QMUIResHelper.getAttrColor(context, R.attr.qmui_config_color_blue); + mTabSegment.setDefaultNormalColor(normalColor); + mTabSegment.setDefaultSelectedColor(selectColor); + + QMUITabSegment.Tab home = new QMUITabSegment.Tab( + ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component), + ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_component_selected), + "主页", false + ); + + QMUITabSegment.Tab Lab = new QMUITabSegment.Tab( + ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util), + ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_util_selected), + "功能", false + ); + QMUITabSegment.Tab My = new QMUITabSegment.Tab( + ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_lab), + ContextCompat.getDrawable(context, R.mipmap.icon_tabbar_lab_selected), + "我的", false + ); + mTabSegment.addTab(home) + .addTab(Lab) + .addTab(My); + } + + /** + * 初始化viewPager并添加fragment + * */ + private void initPagers() { + QMUIPagerAdapter pagerAdapter = new QMUIPagerAdapter() { + private FragmentTransaction mCurrentTransaction; + private Fragment mCurrentPrimaryItem = null; + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == ((Fragment) object).getView(); + } + + @Override + public int getCount() { + return 3; + } + + @SuppressLint("CommitTransaction") + @Override + protected Object hydrate(ViewGroup container, int position) { + String name = makeFragmentName(container.getId(), position); + if (mCurrentTransaction == null) { + mCurrentTransaction = getSupportFragmentManager() + .beginTransaction(); + } + Fragment fragment = getSupportFragmentManager().findFragmentByTag(name); + if(fragment != null){ + return fragment; + } + switch (position) { + case 0: + return new Home1Fragment(); + case 1: + return new Home2Fragment(); + case 2: + return new MyFragment(); + default: + return new Home1Fragment(); + } + } + + @SuppressLint("CommitTransaction") + @Override + protected void populate(ViewGroup container, Object item, int position) { + String name = makeFragmentName(container.getId(), position); + if (mCurrentTransaction == null) { + mCurrentTransaction = getSupportFragmentManager() + .beginTransaction(); + } + Fragment fragment = getSupportFragmentManager().findFragmentByTag(name); + if (fragment != null) { + mCurrentTransaction.attach(fragment); + if(fragment.getView() != null && fragment.getView().getWidth() == 0){ + fragment.getView().requestLayout(); + } + } else { + fragment = (Fragment) item; + mCurrentTransaction.add(container.getId(), fragment, name); + } + if (fragment != mCurrentPrimaryItem) { + fragment.setMenuVisibility(false); + fragment.setUserVisibleHint(false); + } + } + + @SuppressLint("CommitTransaction") + @Override + protected void destroy(ViewGroup container, int position, Object object) { + if (mCurrentTransaction == null) { + mCurrentTransaction = getSupportFragmentManager() + .beginTransaction(); + } + mCurrentTransaction.detach((Fragment) object); + } + + @Override + public void startUpdate(ViewGroup container) { + if (container.getId() == View.NO_ID) { + throw new IllegalStateException("ViewPager with adapter " + this + + " requires a view id"); + } + } + + @Override + public void finishUpdate(ViewGroup container) { + if (mCurrentTransaction != null) { + mCurrentTransaction.commitNowAllowingStateLoss(); + mCurrentTransaction = null; + } + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + Fragment fragment = (Fragment) object; + if (fragment != mCurrentPrimaryItem) { + if (mCurrentPrimaryItem != null) { + mCurrentPrimaryItem.setMenuVisibility(false); + mCurrentPrimaryItem.setUserVisibleHint(false); + } + if (fragment != null) { + fragment.setMenuVisibility(true); + fragment.setUserVisibleHint(true); + } + mCurrentPrimaryItem = fragment; + } + } + + private String makeFragmentName(int viewId, long id) { + return BottomNavigationActivity.class.getSimpleName() + ":" + viewId + ":" + id; + } + }; + mViewPager.setAdapter(pagerAdapter); + mViewPager.setOffscreenPageLimit(3); + mViewPager.addOnPageChangeListener(pageChangeListener); + mTabSegment.setupWithViewPager(mViewPager,false); + } + + /** + * 配置ViewPager监听器,手指滑动监听 + */ + private ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }; + + @Override + protected boolean canDragBack() { + return mViewPager.getCurrentItem() == 0; + } + + /** + * 重写返回键,实现双击退出程序效果 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (System.currentTimeMillis() - exitTime > 2000) { + ToastUtils.normal("再按一次退出程序"); + exitTime = System.currentTimeMillis(); + } else { + OkGo.getInstance().cancelAll(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + ActivityLifecycleManager.get().appExit(); + } + return true; + } + return super.onKeyDown(keyCode, event); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/home/adapter/FragmentAdapter.java b/app/src/main/java/com/bonait/bnframework/modules/home/adapter/FragmentAdapter.java new file mode 100644 index 00000000..74139c9e --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/home/adapter/FragmentAdapter.java @@ -0,0 +1,42 @@ +package com.bonait.bnframework.modules.home.adapter; + +import android.support.annotation.NonNull; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by LY on 2019/3/26. + */ +public class FragmentAdapter extends FragmentPagerAdapter { + private List fragments = new ArrayList<>(); + + public FragmentAdapter(FragmentManager fm) { + super(fm); + + } + + @Override + public Fragment getItem(int position) { + return fragments.get(position); + } + + @Override + public int getCount() { + return fragments.size(); + } + + public void addFragment(Fragment fragment) { + fragments.add(fragment); + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + return super.instantiateItem(container, position); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home1Fragment.java b/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home1Fragment.java new file mode 100644 index 00000000..7402c04c --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home1Fragment.java @@ -0,0 +1,83 @@ +package com.bonait.bnframework.modules.home.fragment; + + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; +import android.view.LayoutInflater; +import android.view.View; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.common.base.BaseFragment; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.orhanobut.logger.Logger; +import com.qmuiteam.qmui.widget.QMUITopBar; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * A simple {@link Fragment} subclass. + */ +public class Home1Fragment extends BaseFragment { + + @BindView(R.id.topbar) + QMUITopBar mTopBar; + + public Home1Fragment() { + } + + @Override + protected View onCreateView() { + View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_home1, null); + ButterKnife.bind(this, root); + + return root; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Logger.d("第一页创建"); + initTopBar(); + } + + @OnClick(R.id.button) + public void onViewClicked() { + ToastUtils.info("主页"); + } + + private void initTopBar() { + mTopBar.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.app_color_theme_4)); + mTopBar.setTitle("快捷点菜界面"); + } + + /*private void initTopBar() { + mTopBar.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.app_color_theme_4)); + + mTopBar.setTitle("沉浸式状态栏示例"); + }*/ + + @Override + public void onDestroy() { + super.onDestroy(); + Logger.d("第一页销毁"); + } + + /** + * 当在activity设置viewPager + BottomNavigation + fragment时, + * 为防止viewPager左滑动切换界面,与fragment左滑返回上一界面冲突引起闪退问题, + * 必须加上此方法,禁止fragment左滑返回上一界面。 + * + * 切记!切记!切记!否则会闪退! + * + * 当在fragment设置viewPager + BottomNavigation + fragment时,则不会出现这个问题。 + * */ + @Override + protected boolean canDragBack() { + return false; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home2Fragment.java b/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home2Fragment.java new file mode 100644 index 00000000..e4d11616 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home2Fragment.java @@ -0,0 +1,78 @@ +package com.bonait.bnframework.modules.home.fragment; + + +import android.app.Fragment; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.common.base.BaseFragment; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.orhanobut.logger.Logger; +import com.qmuiteam.qmui.widget.QMUITopBarLayout; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * A simple {@link Fragment} subclass. + */ +public class Home2Fragment extends BaseFragment { + + + @BindView(R.id.topbar) + QMUITopBarLayout mTopBar; + + public Home2Fragment() { + } + + @Override + protected View onCreateView() { + View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_home2, null); + ButterKnife.bind(this, root); + + + return root; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Logger.d("第二页创建"); + + initTopBar(); + } + + private void initTopBar() { + mTopBar.setTitle("小炒功能菜单"); + } + + @OnClick(R.id.button) + public void onViewClicked() { + ToastUtils.info("功能"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Logger.d("第二页销毁"); + } + + /** + * 当在activity设置viewPager + BottomNavigation + fragment时, + * 为防止viewPager左滑动切换界面,与fragment左滑返回上一界面冲突引起闪退问题, + * 必须加上此方法,禁止fragment左滑返回上一界面。 + * + * 切记!切记!切记!否则会闪退! + * + * 当在fragment设置viewPager + BottomNavigation + fragment时,则不会出现这个问题。 + * */ + @Override + protected boolean canDragBack() { + return false; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home3Fragment.java b/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home3Fragment.java new file mode 100644 index 00000000..296670cf --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home3Fragment.java @@ -0,0 +1,68 @@ +package com.bonait.bnframework.modules.home.fragment; + + +import android.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.common.base.BaseFragment; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.orhanobut.logger.Logger; +import com.qmuiteam.qmui.widget.QMUITopBarLayout; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * A simple {@link Fragment} subclass. + */ +public class Home3Fragment extends BaseFragment { + + @BindView(R.id.topbar) + QMUITopBarLayout mTopBar; + + public Home3Fragment() { + // Required empty public constructor + } + + @Override + protected View onCreateView() { + View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_home3, null); + ButterKnife.bind(this, root); + Logger.d("第三页创建"); + + initTopBar(); + return root; + } + + private void initTopBar() { + mTopBar.setTitle("第三页"); + } + + @OnClick(R.id.button) + public void onViewClicked() { + ToastUtils.info("我的"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Logger.d("第三页销毁"); + } + + /** + * 当在activity设置viewPager + BottomNavigation + fragment时, + * 为防止viewPager左滑动切换界面,与fragment左滑返回上一界面冲突引起闪退问题, + * 必须加上此方法,禁止fragment左滑返回上一界面。 + * + * 切记!切记!切记!否则会闪退! + * + * 若底层是BottomNavigationFragment设置viewPager则不会出现这个问题。 + * */ + @Override + protected boolean canDragBack() { + return false; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/mine/fragment/MyFragment.java b/app/src/main/java/com/bonait/bnframework/modules/mine/fragment/MyFragment.java new file mode 100644 index 00000000..a452a925 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/mine/fragment/MyFragment.java @@ -0,0 +1,220 @@ +package com.bonait.bnframework.modules.mine.fragment; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.allen.library.SuperTextView; +import com.bonait.bnframework.R; +import com.bonait.bnframework.common.base.BaseFragment; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.utils.AlertDialogUtils; +import com.bonait.bnframework.common.utils.UpdateAppUtils; +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.modules.welcome.activity.LoginActivity; +import com.bonait.bnframework.modules.welcome.activity.WelcomeActivity; +import com.orhanobut.logger.Logger; +import com.qmuiteam.qmui.widget.dialog.QMUIDialog; +import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import pub.devrel.easypermissions.AfterPermissionGranted; +import pub.devrel.easypermissions.EasyPermissions; + +public class MyFragment extends BaseFragment { + + + @BindView(R.id.h_background) + ImageView hBackground; + @BindView(R.id.h_head) + ImageView hHead; + @BindView(R.id.h_user_name) + TextView hUserName; + @BindView(R.id.stv_change_pwd) + SuperTextView stvChangePwd; + @BindView(R.id.stv_update) + SuperTextView stvUpdate; + @BindView(R.id.stv_logout) + SuperTextView stvLogout; + + /* + private static final String BUNDLE_TITLE = "key_title"; + public static MyFragment newInstance(String title) { + Bundle bundle = new Bundle(); + bundle.putString(BUNDLE_TITLE,title); + MyFragment myFragment = new MyFragment(); + myFragment.setArguments(bundle); + return myFragment; + } + */ + + private Context context; + + @Override + protected View onCreateView() { + View root = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_my, null); + ButterKnife.bind(this, root); + + return root; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Logger.d("我的fragment创建"); + context = getContext(); + initView(); + } + + @OnClick(R.id.h_head) + public void onViewClicked() { + + } + + private void initView() { + + /* + * 版本更新,点击事件 + * */ + stvUpdate.setOnSuperTextViewClickListener(new SuperTextView.OnSuperTextViewClickListener() { + @Override + public void onClickListener(SuperTextView superTextView) { + //检查权限,并启动版本更新 + checkPermission(); + } + }); + + /** + * 退出按钮 + */ + stvLogout.setOnSuperTextViewClickListener(new SuperTextView.OnSuperTextViewClickListener() { + @Override + public void onClickListener(SuperTextView superTextView) { + String title = "温馨提示!"; + String message = "客官确定要退出程序吗,小菠萝会想你的哦?"; + AlertDialogUtils.showDialog(context, title, message, new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + skipToLoginActivity(); + dialog.dismiss(); + } + }); + } + }); + } + + /** + * 跳转登录界面 + */ + private void skipToLoginActivity() { + // 跳转到登录页面 + Intent intent = new Intent(context, LoginActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + //overridePend1ingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + } + + /***********************************检查App更新********************************************/ + @AfterPermissionGranted(Constants.UPDATE_APP) + @Override + public void checkPermission() { + // 检查文件读写权限 + String[] params = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + if (EasyPermissions.hasPermissions(context,params)) { + + //Android 8.0后,安装应用需要检查打开未知来源应用权限 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + checkInstallPermission(); + } else { + UpdateAppUtils.updateApp(context); + } + } else { + //未获取权限 + EasyPermissions.requestPermissions(this, "更新版本需要读写本地权限!", Constants.UPDATE_APP, params); + } + } + + /** + * Android 8.0后,安装应用需要检查打开未知来源应用权限 + */ + @RequiresApi(api = Build.VERSION_CODES.O) + private void checkInstallPermission() { + // 判断是否已打开未知来源应用权限 + boolean haveInstallPermission = context.getPackageManager().canRequestPackageInstalls(); + + if (haveInstallPermission) { + //已经打开权限,直接启动版本更新 + UpdateAppUtils.updateApp(context); + } else { + AlertDialogUtils.showDialog(getContext(), + "请打开未知来源应用权限", + "为了正常升级APP,请点击设置-高级设置-允许安装未知来源应用,本功能只限用于APP版本升级。", + "权限设置", + new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + // 跳转到系统打开未知来源应用权限,在onActivityResult中启动更新 + toInstallPermissionSettingIntent(context); + dialog.dismiss(); + } + }); + } + } + + /** + * 开启安装未知来源权限 + */ + @RequiresApi(api = Build.VERSION_CODES.O) + private void toInstallPermissionSettingIntent(Context context) { + Uri packageURI = Uri.parse("package:" + context.getPackageName()); + Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI); + startActivityForResult(intent, Constants.INSTALL_PERMISSION_CODE); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + switch (requestCode) { + case Constants.INSTALL_PERMISSION_CODE: + checkPermission(); + break; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + Logger.d("我的fragment销毁"); + } + + /** + * 当在activity设置viewPager + BottomNavigation + fragment时, + * 为防止viewPager左滑动切换界面,与fragment左滑返回上一界面冲突引起闪退问题, + * 必须加上此方法,禁止fragment左滑返回上一界面。 + * + * 切记!切记!切记!否则会闪退! + * + * 当在fragment设置viewPager + BottomNavigation + fragment时,则不会出现这个问题。 + */ + @Override + protected boolean canDragBack() { + return false; + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/mine/model/UpdateAppPo.java b/app/src/main/java/com/bonait/bnframework/modules/mine/model/UpdateAppPo.java new file mode 100644 index 00000000..0ee1d244 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/mine/model/UpdateAppPo.java @@ -0,0 +1,112 @@ +package com.bonait.bnframework.modules.mine.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; + +/** + * Created by LY on 2019/4/8. + */ +public class UpdateAppPo { + /** + * apkId : 10140 + * createtime : 创建时间 + * creator : 发布者 + * description : 描述 + * fileSize : Apk大小byte + * id : 1 + * modifier : null + * updatetime : 更新时间 + * version : Apk版本 + */ + @SerializedName("apkId") + private String apkId; + @SerializedName("createtime") + private Date createTime; + @SerializedName("creator") + private String creator; + @SerializedName("description") + private String description; + @SerializedName("fileSize") + private String fileSize; + @SerializedName("id") + private int id; + @SerializedName("modifier") + private String modifier; + @SerializedName("updatetime") + private Date updateTime; + @SerializedName("version") + private int version; + + public String getApkId() { + return apkId; + } + + public void setApkId(String apkId) { + this.apkId = apkId; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getFileSize() { + return fileSize; + } + + public void setFileSize(String fileSize) { + this.fileSize = fileSize; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getModifier() { + return modifier; + } + + public void setModifier(String modifier) { + this.modifier = modifier; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/welcome/activity/LoginActivity.java b/app/src/main/java/com/bonait/bnframework/modules/welcome/activity/LoginActivity.java new file mode 100644 index 00000000..e0290e37 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/welcome/activity/LoginActivity.java @@ -0,0 +1,477 @@ +package com.bonait.bnframework.modules.welcome.activity; + +import android.animation.ObjectAnimator; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.widget.NestedScrollView; +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.LinearInterpolator; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.common.base.BaseActivity; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.constant.SPConstants; +import com.bonait.bnframework.common.http.callback.json.JsonDialogCallback; +import com.bonait.bnframework.common.model.BaseCodeJson; +import com.bonait.bnframework.common.utils.AlertDialogUtils; +import com.bonait.bnframework.common.utils.AnimationToolUtils; +import com.bonait.bnframework.common.utils.AppUtils; +import com.bonait.bnframework.common.utils.Des3Utils; +import com.bonait.bnframework.common.utils.KeyboardToolUtils; +import com.bonait.bnframework.common.utils.PreferenceUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.modules.home.activity.BottomNavigation2Activity; +import com.bonait.bnframework.modules.welcome.model.AppLoginPo; +import com.bonait.bnframework.test.TestActivity; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.model.HttpParams; +import com.lzy.okgo.model.Response; +import com.mobsandgeeks.saripaar.ValidationError; +import com.mobsandgeeks.saripaar.Validator; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.mobsandgeeks.saripaar.annotation.Order; +import com.mobsandgeeks.saripaar.annotation.Password; +import com.qmuiteam.qmui.util.QMUIStatusBarHelper; + +import org.litepal.LitePal; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class LoginActivity extends BaseActivity implements Validator.ValidationListener { + + @BindView(R.id.logo) + ImageView mLogo; + + @Order(1) + @NotEmpty(message = "用户名不能为空") + @BindView(R.id.et_account) + EditText mEtAccount; + + @Order(2) + @NotEmpty(message = "密码不能为空") + @Password(min = 1, scheme = Password.Scheme.ANY,message = "密码不能少于1位") + @BindView(R.id.et_password) + EditText mEtPassword; + + @BindView(R.id.iv_clean_account) + ImageView mIvCleanAccount; + @BindView(R.id.clean_password) + ImageView mCleanPassword; + @BindView(R.id.iv_show_pwd) + ImageView mIvShowPwd; + + @BindView(R.id.cb_checkbox) + CheckBox cbCheckbox; + @BindView(R.id.content) + LinearLayout mContent; + @BindView(R.id.scrollView) + NestedScrollView mScrollView; + + private long exitTime = 0; + private int screenHeight = 0;//屏幕高度 + private int keyHeight = 0; //软件盘弹起后所占高度 + private final float scale = 0.9f; //logo缩放比例 + private int height = 0; + boolean isCheckBox; + + private Validator validator; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + ButterKnife.bind(this); + + QMUIStatusBarHelper.setStatusBarLightMode(this); + + validator = new Validator(this); + validator.setValidationListener(this); + + initUser(); + initEvent(); + } + + @OnClick({R.id.iv_clean_account, R.id.clean_password, R.id.iv_show_pwd,R.id.forget_password, R.id.btn_login}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.iv_clean_account: + mEtAccount.getText().clear(); + mEtPassword.getText().clear(); + break; + case R.id.clean_password: + mEtPassword.getText().clear(); + break; + case R.id.iv_show_pwd: + changePasswordEye(); + break; + case R.id.forget_password: + forgotPassword(); + break; + case R.id.btn_login: + // 登录,启动表单验证,重写方法:onValidationSucceeded + validator.validate(); + break; + } + } + + /** + * 初始化登录历史记录 + * */ + private void initUser() { + //从sharePreference拿出保存的账号密码 + String username = PreferenceUtils.getString(SPConstants.USER_NAME, ""); + String password = PreferenceUtils.getString(SPConstants.PASSWORD, ""); + mEtAccount.getText().clear(); + mEtPassword.getText().clear(); + + if (!TextUtils.isEmpty(username)) { + mEtAccount.setText(username); + mEtAccount.setSelection(mEtAccount.getText().length()); + mIvCleanAccount.setVisibility(View.VISIBLE); + } + + if (!TextUtils.isEmpty(password)) { + password = Des3Utils.decode(password); + mEtPassword.setText(password); + mEtPassword.setSelection(mEtPassword.getText().length()); + mCleanPassword.setVisibility(View.VISIBLE); + cbCheckbox.setChecked(true); + isCheckBox = true; + } else { + cbCheckbox.setChecked(false); + isCheckBox = false; + } + } + + /** + * 忘记密码 + * */ + private void forgotPassword() { + ToastUtils.info("请与管理员联系修改密码!"); + } + + // *************************以下为登录验证及跳转界面相关*************************// + + /** + * 登录表单验证成功后,请求后台验证账号密码 + * */ + private void attemptLogin() { + String url = Constants.SERVICE_IP + "/appLogin.do"; + + final String userAccount = mEtAccount.getText().toString(); + final String password = mEtPassword.getText().toString(); + //密码加密 + String newPassword = AppUtils.encryptSha256(password); + + //跳转到主页 + skipToMainActivity(); +// OkGo.>post(url) +// .tag(this) +// .params("username",userAccount) +// .params("password",newPassword) +// .execute(new JsonDialogCallback>(this) { +// @Override +// public void onSuccess(Response> response) { +// BaseCodeJson loginJson = response.body(); +// if (loginJson != null) { +// whichDepartment(userAccount,password,loginJson.getResult()); +// } +// } +// }); + } + + /** + * 判断该账号是否有所属部门 + * */ + private void whichDepartment(String userAccount, String password, AppLoginPo appLoginPo) { + String firstDepId = appLoginPo.getFirstDepId(); + + if (firstDepId == null) { + + // 测试数据 + if (Constants.superAdminTest && 1 == appLoginPo.getId()) { + // 测试数据 + superAdminTest(appLoginPo); + // 存储用户必要的数据 + saveUserDataToSharePreference(userAccount, password,appLoginPo); + //判断跳转到测试界面 + if (Constants.SKIP_TO_TEST_ACTIVITY) { + skipToTestActivity(); + return; + } + //跳转到主页 + skipToMainActivity(); + return; + } + + AlertDialogUtils.showDialog( + this, + "登录提示:", + "该账户未确认组织类型,无法登录,请与管理员联系!", + "重新登录"); + } else { + + // 测试数据 + if (Constants.superAdminTest) { + superAdminTest(appLoginPo); + } + + // 存储用户必要的数据 + saveUserDataToSharePreference(userAccount, password,appLoginPo); + //判断跳转到测试界面 + if (Constants.SKIP_TO_TEST_ACTIVITY) { + skipToTestActivity(); + return; + } + + //跳转到主页 + skipToMainActivity(); + } + + } + + /** + * 根据返回的结果,存储用户必要的数据到SharePreference + * */ + private void saveUserDataToSharePreference(String userName, String password,AppLoginPo appLoginPo) { + //保存用户账号密码到sharePreference + PreferenceUtils.setString(SPConstants.USER_NAME, userName); + PreferenceUtils.remove(SPConstants.PASSWORD); + // 如果用户点击了“记住密码”,保存密码 + if (isCheckBox) { + String psd = Des3Utils.encode(password); + PreferenceUtils.setString(SPConstants.PASSWORD, psd); + } + // 保存用户名字 + PreferenceUtils.setString(SPConstants.USER, appLoginPo.getName()); + // 保存用户ID + PreferenceUtils.setInt(SPConstants.USER_ID, appLoginPo.getId()); + // 保存角色 + PreferenceUtils.setString(SPConstants.ROLE_NAMES,appLoginPo.getRoleNames()); + + // 保存部门 + if (appLoginPo.getFirstDepId() != null) { + PreferenceUtils.setString(SPConstants.FIRST_DEP_ID,appLoginPo.getFirstDepId()); + } + + //保存token到sharePreference + PreferenceUtils.setString(SPConstants.TOKEN, appLoginPo.getToken()); + //获取token + HttpParams params = new HttpParams(); + params.put(Constants.APP_TOKEN, PreferenceUtils.getString(SPConstants.TOKEN, "")); + OkGo.getInstance().init(getApplication()) + .addCommonParams(params); + //启动本地数据库 + LitePal.getDatabase(); + } + + /** + * 跳转到测试界面 + * */ + private void skipToTestActivity() { + // 隐藏软键盘 + KeyboardToolUtils.hideSoftInput(LoginActivity.this); + // 退出界面之前把状态栏还原为白色字体与图标 + QMUIStatusBarHelper.setStatusBarDarkMode(LoginActivity.this); + Intent intent = new Intent(LoginActivity.this, TestActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + } + + /** + * 跳转到主界面 + * */ + private void skipToMainActivity() { + // 隐藏软键盘 + KeyboardToolUtils.hideSoftInput(LoginActivity.this); + // 退出界面之前把状态栏还原为白色字体与图标 + QMUIStatusBarHelper.setStatusBarDarkMode(LoginActivity.this); + Intent intent = new Intent(LoginActivity.this, BottomNavigation2Activity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + } + + // *************************以上为登录验证及跳转界面相关*************************// + + /** + * 测试数据 + * */ + private void superAdminTest(AppLoginPo appLoginPo) { + int id = appLoginPo.getId(); + if (id == 1) { + // TODO: do something + } + } + + + //---------------------------------分割线-------------------------------------// + + /** + * 点击眼睛图片显示或隐藏密码 + * */ + private void changePasswordEye() { + if (mEtPassword.getInputType() != InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { + mEtPassword.setInputType(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + mIvShowPwd.setImageResource(R.drawable.icon_pass_visuable); + } else { + mEtPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + mIvShowPwd.setImageResource(R.drawable.icon_pass_gone); + } + + // 将光标移至文字末尾 + String pwd = mEtPassword.getText().toString(); + if (!TextUtils.isEmpty(pwd)) + mEtPassword.setSelection(pwd.length()); + } + + @SuppressLint("ClickableViewAccessibility") + private void initEvent() { + + // 获取屏幕高度 + screenHeight = this.getResources().getDisplayMetrics().heightPixels; + // 弹起高度为屏幕高度的1/3 + keyHeight = screenHeight / 3; + + // 输入账号状态监听,在右边显示或隐藏clean + addIconClearListener(mEtAccount,mIvCleanAccount); + // 监听EtPassword输入状态,在右边显示或隐藏clean + addIconClearListener(mEtPassword,mCleanPassword); + + /* + * 记住密码Checkbox点击监听器 + * */ + cbCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + isCheckBox = isChecked; + } + }); + + /* + * 禁止键盘弹起的时候可以滚动 + */ + mScrollView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + // ScrollView监听滑动状态 + mScrollView.addOnLayoutChangeListener(new ViewGroup.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + /* old是改变前的左上右下坐标点值,没有old的是改变后的左上右下坐标点值 + 现在认为只要控件将Activity向上推的高度超过了1/3屏幕高,就认为软键盘弹起*/ + if (oldBottom != 0 && bottom != 0 && (oldBottom - bottom > keyHeight)) { + int dist = mContent.getBottom() - mScrollView.getHeight(); + if (dist > 0) { + ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(mContent, "translationY", 0.0f, -dist); + mAnimatorTranslateY.setDuration(300); + mAnimatorTranslateY.setInterpolator(new LinearInterpolator()); + mAnimatorTranslateY.start(); + AnimationToolUtils.zoomIn(mLogo, scale, dist); + } + + } else if (oldBottom != 0 && bottom != 0 && (bottom - oldBottom > keyHeight)) { + if ((mContent.getBottom() - oldBottom) > 0) { + ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(mContent, "translationY", mContent.getTranslationY(), 0); + mAnimatorTranslateY.setDuration(300); + mAnimatorTranslateY.setInterpolator(new LinearInterpolator()); + mAnimatorTranslateY.start(); + //键盘收回后,logo恢复原来大小,位置同样回到初始位置 + AnimationToolUtils.zoomOut(mLogo, scale); + } + } + } + }); + } + + /** + * 设置文本框与右侧删除图标监听器 + */ + private void addIconClearListener(final EditText et, final ImageView iv) { + et.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + //如果文本框长度大于0,则显示删除图标,否则不显示 + if (s.length() > 0) { + iv.setVisibility(View.VISIBLE); + } else { + iv.setVisibility(View.INVISIBLE); + } + } + }); + } + + @Override + public void onValidationSucceeded() { + // 注解验证全部通过验证,开始后台验证 + attemptLogin(); + } + + @Override + public void onValidationFailed(List errors) { + for (ValidationError error : errors) { + View view = error.getView(); + String message = error.getCollatedErrorMessage(this); + + // 显示上面注解中添加的错误提示信息 + if (view instanceof EditText) { + ((EditText) view).setError(message); + } else { + ToastUtils.error(message); + } + } + } + + /** + * 重写返回键,实现双击退出程序效果 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (System.currentTimeMillis() - exitTime > 2000) { + ToastUtils.normal("再按一次退出程序"); + exitTime = System.currentTimeMillis(); + } else { + OkGo.getInstance().cancelAll(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + ActivityLifecycleManager.get().appExit(); + } + return true; + } + return super.onKeyDown(keyCode, event); + } +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/welcome/activity/WelcomeActivity.java b/app/src/main/java/com/bonait/bnframework/modules/welcome/activity/WelcomeActivity.java new file mode 100644 index 00000000..9487bc20 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/welcome/activity/WelcomeActivity.java @@ -0,0 +1,196 @@ +package com.bonait.bnframework.modules.welcome.activity; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.AppCompatActivity; +import android.view.KeyEvent; + +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.constant.SPConstants; +import com.bonait.bnframework.common.http.callback.json.JsonCallback; +import com.bonait.bnframework.common.model.BaseCodeJson; +import com.bonait.bnframework.common.utils.PreferenceUtils; +import com.bonait.bnframework.modules.home.activity.BottomNavigation2Activity; +import com.bonait.bnframework.test.TestActivity; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.model.Response; + +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; + +public class WelcomeActivity extends AppCompatActivity { + + private Handler handler = new Handler(); + /* + * 启动模式: + * 1:启动界面时间与App加载时间相等 + * 2:设置启动界面2秒后跳转 + * */ + private final static int SELECT_MODE = 2; + private OkHttpClient.Builder builder; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //setContentView(R.layout.activity_welcome); + initWelcome(); + } + + private void initWelcome() { + switch (WelcomeActivity.SELECT_MODE) { + case 1: + fastWelcome(); + break; + case 2: + slowWelcome(); + break; + } + } + + + /*方法1:启动界面时间与App加载时间相等*/ + private void fastWelcome() { + new Thread(new Runnable() { + @Override + public void run() { + //耗时任务,比如加载网络数据 + runOnUiThread(new Runnable() { + @Override + public void run() { + //判断token + doPost(); + } + }); + } + }).start(); + } + + /*方法2:设置启动界面2秒后跳转*/ + private void slowWelcome() { + handler.postDelayed(new Runnable() { + @Override + public void run() { + //判断token + doPost(); + } + }, 2000); + } + + + /** + * 请求服务器判断token是否过期 + */ + private void doPost() { + //1.直接进入登录界面 + skipToLoginActivity(); +// //判断是否用户修改过密码 +// boolean isChangePwd = PreferenceUtils.getBoolean(SPConstants.CHANGE_PWD, false); +// if (isChangePwd) { +// //若修改过密码,则使token失效 +// PreferenceUtils.setString(SPConstants.TOKEN, ""); +// } +// String token = PreferenceUtils.getString(SPConstants.TOKEN, ""); +// // 请求后台判断token +// String url = Constants.SERVICE_IP + "/checkToken.do"; +// +// // 修改请求超时时间为6s,与全局超时时间分开 +// builder = new OkHttpClient.Builder(); +// builder.readTimeout(2000, TimeUnit.MILLISECONDS); +// builder.writeTimeout(2000, TimeUnit.MILLISECONDS); +// builder.connectTimeout(2000, TimeUnit.MILLISECONDS); +// +// OkGo.>post(url) +// .tag(this) +// .client(builder.build()) +// .params("token", token) +// .execute(new JsonCallback>() { +// @Override +// public void onSuccess(Response> response) { +// +// // 判断是否开启跳转到测试界面 +// if (Constants.SKIP_TO_TEST_ACTIVITY) { +// skipToTestActivity(); +// } else { +// skipToMainActivity(); +// } +// +// } +// +// @Override +// public void onError(Response> response) { +// super.onError(response); +// skipToLoginActivity(); +// } +// }); + } + + + /** + * 跳转到测试页面 + * */ + private void skipToTestActivity() { + // token未过期,跳转到主界面 + Intent intent = new Intent(WelcomeActivity.this, TestActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + startActivity(intent); + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + } + + + /** + * 跳转到主界面 + * */ + private void skipToMainActivity() { + // token未过期,跳转到主界面 + Intent intent = new Intent(WelcomeActivity.this, BottomNavigation2Activity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + } + + /** + * 跳转到登录页面 + * */ + private void skipToLoginActivity() { + // 跳转到登录页面 + Intent intent = new Intent(WelcomeActivity.this, LoginActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + // 结束所有Activity + ActivityLifecycleManager.get().finishAllActivity(); + } + + /** + * 屏蔽物理返回键 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onDestroy() { + if(builder!=null) + { + OkGo.cancelAll(builder.build()); + } + + if (handler != null) { + //If token is null, all callbacks and messages will be removed. + handler.removeCallbacksAndMessages(null); + } + super.onDestroy(); + } + +} diff --git a/app/src/main/java/com/bonait/bnframework/modules/welcome/model/AppLoginPo.java b/app/src/main/java/com/bonait/bnframework/modules/welcome/model/AppLoginPo.java new file mode 100644 index 00000000..da95bed9 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/modules/welcome/model/AppLoginPo.java @@ -0,0 +1,82 @@ +package com.bonait.bnframework.modules.welcome.model; + +import java.util.List; + +/** + * Created by LY on 2019/4/2. + */ +public class AppLoginPo { + + private String token; + private int id; + private String name; + private List roleIds; + private String roleNames; + private String firstDepId; + private String firstDepName; + private String depName; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + public String getRoleNames() { + return roleNames; + } + + public void setRoleNames(String roleNames) { + this.roleNames = roleNames; + } + + public String getFirstDepId() { + return firstDepId; + } + + public void setFirstDepId(String firstDepId) { + this.firstDepId = firstDepId; + } + + public String getFirstDepName() { + return firstDepName; + } + + public void setFirstDepName(String firstDepName) { + this.firstDepName = firstDepName; + } + + public String getDepName() { + return depName; + } + + public void setDepName(String depName) { + this.depName = depName; + } +} diff --git a/app/src/main/java/com/bonait/bnframework/test/Test.java b/app/src/main/java/com/bonait/bnframework/test/Test.java new file mode 100644 index 00000000..6fa2b271 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/test/Test.java @@ -0,0 +1,7 @@ +package com.bonait.bnframework.test; + +/** + * Created by LY on 2019/4/3. + */ +public class Test { +} diff --git a/app/src/main/java/com/bonait/bnframework/test/TestActivity.java b/app/src/main/java/com/bonait/bnframework/test/TestActivity.java new file mode 100644 index 00000000..51fb6bf3 --- /dev/null +++ b/app/src/main/java/com/bonait/bnframework/test/TestActivity.java @@ -0,0 +1,189 @@ +package com.bonait.bnframework.test; + +import android.Manifest; +import android.content.Context; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; + +import com.bonait.bnframework.R; +import com.bonait.bnframework.manager.ActivityLifecycleManager; +import com.bonait.bnframework.common.base.BaseActivity; +import com.bonait.bnframework.common.constant.Constants; +import com.bonait.bnframework.common.http.callback.files.FileProgressDialogCallBack; +import com.bonait.bnframework.common.http.callback.files2.FileProgressDialogCallBack2; +import com.bonait.bnframework.common.http.callback.json.JsonDialogCallback; +import com.bonait.bnframework.common.model.BaseCodeJson; +import com.bonait.bnframework.common.utils.AppUtils; +import com.bonait.bnframework.common.utils.ToastUtils; +import com.bonait.bnframework.modules.welcome.model.AppLoginPo; +import com.bonait.bnframework.modules.mine.model.UpdateAppPo; +import com.lzy.okgo.OkGo; +import com.lzy.okgo.model.Response; +import com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton; + +import java.io.File; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import pub.devrel.easypermissions.AfterPermissionGranted; +import pub.devrel.easypermissions.EasyPermissions; + +public class TestActivity extends BaseActivity { + + @BindView(R.id.button) + QMUIRoundButton button; + @BindView(R.id.button2) + QMUIRoundButton button2; + + private String token = ""; + private Context context; + private long exitTime = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_test); + ButterKnife.bind(this); + context = this; + } + + @OnClick({R.id.button, R.id.button2}) + public void onViewClicked(View view) { + switch (view.getId()) { + case R.id.button: + //ToastUtils.info("提交成功!"); + //SmartToast.info("提交成功!"); + //login(); + break; + case R.id.button2: + //SmartToast.warning("保存失败!"); + //ToastUtils.warning("保存失败!"); + // token未过期,跳转到主界面 + /*Intent intent = new Intent(TestActivity.this, BottomNavigation2Activity.class); + startActivity(intent);*/ + //checkToken(); + checkPermission(); + break; + } + } + + + private void checkToken() { + String url = Constants.SERVICE_IP + "/checkToken.do"; + + OkGo.>post(url) + .tag(this) + .params("token", token) + .execute(new JsonDialogCallback>(this) { + @Override + public void onSuccess(Response> response) { + BaseCodeJson baseCodeJson = response.body(); + ToastUtils.info(baseCodeJson.getMsg()); + } + }); + } + + + private void login() { + String userName = "admin"; + String passWord = AppUtils.encryptSha256("1"); + String url = Constants.SERVICE_IP + "/appLogin.do"; + OkGo.>post(url) + .tag(this) + .params("username",userName) + .params("password",passWord) + .execute(new JsonDialogCallback>(this) { + @Override + public void onSuccess(Response> response) { + BaseCodeJson loginPo = response.body(); + token = loginPo.getResult().getToken(); + } + }); + } + + /** + * 检查权限是否授权 + */ + @AfterPermissionGranted(Constants.ALL_PERMISSION) + @Override + public void checkPermission() { + + //String[] params = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA}; + String[] params = {Manifest.permission.WRITE_EXTERNAL_STORAGE}; + //判断是否获取权限 + if (EasyPermissions.hasPermissions(this, params)) { + // 全部权限申请成功后 + doDownload(); + } else { + //未获取权限或拒绝权限时 + EasyPermissions.requestPermissions(this, + "xxx需要用到以下权限:\n\n1. 录制音频权限\n\n2. 录制视频权限\n\n3. 文件读取存储权限", + Constants.ALL_PERMISSION, params); + } + } + + private String downloadUrl; + private long totalSize; + + private void getAppId() { + String url = Constants.SERVICE_IP+"/iandroid/appVersionAction!getNewVersion.do"; + OkGo.post(url) + .tag(this) + .execute(new JsonDialogCallback(this) { + @Override + public void onSuccess(Response response) { + UpdateAppPo updateAppPo = response.body(); + String appId = updateAppPo.getApkId(); + //获取apk下载地址 + downloadUrl = Constants.SERVICE_IP+ "/file-download?fileId=" + appId; + totalSize = Long.parseLong(updateAppPo.getFileSize()); + //toDownload(); + doDownload(); + } + }); + } + + private void toDownload() { + OkGo.get(downloadUrl) + .tag(this) + .execute(new FileProgressDialogCallBack2(this,totalSize) { + @Override + public void onSuccess(Response response) { + ToastUtils.success("下载完成!"); + } + }); + } + + private void doDownload() { + downloadUrl = Constants.SERVICE_IP+ "/file-download?fileId=1509"; + OkGo.get(downloadUrl) + .tag(this) + .execute(new FileProgressDialogCallBack(this) { + @Override + public void onSuccess(Response response) { + ToastUtils.success("下载完成!"); + } + }); + } + + /** + * 重写返回键,实现双击退出程序效果 + */ + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (System.currentTimeMillis() - exitTime > 2000) { + ToastUtils.normal("再按一次退出程序"); + exitTime = System.currentTimeMillis(); + } else { + OkGo.getInstance().cancelAll(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + ActivityLifecycleManager.get().appExit(); + } + return true; + } + return super.onKeyDown(keyCode, event); + } +} diff --git a/app/src/main/res/color/s_app_color_blue_2.xml b/app/src/main/res/color/s_app_color_blue_2.xml new file mode 100644 index 00000000..20c3beb5 --- /dev/null +++ b/app/src/main/res/color/s_app_color_blue_2.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/s_app_color_gray.xml b/app/src/main/res/color/s_app_color_gray.xml new file mode 100644 index 00000000..c395c1be --- /dev/null +++ b/app/src/main/res/color/s_app_color_gray.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/s_btn_blue_bg.xml b/app/src/main/res/color/s_btn_blue_bg.xml new file mode 100644 index 00000000..c391feb9 --- /dev/null +++ b/app/src/main/res/color/s_btn_blue_bg.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/s_btn_blue_border.xml b/app/src/main/res/color/s_btn_blue_border.xml new file mode 100644 index 00000000..c391feb9 --- /dev/null +++ b/app/src/main/res/color/s_btn_blue_border.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/s_btn_blue_text.xml b/app/src/main/res/color/s_btn_blue_text.xml new file mode 100644 index 00000000..c391feb9 --- /dev/null +++ b/app/src/main/res/color/s_btn_blue_text.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/s_topbar_btn_color.xml b/app/src/main/res/color/s_topbar_btn_color.xml new file mode 100644 index 00000000..70978d9e --- /dev/null +++ b/app/src/main/res/color/s_topbar_btn_color.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/icon_pass_gone.png b/app/src/main/res/drawable-hdpi/icon_pass_gone.png new file mode 100644 index 00000000..52098e18 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_pass_gone.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_pass_visuable.png b/app/src/main/res/drawable-hdpi/icon_pass_visuable.png new file mode 100644 index 00000000..f07dbc92 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_pass_visuable.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_personal_announcement.png b/app/src/main/res/drawable-hdpi/icon_personal_announcement.png new file mode 100644 index 00000000..e7861aef Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_personal_announcement.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_personal_pwd.png b/app/src/main/res/drawable-hdpi/icon_personal_pwd.png new file mode 100644 index 00000000..b09b3767 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_personal_pwd.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_personal_sex.png b/app/src/main/res/drawable-hdpi/icon_personal_sex.png new file mode 100644 index 00000000..498571c6 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_personal_sex.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_personal_sign.png b/app/src/main/res/drawable-hdpi/icon_personal_sign.png new file mode 100644 index 00000000..46ef8ea7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_personal_sign.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_personal_update.png b/app/src/main/res/drawable-hdpi/icon_personal_update.png new file mode 100644 index 00000000..0e7b00f0 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_personal_update.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_personal_user.png b/app/src/main/res/drawable-hdpi/icon_personal_user.png new file mode 100644 index 00000000..5ede653e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_personal_user.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..1f6bb290 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-xhdpi/icon_login_pwd.png b/app/src/main/res/drawable-xhdpi/icon_login_pwd.png new file mode 100644 index 00000000..fe767e9b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_login_pwd.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_login_user.png b/app/src/main/res/drawable-xhdpi/icon_login_user.png new file mode 100644 index 00000000..35fba6ef Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_login_user.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_delete_fill.png b/app/src/main/res/drawable-xxhdpi/icon_delete_fill.png new file mode 100644 index 00000000..3edd18cd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_delete_fill.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_delete_fill_select.png b/app/src/main/res/drawable-xxhdpi/icon_delete_fill_select.png new file mode 100644 index 00000000..b26010e5 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_delete_fill_select.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_announcement.png b/app/src/main/res/drawable-xxhdpi/icon_personal_announcement.png new file mode 100644 index 00000000..95e6aeb8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_announcement.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_download_files.png b/app/src/main/res/drawable-xxhdpi/icon_personal_download_files.png new file mode 100644 index 00000000..2afadcd8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_download_files.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_pwd.png b/app/src/main/res/drawable-xxhdpi/icon_personal_pwd.png new file mode 100644 index 00000000..ed07fa9c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_pwd.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_sex.png b/app/src/main/res/drawable-xxhdpi/icon_personal_sex.png new file mode 100644 index 00000000..d16c3781 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_sex.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_sign.png b/app/src/main/res/drawable-xxhdpi/icon_personal_sign.png new file mode 100644 index 00000000..63ba6a02 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_sign.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_update.png b/app/src/main/res/drawable-xxhdpi/icon_personal_update.png new file mode 100644 index 00000000..6280b150 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_update.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_personal_user.png b/app/src/main/res/drawable-xxhdpi/icon_personal_user.png new file mode 100644 index 00000000..40420631 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_personal_user.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_right.png b/app/src/main/res/drawable-xxhdpi/icon_right.png new file mode 100644 index 00000000..d071f436 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_right.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_user_pic.png b/app/src/main/res/drawable-xxhdpi/icon_user_pic.png new file mode 100644 index 00000000..ecebbd43 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_user_pic.png differ diff --git a/app/src/main/res/drawable/bg_btn_login_selected.xml b/app/src/main/res/drawable/bg_btn_login_selected.xml new file mode 100644 index 00000000..c42b089e --- /dev/null +++ b/app/src/main/res/drawable/bg_btn_login_selected.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/common_bg_with_radius_and_border.xml b/app/src/main/res/drawable/common_bg_with_radius_and_border.xml new file mode 100644 index 00000000..2daa8af1 --- /dev/null +++ b/app/src/main/res/drawable/common_bg_with_radius_and_border.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/delete_selector.xml b/app/src/main/res/drawable/delete_selector.xml new file mode 100644 index 00000000..ca9460fc --- /dev/null +++ b/app/src/main/res/drawable/delete_selector.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml new file mode 100644 index 00000000..46fc8dee --- /dev/null +++ b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml new file mode 100644 index 00000000..f8bb0b55 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..0d025f9b --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml new file mode 100644 index 00000000..78b75c39 --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_home1_selector.xml b/app/src/main/res/drawable/icon_home1_selector.xml new file mode 100644 index 00000000..51468f1e --- /dev/null +++ b/app/src/main/res/drawable/icon_home1_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_home2_selector.xml b/app/src/main/res/drawable/icon_home2_selector.xml new file mode 100644 index 00000000..52ecd23a --- /dev/null +++ b/app/src/main/res/drawable/icon_home2_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_home_my_selector.xml b/app/src/main/res/drawable/icon_home_my_selector.xml new file mode 100644 index 00000000..2abf8456 --- /dev/null +++ b/app/src/main/res/drawable/icon_home_my_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radius_button_bg.xml b/app/src/main/res/drawable/radius_button_bg.xml new file mode 100644 index 00000000..16ca8fd8 --- /dev/null +++ b/app/src/main/res/drawable/radius_button_bg.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/radius_button_bg_pressed.xml b/app/src/main/res/drawable/radius_button_bg_pressed.xml new file mode 100644 index 00000000..b3cb51cf --- /dev/null +++ b/app/src/main/res/drawable/radius_button_bg_pressed.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/s_radius_button_bg.xml b/app/src/main/res/drawable/s_radius_button_bg.xml new file mode 100644 index 00000000..c64a0d8a --- /dev/null +++ b/app/src/main/res/drawable/s_radius_button_bg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/tab_panel_bg.xml b/app/src/main/res/drawable/tab_panel_bg.xml new file mode 100644 index 00000000..388c57af --- /dev/null +++ b/app/src/main/res/drawable/tab_panel_bg.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/welcome.xml b/app/src/main/res/drawable/welcome.xml new file mode 100644 index 00000000..3db75348 --- /dev/null +++ b/app/src/main/res/drawable/welcome.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_bottom_navigation.xml b/app/src/main/res/layout/activity_bottom_navigation.xml new file mode 100644 index 00000000..3068c8d2 --- /dev/null +++ b/app/src/main/res/layout/activity_bottom_navigation.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_bottom_navigation2.xml b/app/src/main/res/layout/activity_bottom_navigation2.xml new file mode 100644 index 00000000..ef5abdf4 --- /dev/null +++ b/app/src/main/res/layout/activity_bottom_navigation2.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 00000000..8f9b3327 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +