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