Quellcode durchsuchen

第一次提交

tags/old_version_1
fyf vor 1 Jahr
Commit
10f7fbbd66
100 geänderte Dateien mit 8182 neuen und 0 gelöschten Zeilen
  1. +13
    -0
      .gitignore
  2. +3
    -0
      .idea/.gitignore
  3. +6
    -0
      .idea/compiler.xml
  4. +19
    -0
      .idea/gradle.xml
  5. +30
    -0
      .idea/jarRepositories.xml
  6. +10
    -0
      .idea/misc.xml
  7. +31
    -0
      README.md
  8. +1
    -0
      app/.gitignore
  9. +90
    -0
      app/build.gradle
  10. BIN
     
  11. +21
    -0
      app/proguard-rules.pro
  12. +64
    -0
      app/src/main/AndroidManifest.xml
  13. +49
    -0
      app/src/main/assets/litepal.xml
  14. +151
    -0
      app/src/main/java/com/bonait/bnframework/MainApplication.java
  15. +86
    -0
      app/src/main/java/com/bonait/bnframework/common/base/BaseActivity.java
  16. +87
    -0
      app/src/main/java/com/bonait/bnframework/common/base/BaseFragment.java
  17. +9
    -0
      app/src/main/java/com/bonait/bnframework/common/base/BaseFragmentActivity.java
  18. +31
    -0
      app/src/main/java/com/bonait/bnframework/common/constant/Constants.java
  19. +56
    -0
      app/src/main/java/com/bonait/bnframework/common/constant/SPConstants.java
  20. +52
    -0
      app/src/main/java/com/bonait/bnframework/common/http/GsonConvert.java
  21. +44
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/bitmap/BitmapDialogCallback.java
  22. +145
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/files/FileProgressDialogCallBack.java
  23. +40
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileCallback2.java
  24. +117
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileConvert2.java
  25. +145
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileProgressDialogCallBack2.java
  26. +21
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/files2/ReadMe.txt
  27. +117
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonCallback.java
  28. +279
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonConvert.java
  29. +57
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonDialogCallback.java
  30. +52
    -0
      app/src/main/java/com/bonait/bnframework/common/http/callback/strings/StringDialogCallback.java
  31. +18
    -0
      app/src/main/java/com/bonait/bnframework/common/http/exception/TokenException.java
  32. +9
    -0
      app/src/main/java/com/bonait/bnframework/common/interfaces/OnDoIntListener.java
  33. +56
    -0
      app/src/main/java/com/bonait/bnframework/common/model/BaseCodeJson.java
  34. +57
    -0
      app/src/main/java/com/bonait/bnframework/common/model/BaseJson.java
  35. +39
    -0
      app/src/main/java/com/bonait/bnframework/common/model/SimpleBaseJson.java
  36. +38
    -0
      app/src/main/java/com/bonait/bnframework/common/model/SimpleCodeJson.java
  37. +98
    -0
      app/src/main/java/com/bonait/bnframework/common/notification/DownloadNotification.java
  38. +85
    -0
      app/src/main/java/com/bonait/bnframework/common/notification/MainNotification.java
  39. +104
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/AlertDialogUtils.java
  40. +226
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/AnimationToolUtils.java
  41. +217
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/AppFileUtils.java
  42. +108
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/AppUtils.java
  43. +98
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/Des3Utils.java
  44. +1300
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/FileUtils.java
  45. +157
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/KeyboardToolUtils.java
  46. +353
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/MediaFileUtils.java
  47. +109
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/NetworkUtils.java
  48. +139
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/PreferenceUtils.java
  49. +278
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/ToastUtils.java
  50. +148
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/UpdateAppUtils.java
  51. +195
    -0
      app/src/main/java/com/bonait/bnframework/common/utils/UriUtils.java
  52. +163
    -0
      app/src/main/java/com/bonait/bnframework/common/view/ClearEditTextView.java
  53. +60
    -0
      app/src/main/java/com/bonait/bnframework/common/view/QMAutoDialogBuilderView.java
  54. +232
    -0
      app/src/main/java/com/bonait/bnframework/manager/ActivityLifecycleManager.java
  55. +129
    -0
      app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigation2Activity.java
  56. +243
    -0
      app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigationActivity.java
  57. +42
    -0
      app/src/main/java/com/bonait/bnframework/modules/home/adapter/FragmentAdapter.java
  58. +83
    -0
      app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home1Fragment.java
  59. +78
    -0
      app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home2Fragment.java
  60. +68
    -0
      app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home3Fragment.java
  61. +220
    -0
      app/src/main/java/com/bonait/bnframework/modules/mine/fragment/MyFragment.java
  62. +112
    -0
      app/src/main/java/com/bonait/bnframework/modules/mine/model/UpdateAppPo.java
  63. +477
    -0
      app/src/main/java/com/bonait/bnframework/modules/welcome/activity/LoginActivity.java
  64. +196
    -0
      app/src/main/java/com/bonait/bnframework/modules/welcome/activity/WelcomeActivity.java
  65. +82
    -0
      app/src/main/java/com/bonait/bnframework/modules/welcome/model/AppLoginPo.java
  66. +7
    -0
      app/src/main/java/com/bonait/bnframework/test/Test.java
  67. +189
    -0
      app/src/main/java/com/bonait/bnframework/test/TestActivity.java
  68. +6
    -0
      app/src/main/res/color/s_app_color_blue_2.xml
  69. +6
    -0
      app/src/main/res/color/s_app_color_gray.xml
  70. +7
    -0
      app/src/main/res/color/s_btn_blue_bg.xml
  71. +7
    -0
      app/src/main/res/color/s_btn_blue_border.xml
  72. +7
    -0
      app/src/main/res/color/s_btn_blue_text.xml
  73. +7
    -0
      app/src/main/res/color/s_topbar_btn_color.xml
  74. BIN
     
  75. BIN
     
  76. BIN
     
  77. BIN
     
  78. BIN
     
  79. BIN
     
  80. BIN
     
  81. BIN
     
  82. +34
    -0
      app/src/main/res/drawable-v24/ic_launcher_foreground.xml
  83. BIN
     
  84. BIN
     
  85. BIN
     
  86. BIN
     
  87. BIN
     
  88. BIN
     
  89. BIN
     
  90. BIN
     
  91. BIN
     
  92. BIN
     
  93. BIN
     
  94. BIN
     
  95. BIN
     
  96. +27
    -0
      app/src/main/res/drawable/bg_btn_login_selected.xml
  97. +17
    -0
      app/src/main/res/drawable/common_bg_with_radius_and_border.xml
  98. +7
    -0
      app/src/main/res/drawable/delete_selector.xml
  99. +9
    -0
      app/src/main/res/drawable/ic_dashboard_black_24dp.xml
  100. +9
    -0
      app/src/main/res/drawable/ic_home_black_24dp.xml

+ 13
- 0
.gitignore Datei anzeigen

@@ -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

+ 3
- 0
.idea/.gitignore Datei anzeigen

@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

+ 6
- 0
.idea/compiler.xml Datei anzeigen

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

+ 19
- 0
.idea/gradle.xml Datei anzeigen

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

+ 30
- 0
.idea/jarRepositories.xml Datei anzeigen

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

+ 10
- 0
.idea/misc.xml Datei anzeigen

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

+ 31
- 0
README.md Datei anzeigen

@@ -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)

+ 1
- 0
app/.gitignore Datei anzeigen

@@ -0,0 +1 @@
/build

+ 90
- 0
app/build.gradle Datei anzeigen

@@ -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')
}


+ 21
- 0
app/proguard-rules.pro Datei anzeigen

@@ -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

+ 64
- 0
app/src/main/AndroidManifest.xml Datei anzeigen

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.bonait.bnframework">

<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- 网络连接 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 获取网络连接状态 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 写存储的权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 读存储的权限 -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- 0未知来源的应用权限,更新App -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" /> <!-- DownloadManager -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 录音权限 -->
<uses-permission android:name="android.permission.RECORD_VIDEO" /> <!-- 录像权限 -->
<uses-permission android:name="android.permission.CAMERA" />

<application
android:name=".MainApplication"
android:networkSecurityConfig="@xml/network_security_config"
android:allowBackup="true"
android:icon="@mipmap/ico"
android:label="@string/app_name"
android:roundIcon="@mipmap/ico"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/app_provider_paths" />
</provider>

<activity
android:name=".modules.welcome.activity.WelcomeActivity"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".modules.welcome.activity.LoginActivity">
<!-- <intent-filter>-->
<!-- <action android:name="com.bonait.bnframework.modules.welcome.activity.LoginActivity.ACTION_START" />-->

<!-- <category android:name="android.intent.category.DEFAULT" />-->
<!-- </intent-filter>-->
</activity>
<activity android:name=".test.TestActivity">
<!--<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>-->
</activity>
<activity android:name=".modules.home.activity.BottomNavigation2Activity" />
<activity android:name=".modules.home.activity.BottomNavigationActivity" />
</application>

</manifest>

+ 49
- 0
app/src/main/assets/litepal.xml Datei anzeigen

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<!--
Define the database name of your application.
By default each database name should be end with .db.
If you didn't name your database end with .db,
LitePal would plus the suffix automatically for you.
For example:
<dbname value="demo" />
-->
<dbname value="framework" />

<!--
Define the version of your database. Each time you want
to upgrade your database, the version tag would helps.
Modify the models you defined in the mapping tag, and just
make the version value plus one, the upgrade of database
will be processed automatically without concern.
For example:
<version value="1" />
-->
<version value="1" />

<!--
Define your models in the list with mapping tag, LitePal will
create tables for each mapping class. The supported fields
defined in models will be mapped into columns.
For example:
<list>
<mapping class="com.test.model.Reader" />
<mapping class="com.test.model.Magazine" />
</list>
-->
<list>

</list>

<!--
Define where the .db file should be. "internal" means the .db file
will be stored in the database folder of internal storage which no
one can access. "external" means the .db file will be stored in the
path to the directory on the primary external storage device where
the application can place persistent files it owns which everyone
can access. "internal" will act as default.
For example:
<storage value="external" />
-->

</litepal>

+ 151
- 0
app/src/main/java/com/bonait/bnframework/MainApplication.java Datei anzeigen

@@ -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); //全局公共参数

}


}

+ 86
- 0
app/src/main/java/com/bonait/bnframework/common/base/BaseActivity.java Datei anzeigen

@@ -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<String> perms) {
}

@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> 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();
}
});
}
}
}

+ 87
- 0
app/src/main/java/com/bonait/bnframework/common/base/BaseFragment.java Datei anzeigen

@@ -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<String> perms) {
}

@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> 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();
}
});
}
}
}

+ 9
- 0
app/src/main/java/com/bonait/bnframework/common/base/BaseFragmentActivity.java Datei anzeigen

@@ -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 {
}

+ 31
- 0
app/src/main/java/com/bonait/bnframework/common/constant/Constants.java Datei anzeigen

@@ -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;

}

+ 56
- 0
app/src/main/java/com/bonait/bnframework/common/constant/SPConstants.java Datei anzeigen

@@ -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";


}

+ 52
- 0
app/src/main/java/com/bonait/bnframework/common/http/GsonConvert.java Datei anzeigen

@@ -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> T fromJson(String json, Class<T> type) throws JsonIOException, JsonSyntaxException {
return create().fromJson(json, type);
}

public static <T> T fromJson(String json, Type type) {
return create().fromJson(json, type);
}

public static <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
return create().fromJson(reader, typeOfT);
}

public static <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
return create().fromJson(json, classOfT);
}

public static <T> 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);
}
}

+ 44
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/bitmap/BitmapDialogCallback.java Datei anzeigen

@@ -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<Bitmap, ? extends Request> request) {
if (tipDialog != null && !tipDialog.isShowing()) {
tipDialog.show();
}
}

@Override
public void onFinish() {
if (tipDialog != null && tipDialog.isShowing()) {
tipDialog.dismiss();
}
}
}

+ 145
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/files/FileProgressDialogCallBack.java Datei anzeigen

@@ -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<File, ? extends 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<File> 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();
}
});
}

}

+ 40
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileCallback2.java Datei anzeigen

@@ -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.
* <p>与OkGo的FileCallback改动不大,如果不想使用该类,可直接使用OkGo默认的FileCallback</p>
* <p>将文件转换类改成FileConvert2</p>
*
*/
public abstract class FileCallback2 extends AbsCallback<File> {

//文件转换类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;
}
}

+ 117
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileConvert2.java Datei anzeigen

@@ -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
* <p>与OkGo的FileConvert改动不大</p>
* <p>只是将代码的 progress.totalSize = body.contentLength();</p>
* <p>替换成progress.totalSize = totalSize; 其中totalSize为形参,需要自己传参</p>
*
*/
public class FileConvert2 implements Converter<File> {

public static final String DM_TARGET_FOLDER = File.separator + "download" + File.separator; //下载目标文件夹

private String folder; //目标文件存储的文件夹路径
private String fileName; //目标文件存储的文件名
private Callback<File> 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<File> 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); //进度回调的方法
}
});
}

}

+ 145
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/files2/FileProgressDialogCallBack2.java Datei anzeigen

@@ -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<File, ? extends 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<File> 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();
}
});
}

}

+ 21
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/files2/ReadMe.txt Datei anzeigen

@@ -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参数。

+ 117
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonCallback.java Datei anzeigen

@@ -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<T> extends AbsCallback<T> {

private Type type;
private Class<T> clazz;

public JsonCallback() {
}

public JsonCallback(Type type) {
this.type = type;
}

public JsonCallback(Class<T> 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<T> convert = new JsonConvert<>(clazz);
return convert.convertResponse(response);
}
}

JsonConvert<T> convert = new JsonConvert<>(type);
return convert.convertResponse(response);
}


@Override
public void onError(Response<T> 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);
}
}

+ 279
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonConvert.java Datei anzeigen

@@ -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<T> implements Converter<T> {

private Type type;
private Class<T> clazz;

public JsonConvert() {
}

public JsonConvert(Type type) {
this.type = type;
}

public JsonConvert(Class<T> 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<BaseCodeJson<Void>>(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<BaseCodeJson<内层JavaBean>>(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<BaseJson<Void>>(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<BaseJson<内层JavaBean>>(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("请求服务器失败,请稍后重试!");
}
}
}

+ 57
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/json/JsonDialogCallback.java Datei anzeigen

@@ -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<T> extends JsonCallback<T> {

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<T, ? extends Request> request) {
if (tipDialog != null && !tipDialog.isShowing()) {
tipDialog.show();
}
}

/**
* 请求网络结束后关闭加载框
* */
@Override
public void onFinish() {
if (tipDialog != null && tipDialog.isShowing()) {
tipDialog.dismiss();
}
}
}

+ 52
- 0
app/src/main/java/com/bonait/bnframework/common/http/callback/strings/StringDialogCallback.java Datei anzeigen

@@ -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<String, ? extends Request> request) {
if (tipDialog != null && !tipDialog.isShowing()) {
tipDialog.show();
}
}

/**
* 请求网络结束后关闭加载框
* */
@Override
public void onFinish() {
if (tipDialog != null && tipDialog.isShowing()) {
tipDialog.dismiss();
}
}

}

+ 18
- 0
app/src/main/java/com/bonait/bnframework/common/http/exception/TokenException.java Datei anzeigen

@@ -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);
}
}

+ 9
- 0
app/src/main/java/com/bonait/bnframework/common/interfaces/OnDoIntListener.java Datei anzeigen

@@ -0,0 +1,9 @@
package com.bonait.bnframework.common.interfaces;

/**
* Created by LY on 2019/3/28.
* 动画工具类接口监听
*/
public interface OnDoIntListener {
void doSomething(int intValue);
}

+ 56
- 0
app/src/main/java/com/bonait/bnframework/common/model/BaseCodeJson.java Datei anzeigen

@@ -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<T> 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;
}
}

+ 57
- 0
app/src/main/java/com/bonait/bnframework/common/model/BaseJson.java Datei anzeigen

@@ -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<T> 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;
}
}

+ 39
- 0
app/src/main/java/com/bonait/bnframework/common/model/SimpleBaseJson.java Datei anzeigen

@@ -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;
}
}

+ 38
- 0
app/src/main/java/com/bonait/bnframework/common/model/SimpleCodeJson.java Datei anzeigen

@@ -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;
}
}

+ 98
- 0
app/src/main/java/com/bonait/bnframework/common/notification/DownloadNotification.java Datei anzeigen

@@ -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);
}
}

}

+ 85
- 0
app/src/main/java/com/bonait/bnframework/common/notification/MainNotification.java Datei anzeigen

@@ -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);
}
}

}

+ 104
- 0
app/src/main/java/com/bonait/bnframework/common/utils/AlertDialogUtils.java Datei anzeigen

@@ -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();
}

}

+ 226
- 0
app/src/main/java/com/bonait/bnframework/common/utils/AnimationToolUtils.java Datei anzeigen

@@ -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;
}
}

+ 217
- 0
app/src/main/java/com/bonait/bnframework/common/utils/AppFileUtils.java Datei anzeigen

@@ -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();
}
}

+ 108
- 0
app/src/main/java/com/bonait/bnframework/common/utils/AppUtils.java Datei anzeigen

@@ -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;
}
}

}

+ 98
- 0
app/src/main/java/com/bonait/bnframework/common/utils/Des3Utils.java Datei anzeigen

@@ -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转变
* <p>法算法名称/加密模式/填充方式</p>
* <p>加密模式有:电子密码本模式ECB、加密块链模式CBC、加密反馈模式CFB、输出反馈模式OFB</p>
* <p>填充方式有:NoPadding、ZerosPadding、PKCS5Padding</p>
*/
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;
}
}
}

+ 1300
- 0
app/src/main/java/com/bonait/bnframework/common/utils/FileUtils.java
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 157
- 0
app/src/main/java/com/bonait/bnframework/common/utils/KeyboardToolUtils.java Datei anzeigen

@@ -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 {
/**
* 避免输入法面板遮挡
* <p>在manifest.xml中activity中设置</p>
* <p>android:windowSoftInputMode="stateVisible|adjustResize"</p>
*/

/**
* 动态隐藏软键盘
*
* @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)
* <p>在onTouch中处理,未获焦点则隐藏</p>
* <p>参照以下注释代码</p>
*/
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)
* <p>根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘</p>
* <p>需重写dispatchTouchEvent</p>
* <p>参照以下注释代码</p>
*/
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);
}
}
}

+ 353
- 0
app/src/main/java/com/bonait/bnframework/common/utils/MediaFileUtils.java Datei anzeigen

@@ -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<String, MediaFileType> sFileTypeMap
= new HashMap<String, MediaFileType>();
private static final HashMap<String, Integer> sMimeTypeMap
= new HashMap<String, Integer>();

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);
}
}

+ 109
- 0
app/src/main/java/com/bonait/bnframework/common/utils/NetworkUtils.java Datei anzeigen

@@ -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;
}
}

+ 139
- 0
app/src/main/java/com/bonait/bnframework/common/utils/PreferenceUtils.java Datei anzeigen

@@ -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();
}
}

+ 278
- 0
app/src/main/java/com/bonait/bnframework/common/utils/ToastUtils.java Datei anzeigen

@@ -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;
}
}

+ 148
- 0
app/src/main/java/com/bonait/bnframework/common/utils/UpdateAppUtils.java Datei anzeigen

@@ -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.<UpdateAppPo>post(getNewVersionUrl)
.tag(context)
.execute(new JsonDialogCallback<UpdateAppPo>(context) {
@Override
public void onSuccess(Response<UpdateAppPo> 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.<File>get(downloadUrl)
.tag(context)
.execute(new FileProgressDialogCallBack(context) {
@Override
public void onSuccess(Response<File> 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);
}
}
}

+ 195
- 0
app/src/main/java/com/bonait/bnframework/common/utils/UriUtils.java Datei anzeigen

@@ -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());
}
}

+ 163
- 0
app/src/main/java/com/bonait/bnframework/common/view/ClearEditTextView.java Datei anzeigen

@@ -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;
}
}

+ 60
- 0
app/src/main/java/com/bonait/bnframework/common/view/QMAutoDialogBuilderView.java Datei anzeigen

@@ -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;
}
}

+ 232
- 0
app/src/main/java/com/bonait/bnframework/manager/ActivityLifecycleManager.java Datei anzeigen

@@ -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<Activity> activityList = Collections.synchronizedList(new LinkedList<Activity>());
//private List<Activity> activityList = new LinkedList<Activity>();


/*单例模式静态内部类*/
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");
}
}

+ 129
- 0
app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigation2Activity.java Datei anzeigen

@@ -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);
}
}

+ 243
- 0
app/src/main/java/com/bonait/bnframework/modules/home/activity/BottomNavigationActivity.java Datei anzeigen

@@ -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);
}
}

+ 42
- 0
app/src/main/java/com/bonait/bnframework/modules/home/adapter/FragmentAdapter.java Datei anzeigen

@@ -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<Fragment> 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);
}
}

+ 83
- 0
app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home1Fragment.java Datei anzeigen

@@ -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;
}
}

+ 78
- 0
app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home2Fragment.java Datei anzeigen

@@ -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;
}
}

+ 68
- 0
app/src/main/java/com/bonait/bnframework/modules/home/fragment/Home3Fragment.java Datei anzeigen

@@ -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;
}
}

+ 220
- 0
app/src/main/java/com/bonait/bnframework/modules/mine/fragment/MyFragment.java Datei anzeigen

@@ -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;
}

}

+ 112
- 0
app/src/main/java/com/bonait/bnframework/modules/mine/model/UpdateAppPo.java Datei anzeigen

@@ -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;
}
}

+ 477
- 0
app/src/main/java/com/bonait/bnframework/modules/welcome/activity/LoginActivity.java Datei anzeigen

@@ -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.<BaseCodeJson<AppLoginPo>>post(url)
// .tag(this)
// .params("username",userAccount)
// .params("password",newPassword)
// .execute(new JsonDialogCallback<BaseCodeJson<AppLoginPo>>(this) {
// @Override
// public void onSuccess(Response<BaseCodeJson<AppLoginPo>> response) {
// BaseCodeJson<AppLoginPo> 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<ValidationError> 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);
}
}

+ 196
- 0
app/src/main/java/com/bonait/bnframework/modules/welcome/activity/WelcomeActivity.java Datei anzeigen

@@ -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.<BaseCodeJson<Void>>post(url)
// .tag(this)
// .client(builder.build())
// .params("token", token)
// .execute(new JsonCallback<BaseCodeJson<Void>>() {
// @Override
// public void onSuccess(Response<BaseCodeJson<Void>> response) {
//
// // 判断是否开启跳转到测试界面
// if (Constants.SKIP_TO_TEST_ACTIVITY) {
// skipToTestActivity();
// } else {
// skipToMainActivity();
// }
//
// }
//
// @Override
// public void onError(Response<BaseCodeJson<Void>> 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();
}

}

+ 82
- 0
app/src/main/java/com/bonait/bnframework/modules/welcome/model/AppLoginPo.java Datei anzeigen

@@ -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<Integer> 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<Integer> getRoleIds() {
return roleIds;
}

public void setRoleIds(List<Integer> 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;
}
}

+ 7
- 0
app/src/main/java/com/bonait/bnframework/test/Test.java Datei anzeigen

@@ -0,0 +1,7 @@
package com.bonait.bnframework.test;

/**
* Created by LY on 2019/4/3.
*/
public class Test {
}

+ 189
- 0
app/src/main/java/com/bonait/bnframework/test/TestActivity.java Datei anzeigen

@@ -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.<BaseCodeJson<Void>>post(url)
.tag(this)
.params("token", token)
.execute(new JsonDialogCallback<BaseCodeJson<Void>>(this) {
@Override
public void onSuccess(Response<BaseCodeJson<Void>> response) {
BaseCodeJson<Void> 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.<BaseCodeJson<AppLoginPo>>post(url)
.tag(this)
.params("username",userName)
.params("password",passWord)
.execute(new JsonDialogCallback<BaseCodeJson<AppLoginPo>>(this) {
@Override
public void onSuccess(Response<BaseCodeJson<AppLoginPo>> response) {
BaseCodeJson<AppLoginPo> 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.<UpdateAppPo>post(url)
.tag(this)
.execute(new JsonDialogCallback<UpdateAppPo>(this) {
@Override
public void onSuccess(Response<UpdateAppPo> 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.<File>get(downloadUrl)
.tag(this)
.execute(new FileProgressDialogCallBack2(this,totalSize) {
@Override
public void onSuccess(Response<File> response) {
ToastUtils.success("下载完成!");
}
});
}

private void doDownload() {
downloadUrl = Constants.SERVICE_IP+ "/file-download?fileId=1509";
OkGo.<File>get(downloadUrl)
.tag(this)
.execute(new FileProgressDialogCallBack(this) {
@Override
public void onSuccess(Response<File> 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);
}
}

+ 6
- 0
app/src/main/res/color/s_app_color_blue_2.xml Datei anzeigen

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/app_color_blue_2_pressed" android:state_pressed="true"/>
<item android:color="@color/app_color_blue_2"/>
</selector>

+ 6
- 0
app/src/main/res/color/s_app_color_gray.xml Datei anzeigen

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/qmui_config_color_gray_7" android:state_pressed="true" />
<item android:color="@color/qmui_config_color_gray_9" />
</selector>

+ 7
- 0
app/src/main/res/color/s_btn_blue_bg.xml Datei anzeigen

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/app_color_blue_disabled" android:state_enabled="false" />
<item android:color="@color/app_color_blue_pressed" android:state_pressed="true" />
<item android:color="@color/app_color_blue" />
</selector>

+ 7
- 0
app/src/main/res/color/s_btn_blue_border.xml Datei anzeigen

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/app_color_blue_disabled" android:state_enabled="false" />
<item android:color="@color/app_color_blue_pressed" android:state_pressed="true" />
<item android:color="@color/app_color_blue" />
</selector>

+ 7
- 0
app/src/main/res/color/s_btn_blue_text.xml Datei anzeigen

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/app_color_blue_disabled" android:state_enabled="false" />
<item android:color="@color/app_color_blue_pressed" android:state_pressed="true" />
<item android:color="@color/app_color_blue" />
</selector>

+ 7
- 0
app/src/main/res/color/s_topbar_btn_color.xml Datei anzeigen

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/qmui_config_color_50_white" android:state_enabled="false"/>
<item android:color="@color/qmui_config_color_75_white" android:state_pressed="true"/>
<item android:color="@color/qmui_config_color_white"/>
</selector>









+ 34
- 0
app/src/main/res/drawable-v24/ic_launcher_foreground.xml Datei anzeigen

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>














+ 27
- 0
app/src/main/res/drawable/bg_btn_login_selected.xml Datei anzeigen

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#7FEF5362"
android:radius="-1dp">

<item android:state_pressed="true">
<shape>
<solid android:color="#c000A8E1" />
<corners android:radius="6dp" />
</shape>
</item>

<item android:state_enabled="false">
<shape>
<solid android:color="#cccccc" />
<corners android:radius="6dp" />
</shape>
</item>


<item>
<shape>
<solid android:color="@color/app_color_blue" />
<corners android:radius="6dp" />
</shape>
</item>
</ripple>

+ 17
- 0
app/src/main/res/drawable/common_bg_with_radius_and_border.xml Datei anzeigen

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- rectangle 表示为矩形 -->

<!-- 填充的颜色 -->
<solid android:color="@color/qmui_config_color_white"/>

<!-- 边框的颜色和粗细 -->
<stroke
android:width="1px"
android:color="@color/qmui_config_color_separator"/>

<!-- android:radius 圆角的半径 -->
<corners android:radius="2dp"/>

</shape>

+ 7
- 0
app/src/main/res/drawable/delete_selector.xml Datei anzeigen

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--<item android:state_pressed="true" android:drawable="@drawable/search_clear_pressed" />
<item android:drawable="@drawable/search_clear_normal" />-->
<item android:state_pressed="true" android:drawable="@drawable/icon_delete_fill" />
<item android:drawable="@drawable/icon_delete_fill_select" />
</selector>

+ 9
- 0
app/src/main/res/drawable/ic_dashboard_black_24dp.xml Datei anzeigen

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
</vector>

+ 9
- 0
app/src/main/res/drawable/ic_home_black_24dp.xml Datei anzeigen

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
</vector>

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.

Laden…
Abbrechen
Speichern