@@ -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 |
@@ -0,0 +1,3 @@ | |||
# Default ignored files | |||
/shelf/ | |||
/workspace.xml |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="CompilerConfiguration"> | |||
<bytecodeTargetLevel target="11" /> | |||
</component> | |||
</project> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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) |
@@ -0,0 +1 @@ | |||
/build |
@@ -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') | |||
} |
@@ -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 |
@@ -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> |
@@ -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> |
@@ -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); //全局公共参数 | |||
} | |||
} |
@@ -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(); | |||
} | |||
}); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
}); | |||
} | |||
} | |||
} |
@@ -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 { | |||
} |
@@ -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; | |||
} |
@@ -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"; | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
}); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); //进度回调的方法 | |||
} | |||
}); | |||
} | |||
} |
@@ -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(); | |||
} | |||
}); | |||
} | |||
} |
@@ -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参数。 |
@@ -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); | |||
} | |||
} |
@@ -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("请求服务器失败,请稍后重试!"); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
package com.bonait.bnframework.common.interfaces; | |||
/** | |||
* Created by LY on 2019/3/28. | |||
* 动画工具类接口监听 | |||
*/ | |||
public interface OnDoIntListener { | |||
void doSomething(int intValue); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
package com.bonait.bnframework.test; | |||
/** | |||
* Created by LY on 2019/4/3. | |||
*/ | |||
public class Test { | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |