diff --git a/Readme.md b/Readme.md index 8aeec97..26d1b10 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,4 @@ -#XDroidMvp 轻量级的Android MVP快速开发框架 +#XDroidMvp 轻量级的Android MVP快速开发框架(AndroidX适配版本) ## 概述 @@ -196,10 +196,19 @@ dependencies { * [ButterKnife](https://github.com/JakeWharton/butterknife)使用的是8.4.0版本,重点是 `@BindView`,可以去项目官网查看。 * [Rxlifecycle](https://github.com/trello/RxLifecycle)使用的是1.0版本,具体如何使用可以查看官网。 * [RxPermissions](https://github.com/tbruyelle/RxPermissions)使用的是0.9.1版本,具体如何使用可以查看官网。 -* [retrofit](https://github.com/square/retrofit),具体如何使用可以查看官网。 +* [retrofit](https://github.com/square/retrofit),具体如何使用可以查看官网。 +* [一定]()要修改当前项目的 gradle.properties添加 + android.useAndroidX=true + android.enableJetifier=true +* [注意]()使用viewBinding的模块需要在 build.gradle中的 android节点内添加 + buildFeatures { + viewBinding true + } ## 更新日志 +* 2021-01-06, 添加viewBinding支持 +* 2019-11-27, 修改support依赖为AndroidX依赖 * 2017-04-23,新增proguard rules,upgrade to rx2 * 2016-12-23,新增mvp、base、cache、event、imageloader、log、router * 2016-12-25,新增rxJava、rxAndroid、rxlifecycle、rxpermission、rxbus、net(retrofit) @@ -225,8 +234,11 @@ dependencies { **XDroid交流群**:153569290 +**XDroid MVP原版**:[XDroidMvp](https://github.com/limedroid/XDroidMvp) + **XDroid MVC版本**:[XDroid](https://github.com/limedroid/XDroid) 若您在使用过程中遇到任何问题,欢迎加入 **153569290** 群或者是邮件反馈,谢谢您的关注。**XDroidMvp**会持续维护,如果喜欢,记得star fork。 +[![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..fc24e7a --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 8882733..9f74e49 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion rootProject.ext.android.compileSdkVersion - + buildToolsVersion "29.0.3" defaultConfig { applicationId 'cn.droidlover.xdroidmvp.demo' minSdkVersion rootProject.ext.android.minSdkVersion @@ -17,12 +17,19 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + lintOptions { + abortOnError false + } + } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - annotationProcessor rootProject.ext.dependencies["butterknife-apt"] - implementation rootProject.ext.dependencies["avi-loading"] - implementation project(":mvp") + api fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies["avi-loading"] + api "com.lennon.utill:xdroid-mvp:1.0.1" } diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java index 2dd2ac9..d759023 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GanhuoAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.demo.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.TextView; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java index a51c03d..2bbebb5 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/GirlAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.demo.adapter; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.ImageView; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java index 14398e6..21332a3 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/adapter/HomeAdapter.java @@ -3,7 +3,7 @@ import android.content.Context; import android.graphics.Color; import android.net.Uri; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java index c25b60b..67d041e 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/AboutActivity.java @@ -2,7 +2,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java index 2372fc2..bfb6ddd 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/BasePagerFragment.java @@ -9,7 +9,6 @@ import cn.droidlover.xdroidmvp.demo.model.GankResults; import cn.droidlover.xdroidmvp.demo.present.PBasePager; import cn.droidlover.xdroidmvp.demo.widget.StateView; -import cn.droidlover.xdroidmvp.mvp.XLazyFragment; import cn.droidlover.xdroidmvp.net.NetError; import cn.droidlover.xrecyclerview.XRecyclerContentLayout; import cn.droidlover.xrecyclerview.XRecyclerView; diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java index 304c222..8509baf 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/MainActivity.java @@ -1,10 +1,9 @@ package cn.droidlover.xdroidmvp.demo.ui; import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; import android.view.MenuItem; import java.util.ArrayList; @@ -14,6 +13,7 @@ import cn.droidlover.xdroidmvp.base.XFragmentAdapter; import cn.droidlover.xdroidmvp.demo.R; import cn.droidlover.xdroidmvp.mvp.XActivity; +import com.google.android.material.tabs.TabLayout; /** * Created by wanglei on 2016/12/22. diff --git a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java index 578ace7..7826264 100644 --- a/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java +++ b/app/src/main/java/cn/droidlover/xdroidmvp/demo/ui/WebActivity.java @@ -2,8 +2,7 @@ import android.app.Activity; import android.os.Bundle; -import android.support.v4.widget.SwipeRefreshLayout; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.KeyEvent; import android.view.MenuItem; import android.view.View; @@ -13,6 +12,7 @@ import android.webkit.WebView; import android.webkit.WebViewClient; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import butterknife.BindView; import cn.droidlover.xdroidmvp.demo.R; import cn.droidlover.xdroidmvp.demo.kit.AppKit; diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index daa8d1e..0f112f9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,16 +1,16 @@ - - - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_web.xml b/app/src/main/res/layout/activity_web.xml index 3997c13..0e9258a 100644 --- a/app/src/main/res/layout/activity_web.xml +++ b/app/src/main/res/layout/activity_web.xml @@ -11,7 +11,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - + diff --git a/app/src/main/res/layout/view_loading.xml b/app/src/main/res/layout/view_loading.xml index cfd8d8a..dd43976 100644 --- a/app/src/main/res/layout/view_loading.xml +++ b/app/src/main/res/layout/view_loading.xml @@ -1,16 +1,16 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#ffffff"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="visible" + app:indicatorColor="@color/colorPrimary" + app:indicatorName="LineScale" /> diff --git a/app/src/main/res/layout/view_toolbar.xml b/app/src/main/res/layout/view_toolbar.xml index d416e75..c04fd66 100644 --- a/app/src/main/res/layout/view_toolbar.xml +++ b/app/src/main/res/layout/view_toolbar.xml @@ -1,12 +1,12 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 45e61a8..8266870 100644 --- a/build.gradle +++ b/build.gradle @@ -1,33 +1,22 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. apply from: "conf.gradle" buildscript { + ext.kotlin_version = '1.5.10' repositories { - jcenter() - maven { - url 'https://maven.google.com/' - name 'Google' - } + maven {url 'http://127.0.0.1:8081/repository/lennon/' } } dependencies { - classpath 'com.android.tools.build:gradle:3.1.3' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' - classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath 'com.android.tools.build:gradle:4.1.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { - mavenCentral() - maven { - url 'https://maven.google.com/' - name 'Google' - } - jcenter() - maven { url "https://jitpack.io" } + maven {url 'http://127.0.0.1:8081/repository/lennon/' } + } + tasks.withType(Javadoc).all { + enabled = false } } diff --git a/conf.gradle b/conf.gradle index 2ee30dc..140efcb 100644 --- a/conf.gradle +++ b/conf.gradle @@ -1,51 +1,111 @@ ext { - + siteUrl = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' + // #修改# // 项目的主页地址,我这里是我的PickerView项目在github的链接地址 + gitUrl = 'https://github.com/lennon19940306/XDroidMvp-AndroidX.git' + // #修改# // 项目 git 地址,我这里同样是用Github上的git地址 + group = "com.lennon.utill" + // #修改# // 组名称,这个相当于依赖的时候 compile 'com.bigkoo:pickerview:1.0.1' “:”号前面的前缀 + userOrg = 'lennon-xia' // 用户名 + repoName = "lennon" // 仓库名 + groupId = 'com.lennon.utill' // groupId + email = '1136160757@qq.com' + website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 + licences = ['Apache-2.0']// 描述 , 填写licences +// ext{ +// userOrg = 'lennon-xia' // 用户名 +// repoName = "lennon" // 仓库名 +// groupId = 'com.lennon.utill' // groupId +// desc = 'useful tool for android' // 描述 +// website = 'https://github.com/lennon19940306/XDroidMvp-AndroidX' // 地址 , 填写真实可访问地址 +// licences = ['Apache-2.0']// 描述 , 填写licences +// } android = [ - compileSdkVersion: 27, - - minSdkVersion : 15, - targetSdkVersion : 26, + compileSdkVersion: 29, + minSdkVersion : 17, + targetSdkVersion : 29, versionCode : 1, versionName : '1.0.0', - VSupportSdk : '27.1.1', - VRetrofitSdk : "2.2.0", - VOkhttp : "3.4.2", - VRxlifecycle : "2.0.1" + VRetrofitSdk : '2.9.0', + VOkhttp : '4.9.1', + VRxlifecycle : '3.1.0', + + NavigationSDK : "2.3.5", + ] dependencies = [ - "appcompat-v7" : "com.android.support:appcompat-v7:${android["VSupportSdk"]}", - "support-v4" : "com.android.support:support-v4:${android["VSupportSdk"]}", - "design" : "com.android.support:design:${android["VSupportSdk"]}", - "annotations" : "com.android.support:support-annotations:${android["VSupportSdk"]}", - "recyclerview-v7" : "com.android.support:recyclerview-v7:${android["VSupportSdk"]}", - - "butterknife" : "com.jakewharton:butterknife:8.4.0", - "butterknife-apt" : "com.jakewharton:butterknife-compiler:8.4.0", - "eventbus" : "org.greenrobot:eventbus:3.0.0", - "glide" : "com.github.bumptech.glide:glide:4.8.0", - "glide-compiler" : "com.github.bumptech.glide:compiler:4.8.0", - "picasso" : "com.squareup.picasso:picasso:2.5.2", - "xrecyclerview" : "com.github.limedroid:ARecyclerView:v1.2.3", - "avi-loading" : "com.wang.avi:library:1.0.2", - "rxbus" : "com.blankj:rxbus:1.2", - - "gson" : "com.google.code.gson:gson:2.6.2", - "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.0.1", - "rxjava" : "io.reactivex.rxjava2:rxjava:2.0.1", + "viewpager2" : "androidx.viewpager2:viewpager2:1.0.0", + + "annotations" : "androidx.annotation:annotation:1.2.0", + + "constraintlayout" : "androidx.constraintlayout:constraintlayout:2.0.4", + + "glide" : "com.github.bumptech.glide:glide:4.12.0", + "glide-compiler" : "com.github.bumptech.glide:compiler:4.12.0", + + + "zxing" : "com.google.zxing:core:3.4.1", + + "navigation-fragment-ktx" : "androidx.navigation:navigation-fragment-ktx:${android["NavigationSDK"]}", + "navigation-ui-ktx" : "androidx.navigation:navigation-ui-ktx:${android["NavigationSDK"]}", + + "navigation-ui" : "androidx.navigation:navigation-ui:${android["NavigationSDK"]}", + "navigation-fragment" : "androidx.navigation:navigation-fragment:${android["NavigationSDK"]}", + + + + "exifinterface" : "androidx.exifinterface:exifinterface:1.3.2", + + "multidex" : "androidx.multidex:multidex:2.0.1", + + "kotlin" : "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version", + + "PersistentCookieJar" : "com.github.franmontiel:PersistentCookieJar:1.0.1", + + "qiujuer-ui" : "net.qiujuer.genius:ui:2.1.1", + + "appcompat-v7" : "androidx.appcompat:appcompat:1.2.0", + + "support-v4" : "androidx.legacy:legacy-support-v4:1.0.0", + + "design" : "com.google.android.material:material:1.2.1", + + "recyclerview-v7" : "androidx.recyclerview:recyclerview:1.2.1", + + "picasso" : "com.squareup.picasso:picasso:2.8", + + "xrecyclerview" : "com.lennon.utill:xrecycler:1.0.7", + + "xdroid-mvp" : "com.lennon.utill:mvp:1.3.35", + + "avi-loading" : "com.wang.avi:library:2.1.3",//https://github.com/81813780/AVLoadingIndicatorView + "rxbus" : "com.blankj:rxbus:1.6", + + "gson" : "com.google.code.gson:gson:2.8.7", + "rxandroid" : "io.reactivex.rxjava2:rxandroid:2.1.1", + "rxjava" : "io.reactivex.rxjava2:rxjava:2.2.21", + "retrofit" : "com.squareup.retrofit2:retrofit:${android["VRetrofitSdk"]}", "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${android["VRetrofitSdk"]}", "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${android["VRetrofitSdk"]}", "okhttp3-logging-interceptor": "com.squareup.okhttp3:logging-interceptor:${android["VOkhttp"]}", "okhttp3" : "com.squareup.okhttp3:okhttp:${android["VOkhttp"]}", - "rxlifecycle" : "com.trello.rxlifecycle2:rxlifecycle:${android["VRxlifecycle"]}", - "rxlifecycle-android" : "com.trello.rxlifecycle2:rxlifecycle-android:${android["VRxlifecycle"]}", - "rxlifecycle-components" : "com.trello.rxlifecycle2:rxlifecycle-components:${android["VRxlifecycle"]}", - "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar", - "canary-debug" : "com.squareup.leakcanary:leakcanary-android:1.4-beta2", - "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2", + "rxlifecycle" : "com.trello.rxlifecycle3:rxlifecycle:${android["VRxlifecycle"]}", + + "rxlifecycle-android" : "com.trello.rxlifecycle3:rxlifecycle-android:${android["VRxlifecycle"]}", + + "rxlifecycle-components" : "com.trello.rxlifecycle3:rxlifecycle-components:${android["VRxlifecycle"]}", + + "rxpermissions" : "com.tbruyelle.rxpermissions2:rxpermissions:0.9.5", + + "canary-debug" : "com.squareup.leakcanary:leakcanary-android:2.7", + "canary-release" : "com.squareup.leakcanary:leakcanary-android-no-op:1.6.3", + + "XStateController" : "com.github.limedroid:XStateController:v1.1.1", + + "nativelogger" : "com.lennon.utill:nativelogger:1.1" ] } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index aac7c9b..4755f39 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,13 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m + +kotlin.code.style=official +android.enableJetifier=true +org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" +android.useAndroidX=true +android.debug.obsoleteApi=true +android.obsoleteApi=true # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372ae..5c2d1cf 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9f26c29..6559024 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Apr 13 19:19:31 CST 2018 +#Wed Dec 30 10:53:20 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/mvp/build.gradle b/mvp/build.gradle index a1d9d61..de2d498 100644 --- a/mvp/build.gradle +++ b/mvp/build.gradle @@ -1,7 +1,10 @@ apply plugin: 'com.android.library' -apply plugin: 'com.github.dcendents.android-maven' +apply plugin: 'kotlin-android' -group = 'com.github.limedroid' +apply plugin: 'kotlin-kapt' + +version = '1.3.35' +group = rootProject.ext.groupId android { compileSdkVersion rootProject.ext.android.compileSdkVersion @@ -12,15 +15,22 @@ android { versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName } + buildFeatures { + viewBinding true + } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - buildToolsVersion '27.0.3' -} + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + +} dependencies { api fileTree(dir: 'libs', include: ['*.jar']) api rootProject.ext.dependencies["appcompat-v7"] @@ -30,9 +40,10 @@ dependencies { api rootProject.ext.dependencies["recyclerview-v7"] api rootProject.ext.dependencies["xrecyclerview"] - api rootProject.ext.dependencies["butterknife"] + api rootProject.ext.dependencies["glide"] annotationProcessor rootProject.ext.dependencies["glide-compiler"] + kapt rootProject.ext.dependencies["glide-compiler"] api rootProject.ext.dependencies["gson"] api rootProject.ext.dependencies["rxandroid"] @@ -41,23 +52,47 @@ dependencies { api rootProject.ext.dependencies["retrofit-converter-gson"] api rootProject.ext.dependencies["retrofit-adapter-rxjava"] api rootProject.ext.dependencies["okhttp3"] + api rootProject.ext.dependencies["rxbus"] api rootProject.ext.dependencies["rxlifecycle"] api rootProject.ext.dependencies["rxlifecycle-android"] api rootProject.ext.dependencies["rxlifecycle-components"] api rootProject.ext.dependencies["rxpermissions"] + + api rootProject.ext.dependencies["kotlin"] + + api rootProject.ext.dependencies["nativelogger"] + + api rootProject.ext.dependencies["viewpager2"] + } tasks.withType(JavaCompile) { options.encoding = "UTF-8" } +// 生成sourceJar和javaDocJar构件 task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} artifacts { + archives javadocJar archives sourcesJar } + +//apply from: 'uploadLocal.gradle' +//apply from: 'uploadAliRelease.gradle' +apply from: 'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/mvp/src/main/AndroidManifest.xml b/mvp/src/main/AndroidManifest.xml index 45033fc..3c1c3e3 100644 --- a/mvp/src/main/AndroidManifest.xml +++ b/mvp/src/main/AndroidManifest.xml @@ -1,8 +1,9 @@ + + android:supportsRtl="true"> diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java new file mode 100644 index 0000000..04ec8aa --- /dev/null +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/XDroidMvpUtill.java @@ -0,0 +1,56 @@ +package cn.droidlover.xdroidmvp; + +import android.app.Service; +import android.content.Context; +import android.os.Vibrator; + +import androidx.viewbinding.ViewBinding; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import cn.droidlover.xdroidmvp.mvp.XActivity; +import cn.droidlover.xdroidmvp.mvp.XFragment; +import cn.droidlover.xdroidmvp.mvp.XPresentation; + +public class XDroidMvpUtill { + public static void vibrator(Context context) { + //获取系统震动服务 + Vibrator vib = (Vibrator) context.getSystemService(Service.VIBRATOR_SERVICE); + //震动70毫秒 + vib.vibrate(70); + } + + public static Class getViewBindingClass(Class _class) { + if (_class == null) { + return null; + } + if (_class == XActivity.class || _class == XFragment.class || _class == XPresentation.class || _class == Object.class) { + return null; + } + Type genType = _class.getGenericSuperclass(); + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + if (params.length > 0) { + if (params.length == 1) { + return getViewBindingClass(_class.getSuperclass()); + } else { + for (int i = 1; i < params.length; i++) { + Class c = (Class) params[i]; + if (c == ViewBinding.class) { + return null; + } + if (ViewBinding.class.isAssignableFrom(c)){ + return (Class)c; + } +// if (cs == ViewBinding.class) { +// return (Class) c; +// } + } + } + } else { + return getViewBindingClass(_class.getSuperclass()); + } + return null; + } + +} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java index 2e76bd3..17524ca 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleRecAdapter.java @@ -1,11 +1,12 @@ package cn.droidlover.xdroidmvp.base; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xrecyclerview.RecyclerAdapter; /** @@ -13,6 +14,7 @@ */ public abstract class SimpleRecAdapter extends RecyclerAdapter { + public static final int TAG_VIEW = 0; public SimpleRecAdapter(Context context) { super(context); @@ -24,6 +26,36 @@ public F onCreateViewHolder(ViewGroup parent, int viewType) { return newViewHolder(view); } + @Override + public void onBindViewHolder(final F holder, final int position) { + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemClick(position, d, TAG_VIEW, holder); + } + } + }); + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + XDroidMvpUtill.vibrator(context); + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemLongClick(position, d, TAG_VIEW, holder); + } + return true; + } + }); + } + public abstract F newViewHolder(View itemView); public abstract int getLayoutId(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java new file mode 100644 index 0000000..53a5fff --- /dev/null +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/SimpleViewBindingAdapter.java @@ -0,0 +1,75 @@ +package cn.droidlover.xdroidmvp.base; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewbinding.ViewBinding; + +import cn.droidlover.xdroidmvp.XDroidMvpUtill; +import cn.droidlover.xrecyclerview.RecyclerAdapter; + +public abstract class SimpleViewBindingAdapter extends RecyclerAdapter> { + public static final int TAG_VIEW = 0; + + public SimpleViewBindingAdapter(Context context) { + super(context); + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + F v; + + public ViewHolder(F viewBinding) { + super(viewBinding.getRoot()); + v = viewBinding; + } + + public F getV() { + return v; + } + } + + @Override + public void onBindViewHolder(final ViewHolder holder, final int position) { + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemClick(position, d, TAG_VIEW, holder); + } + } + }); + holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + XDroidMvpUtill.vibrator(context); + if (getRecItemClick() != null) { + T d = null; + if (position < data.size() && position >= 0) { + d = data.get(position); + } + getRecItemClick().onItemLongClick(position, d, TAG_VIEW, holder); + } + return true; + } + }); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new ViewHolder(getViewBinding(viewType, LayoutInflater.from(context), parent)); + } + + + /* + * 获取 ViewBinding 实现类 + * */ + protected abstract E getViewBinding(@NonNull int viewType, @NonNull LayoutInflater from, @NonNull ViewGroup parent); +} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/XFragmentAdapter.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/XFragmentAdapter.java index 7c67710..bf8f5d9 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/base/XFragmentAdapter.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/base/XFragmentAdapter.java @@ -1,8 +1,8 @@ package cn.droidlover.xdroidmvp.base; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; import java.util.ArrayList; import java.util.List; @@ -10,7 +10,6 @@ /** * Created by wanglei on 2016/12/10. */ - public class XFragmentAdapter extends FragmentPagerAdapter { private List fragmentList = new ArrayList<>(); private String[] titles; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java index 203b6db..7593ca1 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskCache.java @@ -58,7 +58,9 @@ public void put(String key, String value) { } public void put(String key, String value, long expireMills) { - if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) return; + if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) { + return; + } String name = getMd5Key(key); try { @@ -82,6 +84,7 @@ public void put(String key, Object value) { } + @Override public String get(String key) { try { String md5Key = getMd5Key(key); @@ -115,6 +118,7 @@ public String get(String key) { return null; } + @Override public void remove(String key) { try { cache.remove(getMd5Key(key)); @@ -123,6 +127,7 @@ public void remove(String key) { } } + @Override public boolean contains(String key) { try { DiskLruCache.Snapshot snapshot = cache.get(getMd5Key(key)); @@ -133,6 +138,7 @@ public boolean contains(String key) { return false; } + @Override public void clear() { try { cache.delete(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java index c096b9b..ac2024f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/DiskLruCache.java @@ -16,7 +16,7 @@ package cn.droidlover.xdroidmvp.cache; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.io.BufferedInputStream; import java.io.BufferedWriter; @@ -688,6 +688,7 @@ public synchronized void flush() throws IOException { /** * Closes this cache. Stored values will remain on the filesystem. */ + @Override public synchronized void close() throws IOException { if (journalWriter == null) { return; // already closed diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java index c2c0ef8..63f1298 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/cache/MemoryCache.java @@ -1,6 +1,6 @@ package cn.droidlover.xdroidmvp.cache; -import android.support.v4.util.LruCache; +import androidx.collection.LruCache; import android.text.TextUtils; /** @@ -33,7 +33,9 @@ public static MemoryCache getInstance() { @Override public synchronized void put(String key, Object value) { - if (TextUtils.isEmpty(key)) return; + if (TextUtils.isEmpty(key)) { + return; + } if (cache.get(key) != null) { cache.remove(key); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/event/IBus.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/event/IBus.java index 2c623ee..d22197b 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/event/IBus.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/event/IBus.java @@ -1,5 +1,6 @@ package cn.droidlover.xdroidmvp.event; + /** * Created by wanglei on 2016/12/22. */ @@ -14,9 +15,7 @@ public interface IBus { void postSticky(AbsEvent event); - abstract class AbsEvent { public abstract int getTag(); } - } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java index 029e94c..a18d71c 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/GlideLoader.java @@ -4,10 +4,9 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.support.v4.graphics.drawable.RoundedBitmapDrawable; -import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; + +import androidx.annotation.Nullable; + import android.widget.ImageView; import com.bumptech.glide.Glide; @@ -19,7 +18,6 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners; import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.BitmapImageViewTarget; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.DrawableCrossFadeFactory; import com.bumptech.glide.request.transition.Transition; @@ -79,6 +77,9 @@ public void loadNet(Context context, String url, Options options, final LoadCall @Override public void onLoadFailed(@Nullable Drawable errorDrawable) { super.onLoadFailed(errorDrawable); + if (callback != null) { + callback.onLoadFailed(); + } } @Override @@ -87,6 +88,10 @@ public void onResourceReady(Drawable resource, Transition tran if (callback != null) { callback.onLoadReady(resource); } + } else { + if (callback != null) { + callback.onLoadFailed(); + } } } @@ -133,28 +138,27 @@ private RequestOptions wrapScaleType(Options options) { .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .priority(Priority.HIGH); - if (options != null){ + if (options != null) { + if (options.loadingResId != Options.RES_NONE) { + request.placeholder(options.loadingResId); + } + if (options.loadErrorResId != Options.RES_NONE) { + request.error(options.loadErrorResId); + } if (options.scaleType != null) { - if (options.loadingResId != Options.RES_NONE) { - request.placeholder(options.loadingResId); - } - if (options.loadErrorResId != Options.RES_NONE) { - request.error(options.loadErrorResId); - } - switch (options.scaleType) { case MATRIX: case FIT_XY: case FIT_START: case FIT_END: case CENTER: - case CENTER_INSIDE: - break; - case FIT_CENTER: request.fitCenter(); break; - + default: + case CENTER_INSIDE: + request.centerInside(); + break; case CENTER_CROP: request.centerCrop(); break; @@ -162,7 +166,7 @@ private RequestOptions wrapScaleType(Options options) { } else { request.centerCrop(); } - }else { + } else { request.centerCrop(); } @@ -183,6 +187,41 @@ public void loadCircle(String url, final ImageView target, Options options) { } + @Override + public void loadCircle(String url, Context context, Options options, final LoadCallback callback) { + RequestOptions requestOptions = wrapScaleType(options); + requestOptions.optionalCircleCrop(); + + getRequestManager(context) + .load(url) + .apply(requestOptions) + .transition(withCrossFade()) + .into(new SimpleTarget() { + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (callback != null) { + callback.onLoadFailed(); + } + } + + @Override + public void onResourceReady(Drawable resource, Transition transition) { + if (resource != null) { + if (callback != null) { + callback.onLoadReady(resource); + } + } else { + if (callback != null) { + callback.onLoadFailed(); + } + } + } + + }); + } + //加载圆形图片 @Override public void loadCorner(String url, final ImageView target, int radius, Options options) { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java index 26b0361..1fc3067 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/ILoader.java @@ -35,6 +35,8 @@ public interface ILoader { void loadCircle(String url, ImageView target, Options options); + void loadCircle(String url, Context context, Options options, LoadCallback callback); + void loadCorner(String url, ImageView target, int radius, Options options); class Options { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java index c5192fe..c576967 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/imageloader/LoadCallback.java @@ -7,9 +7,11 @@ */ public abstract class LoadCallback { - void onLoadFailed() {} + public void onLoadFailed() { + } public abstract void onLoadReady(Drawable drawable); - void onLoadCanceled() {} + public void onLoadCanceled() { + } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java index de0d061..af523b3 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/Kits.java @@ -1,5 +1,6 @@ package cn.droidlover.xdroidmvp.kit; +import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -144,6 +145,7 @@ public static boolean isSystemApplication(Context context, String packageName) { * @param packageName * @return */ + @SuppressLint("NewApi") public static Boolean isTopActivity(Context context, String packageName) { if (context == null || TextUtils.isEmpty(packageName)) { return null; @@ -198,11 +200,12 @@ public static String getAppMetaData(Context context, String key) { * @param context * @return */ + @SuppressLint("NewApi") public static boolean isApplicationInBackground(Context context) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List taskList = am.getRunningTasks(1); if (taskList != null && !taskList.isEmpty()) { - ComponentName topActivity = taskList.get(0).topActivity; + ComponentName topActivity = taskList.get(0).topActivity; if (topActivity != null && !topActivity.getPackageName().equals(context.getPackageName())) { return true; } @@ -1011,7 +1014,7 @@ public static long getFirstOfMonth(long mills) { } - + @SuppressLint("MissingPermission") public static class NetWork { public static final String NETWORK_TYPE_WIFI = "wifi"; public static final String NETWORK_TYPE_3G = "eg"; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java deleted file mode 100644 index 9b08959..0000000 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/kit/KnifeKit.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.droidlover.xdroidmvp.kit; - -import android.app.Activity; -import android.app.Dialog; -import android.view.View; - -import butterknife.ButterKnife; -import butterknife.Unbinder; - -/** - * Created by wanglei on 2016/11/27. - */ - -public class KnifeKit { - - public static Unbinder bind(Object target) { - if (target instanceof Activity) { - return ButterKnife.bind((Activity) target); - } else if (target instanceof Dialog) { - return ButterKnife.bind((Dialog) target); - } else if (target instanceof View) { - return ButterKnife.bind((View) target); - } - return Unbinder.EMPTY; - } - - - public static Unbinder bind(Object target, Object source) { - if (source instanceof Activity) { - return ButterKnife.bind(target, (Activity) source); - } else if (source instanceof Dialog) { - return ButterKnife.bind(target, (Dialog) source); - } else if (source instanceof View) { - return ButterKnife.bind(target, (View) source); - } - return Unbinder.EMPTY; - } - - - public static void unbind(Unbinder unbinder) { - if (unbinder != Unbinder.EMPTY) { - unbinder.unbind(); - } - } -} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java index 85037aa..46159d2 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/LogFormat.java @@ -28,20 +28,12 @@ public class LogFormat { private static final char VERTICAL_BORDER_CHAR = '║'; - // Length: 100. private static final String TOP_HORIZONTAL_BORDER = - "╔═════════════════════════════════════════════════" + - "══════════════════════════════════════════════════"; - - // Length: 99. - private static final String DIVIDER_HORIZONTAL_BORDER = - "╟─────────────────────────────────────────────────" + - "──────────────────────────────────────────────────"; - - // Length: 100. - private static final String BOTTOM_HORIZONTAL_BORDER = - "╚═════════════════════════════════════════════════" + - "══════════════════════════════════════════════════"; + "╔═══════════════════════════════════════════════════════════════════════════════════════════════════"; + + private static final String DIVIDER_HORIZONTAL_BORDER = "╟───────────────────────────────────────────────────────────────────────────────────────────────────"; + + private static final String BOTTOM_HORIZONTAL_BORDER = "╚═══════════════════════════════════════════════════════════════════════════════════════════════════"; public static String formatJson(String json) { String formatted = null; diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java index 418091c..c3418e3 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/log/XLog.java @@ -4,6 +4,8 @@ import android.util.Log; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.jesse.nativelogger.NLogger; +import cn.jesse.nativelogger.logger.LoggerLevel; /** * Created by wanglei on 2016/11/29. @@ -15,25 +17,26 @@ public class XLog { public static String TAG_ROOT = XDroidConf.LOG_TAG; public static void json(String json) { - json(Log.DEBUG, null, json); +// json(Log.DEBUG, null, json); + NLogger.json(LoggerLevel.DEBUG, json); } - public static void json(int logLevel, String tag, String json) { + public static void json(LoggerLevel logLevel, String tag, String json) { if (LOG) { String formatJson = LogFormat.formatBorder(new String[]{LogFormat.formatJson(json)}); - XPrinter.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatJson); + NLogger.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatJson); } } public static void xml(String xml) { - xml(Log.DEBUG, null, xml); + xml(LoggerLevel.DEBUG, null, xml); } - public static void xml(int logLevel, String tag, String xml) { + public static void xml(LoggerLevel logLevel, String tag, String xml) { if (LOG) { String formatXml = LogFormat.formatBorder(new String[]{LogFormat.formatXml(xml)}); - XPrinter.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatXml); + NLogger.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatXml); } } @@ -44,31 +47,31 @@ public static void error(Throwable throwable) { public static void error(String tag, Throwable throwable) { if (LOG) { String formatError = LogFormat.formatBorder(new String[]{LogFormat.formatThrowable(throwable)}); - XPrinter.println(Log.ERROR, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatError); + NLogger.println(LoggerLevel.ERROR, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatError); } } - private static void msg(int logLevel, String tag, String format, Object... args) { + private static void msg(LoggerLevel logLevel, String tag, String format, Object... args) { if (LOG) { String formatMsg = LogFormat.formatBorder(new String[]{LogFormat.formatArgs(format, args)}); - XPrinter.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatMsg); + NLogger.println(logLevel, TextUtils.isEmpty(tag) ? TAG_ROOT : tag, formatMsg); } } public static void d(String msg, Object... args) { - msg(Log.DEBUG, null, msg, args); + msg(LoggerLevel.DEBUG, null, msg, args); } public static void d(String tag, String msg, Object... args) { - msg(Log.DEBUG, tag, msg, args); + msg(LoggerLevel.DEBUG, tag, msg, args); } public static void e(String msg, Object... args) { - msg(Log.ERROR, null, msg, args); + msg(LoggerLevel.ERROR, null, msg, args); } public static void e(String tag, String msg, Object... args) { - msg(Log.ERROR, tag, msg, args); + msg(LoggerLevel.ERROR, tag, msg, args); } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java index 2d2f075..814256a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IPresent.java @@ -4,7 +4,7 @@ * Created by wanglei on 2016/12/29. */ -public interface IPresent { +public interface IPresent { void attachV(V view); void detachV(); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java index 56c3224..b8ed80f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/IView.java @@ -3,12 +3,13 @@ import android.os.Bundle; import android.view.View; +import androidx.viewbinding.ViewBinding; + /** * Created by wanglei on 2016/12/29. */ -public interface IView

{ - void bindUI(View rootView); +public interface IView

{ void bindEvent(); @@ -16,9 +17,13 @@ public interface IView

{ int getOptionsMenuId(); - int getLayoutId(); - boolean useEventBus(); P newP(); + + void showProgressDialog(String msg); + + void closeProgressDialog(); + + void onRefresh(Boolean bRefresh); } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java deleted file mode 100644 index b3961ab..0000000 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/LazyFragment.java +++ /dev/null @@ -1,256 +0,0 @@ -package cn.droidlover.xdroidmvp.mvp; - -import android.app.Activity; -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.trello.rxlifecycle2.components.support.RxFragment; - -import java.lang.reflect.Field; - -/** - * Created by wanglei on 2017/1/28. - */ - -public class LazyFragment extends RxFragment { - protected LayoutInflater layoutInflater; - protected Activity context; - - private View rootView; - private ViewGroup container; - - private boolean isInitReady = false; - private int isVisibleToUserState = STATE_NO_SET; - private Bundle saveInstanceState; - private boolean isLazyEnable = true; - private boolean isStart = false; - private FrameLayout layout; - - private static final int STATE_VISIBLE = 1; //用户可见 - private static final int STATE_NO_SET = -1; //未设置值 - private static final int STATE_NO_VISIBLE = 0; //用户不可见 - - private static final String TAG_ROOT_FRAMELAYOUT = "tag_root_framelayout"; - - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - this.layoutInflater = inflater; - this.container = container; - onCreateView(savedInstanceState); - if (rootView == null) - return super.onCreateView(inflater, container, savedInstanceState); - - return rootView; - } - - private void onCreateView(Bundle savedInstanceState) { - this.saveInstanceState = savedInstanceState; - boolean isVisible; - if (isVisibleToUserState == STATE_NO_SET) { - isVisible = getUserVisibleHint(); - } else { - isVisible = isVisibleToUserState == STATE_VISIBLE; - } - if (isLazyEnable) { - if (isVisible && !isInitReady) { - onCreateViewLazy(savedInstanceState); - isInitReady = true; - } else { - LayoutInflater mInflater = layoutInflater; - if (mInflater == null && context != null) { - mInflater = LayoutInflater.from(context); - } - layout = new FrameLayout(context); - layout.setTag(TAG_ROOT_FRAMELAYOUT); - - View view = getPreviewLayout(mInflater, layout); - if (view != null) { - layout.addView(view); - } - layout.setLayoutParams( - new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - setContentView(layout); - } - } else { - onCreateViewLazy(savedInstanceState); - isInitReady = true; - } - } - - protected View getRealRootView() { - if (rootView != null) { - if (rootView instanceof FrameLayout - && TAG_ROOT_FRAMELAYOUT.equals(rootView.getTag())) { - return ((FrameLayout) rootView).getChildAt(0); - } - } - - return rootView; - } - - protected View getRootView() { - return rootView; - } - - protected View $(int id) { - if (rootView != null) { - return rootView.findViewById(id); - } - return null; - } - - protected void setContentView(int layoutResID) { - if (isLazyEnable && getRootView() != null && getRootView().getParent() != null) { - layout.removeAllViews(); - View view = layoutInflater.inflate(layoutResID, layout, false); - layout.addView(view); - } else { - rootView = layoutInflater.inflate(layoutResID, container, false); - } - } - - protected void setContentView(View view) { - if (isLazyEnable && getRootView() != null && getRootView().getParent() != null) { - layout.removeAllViews(); - layout.addView(view); - } else { - rootView = view; - } - } - - @Override - public void setUserVisibleHint(boolean isVisibleToUser) { - super.setUserVisibleHint(isVisibleToUser); - isVisibleToUserState = isVisibleToUser ? STATE_VISIBLE : STATE_NO_VISIBLE; - if (isVisibleToUser - && !isInitReady - && getRootView() != null) { - isInitReady = true; - onCreateViewLazy(saveInstanceState); - onResumeLazy(); - } - if (isInitReady && getRootView() != null) { - if (isVisibleToUser) { - isStart = true; - onStartLazy(); - } else { - isStart = false; - onStopLazy(); - } - } - } - - @Override - public void onResume() { - super.onResume(); - if (isInitReady) { - onResumeLazy(); - } - } - - @Override - public void onPause() { - super.onPause(); - if (isInitReady) { - onPauseLazy(); - } - } - - @Override - public void onStart() { - super.onStart(); - if (isInitReady - && !isStart - && getUserVisibleHint()) { - isStart = true; - onStartLazy(); - } - } - - @Override - public void onStop() { - super.onStop(); - if (isInitReady - && isStart - && getUserVisibleHint()) { - isStart = false; - onStopLazy(); - } - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof Activity) { - this.context = (Activity) context; - } - } - - - @Override - public void onDetach() { - super.onDetach(); - context = null; - - try { - Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager"); - childFragmentManager.setAccessible(true); - childFragmentManager.set(this, null); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - - @Override - public void onDestroyView() { - super.onDestroyView(); - rootView = null; - container = null; - layoutInflater = null; - if (isInitReady) onDestoryLazy(); - isInitReady = false; - } - - protected View getPreviewLayout(LayoutInflater mInflater, FrameLayout layout) { - return null; - } - - protected void onCreateViewLazy(Bundle savedInstanceState) { - - } - - protected void onStartLazy() { - - } - - protected void onStopLazy() { - - } - - protected void onResumeLazy() { - - } - - protected void onPauseLazy() { - - } - - protected void onDestoryLazy() { - - } - - -} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java index faccc4d..729aef3 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XActivity.java @@ -2,23 +2,30 @@ import android.app.Activity; import android.os.Bundle; -import android.support.annotation.Nullable; + +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + +import android.view.LayoutInflater; import android.view.Menu; -import android.view.View; import com.tbruyelle.rxpermissions2.RxPermissions; -import com.trello.rxlifecycle2.components.support.RxAppCompatActivity; -import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xdroidmvp.event.BusProvider; -import cn.droidlover.xdroidmvp.kit.KnifeKit; + +import com.trello.rxlifecycle3.components.support.RxAppCompatActivity; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; /** * Created by wanglei on 2016/12/29. */ -public abstract class XActivity

extends RxAppCompatActivity implements IView

{ +public abstract class XActivity

extends RxAppCompatActivity implements IView

{ private VDelegate vDelegate; private P p; @@ -26,28 +33,41 @@ public abstract class XActivity

extends RxAppCompatActivity private RxPermissions rxPermissions; - private Unbinder unbinder; + private E viewBinding; + + @Override + public void onRefresh(Boolean bRefresh) { + + } + protected final E getViewBinding() { + return viewBinding; + } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); context = this; - getP(); - - if (getLayoutId() > 0) { - setContentView(getLayoutId()); - bindUI(null); - bindEvent(); + try { + Class eClass = getViewBindingClass(); + if (eClass != null) { + Method method2 = eClass.getMethod("inflate", LayoutInflater.class); + viewBinding = (E) method2.invoke(null, getLayoutInflater()); + } + } catch (Exception e) { + e.printStackTrace(); + } + if (viewBinding != null) { + setContentView(viewBinding.getRoot()); } + bindEvent(); initData(savedInstanceState); } - @Override - public void bindUI(View rootView) { - unbinder = KnifeKit.bind(this); + protected Class getViewBindingClass() { + return XDroidMvpUtill.getViewBindingClass(getClass()); } protected VDelegate getvDelegate() { diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java index d3db641..e79a807 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XFragment.java @@ -3,51 +3,73 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; + +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.tbruyelle.rxpermissions2.RxPermissions; -import com.trello.rxlifecycle2.components.support.RxFragment; -import butterknife.Unbinder; import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; import cn.droidlover.xdroidmvp.event.BusProvider; -import cn.droidlover.xdroidmvp.kit.KnifeKit; + +import com.trello.rxlifecycle3.components.support.RxFragment; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; /** * Created by wanglei on 2016/12/29. */ -public abstract class XFragment

extends RxFragment implements IView

{ +public abstract class XFragment

extends RxFragment implements IView

{ private VDelegate vDelegate; private P p; protected Activity context; - private View rootView; protected LayoutInflater layoutInflater; private RxPermissions rxPermissions; + private E viewBinding; - private Unbinder unbinder; + @Override + public void onRefresh(Boolean bRefresh) { + + } + protected final E getViewBinding() { + return viewBinding; + } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { layoutInflater = inflater; - if (rootView == null && getLayoutId() > 0) { - rootView = inflater.inflate(getLayoutId(), null); - bindUI(rootView); - } else { - ViewGroup viewGroup = (ViewGroup) rootView.getParent(); + try { + Class eClass = getViewBindingClass(); + if (eClass != null) { + Method method = eClass.getMethod("inflate", LayoutInflater.class, ViewGroup.class, Boolean.TYPE); + viewBinding = (E) method.invoke(null, inflater, container, false); + } + } catch (Exception e) { + e.printStackTrace(); + } + if (viewBinding != null) { + ViewGroup viewGroup = (ViewGroup) viewBinding.getRoot().getParent(); if (viewGroup != null) { - viewGroup.removeView(rootView); + viewGroup.removeView(viewBinding.getRoot()); } } + return viewBinding.getRoot(); + } - return rootView; + private Class getViewBindingClass() { + return XDroidMvpUtill.getViewBindingClass(getClass()); } @@ -55,7 +77,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getP(); - if (useEventBus()) { BusProvider.getBus().register(this); } @@ -63,11 +84,6 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { initData(savedInstanceState); } - @Override - public void bindUI(View rootView) { - unbinder = KnifeKit.bind(this, rootView); - } - protected VDelegate getvDelegate() { if (vDelegate == null) { vDelegate = VDelegateBase.create(context); diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java deleted file mode 100644 index 1482a54..0000000 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XLazyFragment.java +++ /dev/null @@ -1,108 +0,0 @@ -package cn.droidlover.xdroidmvp.mvp; - -import android.os.Bundle; -import android.view.View; - -import com.tbruyelle.rxpermissions2.RxPermissions; - -import butterknife.Unbinder; -import cn.droidlover.xdroidmvp.XDroidConf; -import cn.droidlover.xdroidmvp.event.BusProvider; -import cn.droidlover.xdroidmvp.kit.KnifeKit; - -/** - * Created by wanglei on 2017/1/26. - */ - -public abstract class XLazyFragment

- extends LazyFragment implements IView

{ - - private VDelegate vDelegate; - private P p; - - private RxPermissions rxPermissions; - private Unbinder unbinder; - - @Override - protected void onCreateViewLazy(Bundle savedInstanceState) { - super.onCreateViewLazy(savedInstanceState); - - getP(); - - if (getLayoutId() > 0) { - setContentView(getLayoutId()); - bindUI(getRealRootView()); - } - if (useEventBus()) { - BusProvider.getBus().register(this); - } - bindEvent(); - initData(savedInstanceState); - } - - @Override - public void bindUI(View rootView) { - unbinder = KnifeKit.bind(this, rootView); - } - - @Override - public void bindEvent() { - - } - - - public VDelegate getvDelegate() { - if (vDelegate == null) { - vDelegate = VDelegateBase.create(context); - } - return vDelegate; - } - - protected P getP() { - if (p == null) { - p = newP(); - } - if (p != null) { - if (!p.hasV()) { - p.attachV(this); - } - } - return p; - } - - @Override - protected void onDestoryLazy() { - super.onDestoryLazy(); - if (useEventBus()) { - BusProvider.getBus().unregister(this); - } - if (getP() != null) { - getP().detachV(); - } - getvDelegate().destory(); - - p = null; - vDelegate = null; - } - - - protected RxPermissions getRxPermissions() { - rxPermissions = new RxPermissions(getActivity()); - rxPermissions.setLogging(XDroidConf.DEV); - return rxPermissions; - } - - - @Override - public int getOptionsMenuId() { - return 0; - } - - - @Override - public boolean useEventBus() { - return false; - } - - -} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java new file mode 100644 index 0000000..df81c60 --- /dev/null +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/mvp/XPresentation.java @@ -0,0 +1,116 @@ +package cn.droidlover.xdroidmvp.mvp; + +import android.app.Presentation; +import android.content.Context; +import android.os.Bundle; +import android.view.Display; +import android.view.LayoutInflater; + +import androidx.viewbinding.ViewBinding; + +import com.tbruyelle.rxpermissions2.RxPermissions; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import cn.droidlover.xdroidmvp.XDroidConf; +import cn.droidlover.xdroidmvp.XDroidMvpUtill; +import cn.droidlover.xdroidmvp.event.BusProvider; + +public abstract class XPresentation

extends Presentation implements IView

{ + private P p; + private VDelegate vDelegate; + protected Context context; + private E viewBinding; + private RxPermissions rxPermissions; + + public XPresentation(Context outerContext, Display display) { + super(outerContext, display); + this.context = outerContext; + } + + protected RxPermissions getRxPermissions() { + rxPermissions = new RxPermissions(getOwnerActivity()); + rxPermissions.setLogging(XDroidConf.DEV); + return rxPermissions; + } + + public XPresentation(Context outerContext, Display display, int theme) { + super(outerContext, display, theme); + this.context = outerContext; + } + + @Override + public void onRefresh(Boolean bRefresh) { + + } + + protected final E getViewBinding() { + return viewBinding; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getP(); + try { + Class eClass = getViewBindingClass(); + Method method2 = eClass.getMethod("inflate", LayoutInflater.class); + viewBinding = (E) method2.invoke(null, getLayoutInflater()); + } catch (Exception e) { + e.printStackTrace(); + } + if (viewBinding != null) { + setContentView(viewBinding.getRoot()); + bindEvent(); + } + initData(savedInstanceState); + } + + private Class getViewBindingClass() { + return XDroidMvpUtill.getViewBindingClass(getClass()); + } + + protected VDelegate getvDelegate() { + if (vDelegate == null) { + vDelegate = VDelegateBase.create(context); + } + return vDelegate; + } + + @Override + protected void onStart() { + super.onStart(); + if (useEventBus()) { + BusProvider.getBus().register(this); + } + } + + @Override + public void onDisplayRemoved() { + super.onDisplayRemoved(); + if (useEventBus()) { + BusProvider.getBus().unregister(this); + } + if (getP() != null) { + getP().detachV(); + } + getvDelegate().destory(); + + p = null; + vDelegate = null; + } + + protected P getP() { + if (p == null) { + p = newP(); + } + if (p != null) { + if (!p.hasV()) { + p.attachV(this); + } + } + return p; + } +} diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java index 80c47ab..d56487f 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/ApiSubscriber.java @@ -5,9 +5,11 @@ import org.json.JSONException; -import java.net.UnknownHostException; +import java.io.IOException; +import cn.droidlover.xdroidmvp.mvp.IView; import io.reactivex.subscribers.ResourceSubscriber; +import retrofit2.HttpException; /** @@ -15,15 +17,27 @@ */ public abstract class ApiSubscriber extends ResourceSubscriber { + private IView iView; + public ApiSubscriber() { + } + + public ApiSubscriber(IView iView) { + this.iView = iView; + } @Override public void onError(Throwable e) { NetError error = null; if (e != null) { + e.printStackTrace(); if (!(e instanceof NetError)) { - if (e instanceof UnknownHostException) { + if (e instanceof IOException) { error = new NetError(e, NetError.NoConnectError); + } else if (e instanceof HttpException) { + error = new NetError(e, NetError.HttpError); + HttpException httpException = (HttpException) e; + error.setHttpCode(httpException.code()); } else if (e instanceof JSONException || e instanceof JsonParseException || e instanceof JsonSyntaxException) { @@ -38,6 +52,10 @@ public void onError(Throwable e) { if (useCommonErrorHandler() && XApi.getCommonProvider() != null) { if (XApi.getCommonProvider().handleError(error)) { //使用通用异常处理 + if (iView != null) { + iView.closeProgressDialog(); + } + afterHandleError(); return; } } @@ -46,6 +64,9 @@ public void onError(Throwable e) { } + protected void afterHandleError() { + } + protected abstract void onFail(NetError error); @Override diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java index 9299970..299044b 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/IModel.java @@ -5,6 +5,8 @@ */ public interface IModel { + String getErrorCode(); + boolean isNull(); //空数据 boolean isAuthError(); //验证错误 diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java index 51941fd..f29117a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/LogInterceptor.java @@ -5,6 +5,7 @@ import java.io.IOException; import cn.droidlover.xdroidmvp.log.XLog; +import cn.jesse.nativelogger.logger.LoggerLevel; import okhttp3.Headers; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -35,8 +36,8 @@ private void logRequest(Request request) { String url = request.url().toString(); Headers headers = request.headers(); - XLog.d(TAG, "url : " + url); - XLog.d(TAG, "method : " + request.method()); + XLog.e(TAG, "url : " + url); + XLog.e(TAG, "method : " + request.method()); if (headers != null && headers.size() > 0) { XLog.e(TAG, "headers : " + headers.toString()); } @@ -45,9 +46,9 @@ private void logRequest(Request request) { MediaType mediaType = requestBody.contentType(); if (mediaType != null) { if (isText(mediaType)) { - XLog.d(TAG, "params : " + bodyToString(request)); + XLog.e(TAG, "params : " + bodyToString(request)); } else { - XLog.d(TAG, "params : " + " maybe [file part] , too large too print , ignored!"); + XLog.e(TAG, "params : " + " maybe [file part] , too large too print , ignored!"); } } } @@ -65,13 +66,17 @@ private Response logResponse(Response response) { MediaType mediaType = body.contentType(); if (mediaType != null) { if (isText(mediaType)) { + XLog.e(TAG, "url : " + response.request().url()); String resp = body.string(); - XLog.json(Log.DEBUG, TAG, resp); - + XLog.e(TAG, resp); + Headers headers = response.headers(); + if (headers != null && headers.size() > 0) { + XLog.e(TAG, "headers : " + headers.toString()); + } body = ResponseBody.create(mediaType, resp); return response.newBuilder().body(body).build(); } else { - XLog.d(TAG, "data : " + " maybe [file part] , too large too print , ignored!"); + XLog.e(TAG, "data : " + " maybe [file part] , too large too print , ignored!"); } } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java index 3b78762..a84edbf 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/NetError.java @@ -7,6 +7,8 @@ public class NetError extends Exception { private Throwable exception; private int type = NoConnectError; + private String code; + private int httpCode; public static final int ParseError = 0; //数据解析异常 public static final int NoConnectError = 1; //无连接异常 @@ -14,6 +16,7 @@ public class NetError extends Exception { public static final int NoDataError = 3; //无数据返回异常 public static final int BusinessError = 4; //业务异常 public static final int OtherError = 5; //其他异常 + public static final int HttpError = 6; public NetError(Throwable exception, int type) { this.exception = exception; @@ -25,6 +28,18 @@ public NetError(String detailMessage, int type) { this.type = type; } + public NetError(Throwable exception, int type, String code) { + this.exception = exception; + this.type = type; + this.code = code; + } + + public NetError(String detailMessage, int type, String code) { + super(detailMessage); + this.type = type; + this.code = code; + } + @Override public String getMessage() { if (exception != null) return exception.getMessage(); @@ -34,4 +49,24 @@ public String getMessage() { public int getType() { return type; } + + public Throwable getThrowable() { + return exception; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public int getHttpCode() { + return httpCode; + } + + public void setHttpCode(int httpCode) { + this.httpCode = httpCode; + } } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java index b11b109..2effc4a 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/net/XApi.java @@ -103,7 +103,9 @@ private OkHttpClient getClient(String baseUrl, NetProvider provider) { if (Kits.Empty.check(baseUrl)) { throw new IllegalStateException("baseUrl can not be null"); } - if (clientMap.get(baseUrl) != null) return clientMap.get(baseUrl); + if (clientMap.get(baseUrl) != null) { + return clientMap.get(baseUrl); + } checkProvider(provider); @@ -201,13 +203,14 @@ public Publisher apply(Flowable upstream) { return upstream.flatMap(new Function>() { @Override public Publisher apply(T model) throws Exception { - - if (model == null || model.isNull()) { + if (model == null) { return Flowable.error(new NetError(model.getErrorMsg(), NetError.NoDataError)); + } else if (model.isNull()) { + return Flowable.error(new NetError(model.getErrorMsg(), NetError.NoDataError, model.getErrorCode())); } else if (model.isAuthError()) { - return Flowable.error(new NetError(model.getErrorMsg(), NetError.AuthError)); + return Flowable.error(new NetError(model.getErrorMsg(), NetError.AuthError, model.getErrorCode())); } else if (model.isBizError()) { - return Flowable.error(new NetError(model.getErrorMsg(), NetError.BusinessError)); + return Flowable.error(new NetError(model.getErrorMsg(), NetError.BusinessError, model.getErrorCode())); } else { return Flowable.just(model); } diff --git a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java index dd20f80..80e87ce 100644 --- a/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java +++ b/mvp/src/main/java/cn/droidlover/xdroidmvp/router/Router.java @@ -4,13 +4,14 @@ import android.content.Intent; import android.os.Bundle; import android.os.Parcelable; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.ActivityOptionsCompat; import java.io.Serializable; import java.util.ArrayList; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; + import cn.droidlover.xdroidmvp.XDroidConf; /** @@ -29,7 +30,7 @@ public class Router { public static final int RES_NONE = -1; - private static RouterCallback callback; + private RouterCallback callback; private Router() { intent = new Intent(); @@ -63,6 +64,11 @@ public Router putBundle(@Nullable String key, Bundle bundle) { return this; } + public Router setBundle(@Nullable Bundle bundle) { + this.data = bundle; + return this; + } + public Router putByte(@Nullable String key, byte value) { getBundleData().putByte(key, value); return this; @@ -103,6 +109,7 @@ public Router putCharSequence(@Nullable String key, @Nullable CharSequence value return this; } + public Router putParcelable(@Nullable String key, @Nullable Parcelable value) { getBundleData().putParcelable(key, value); return this; @@ -206,11 +213,13 @@ private Bundle getBundleData() { return data; } + public static void pop(Activity activity) { activity.finish(); } - public static void setCallback(RouterCallback callback) { - Router.callback = callback; + public Router setCallback(RouterCallback callback) { + this.callback = callback; + return this; } } diff --git a/mvp/uploadAliRelease.gradle b/mvp/uploadAliRelease.gradle new file mode 100644 index 0000000..9f68b7b --- /dev/null +++ b/mvp/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'mvp' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/mvp/uploadAliSnapshot.gradle b/mvp/uploadAliSnapshot.gradle new file mode 100644 index 0000000..e93988c --- /dev/null +++ b/mvp/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'mvp' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/mvp/uploadLocal.gradle b/mvp/uploadLocal.gradle new file mode 100644 index 0000000..ce193a1 --- /dev/null +++ b/mvp/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'mvp' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/nativelogger/build.gradle b/nativelogger/build.gradle new file mode 100644 index 0000000..299e7a8 --- /dev/null +++ b/nativelogger/build.gradle @@ -0,0 +1,56 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + api fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies["kotlin"] +} +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} +version = '1.2' +group = rootProject.groupId + +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/nativelogger/proguard-rules.pro b/nativelogger/proguard-rules.pro new file mode 100644 index 0000000..de1a1e5 --- /dev/null +++ b/nativelogger/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/jesse/Platform/android-sdk-macosx/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/nativelogger/src/main/AndroidManifest.xml b/nativelogger/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2c5cbd5 --- /dev/null +++ b/nativelogger/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt new file mode 100644 index 0000000..85990fe --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/Logger.kt @@ -0,0 +1,12 @@ +package cn.jesse.nativelogger + +import cn.jesse.nativelogger.logger.LoggerLevel + +/** + * 注解模式初始化: 提供日志TAG 和日志等级快捷设置 + * + * @author Jesse + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE) +annotation class Logger(val tag: String = "NLogger", val level: LoggerLevel = LoggerLevel.WARN) \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt new file mode 100644 index 0000000..1212065 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/NLogger.kt @@ -0,0 +1,330 @@ +package cn.jesse.nativelogger + +import cn.jesse.nativelogger.logger.LoggerLevel +import cn.jesse.nativelogger.logger.base.IFileLogger + +/** + * NLogger 静态工具类 + * + * @author Jesse + */ +object NLogger { + + /** + * 压缩日志文件 + * + * @params listener 子线程回调 + */ + @JvmStatic + fun zipLogs(listener: (succeed: Boolean, target: String) -> Unit) { + val fileLogger = NLoggerConfig.getInstance().getFileLogger() as IFileLogger? + + if (null == fileLogger) { + w("unexpected zip logs, file logger is null") + return + } + fileLogger.zipLogs(listener) + } + + @JvmStatic + fun i(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().info(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } else { + NLoggerConfig.getInstance().getFileLogger()?.info(msg) + } + } + + @JvmStatic + fun i(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, msg) + } + + @JvmStatic + fun i(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, format, arg) + } + + @JvmStatic + fun i(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, format, argA, argB) + } + + @JvmStatic + fun i(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, format, *args) + } + + @JvmStatic + fun i(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().info(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.info(tag, ex) + } + + @JvmStatic + fun d(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().debug(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(msg) + } + + @JvmStatic + fun d(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, msg) + } + + @JvmStatic + fun d(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, format, arg) + } + + @JvmStatic + fun d(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, format, argA, argB) + } + + @JvmStatic + fun d(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, format, *args) + } + + @JvmStatic + fun d(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().debug(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.debug(tag, ex) + } + + @JvmStatic + fun w(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().warn(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(msg) + } + + @JvmStatic + fun w(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, msg) + } + + @JvmStatic + fun w(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, format, arg) + } + + @JvmStatic + fun w(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, format, argA, argB) + } + + @JvmStatic + fun w(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, format, *args) + } + + @JvmStatic + fun w(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().warn(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.warn(tag, ex) + } + + @JvmStatic + fun e(msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().error(msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(msg) + } + + @JvmStatic + fun e(tag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, msg) + } + + @JvmStatic + fun e(tag: String, format: String, arg: Any) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, format, arg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, format, arg) + } + + @JvmStatic + fun e(tag: String, format: String, argA: Any, argB: Any) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, format, argA, argB) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, format, argA, argB) + } + + @JvmStatic + fun e(tag: String, format: String, vararg args: Any) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, format, *args) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, format, *args) + } + + @JvmStatic + fun e(tag: String, ex: Throwable) { + NLoggerConfig.getInstance().getDefaultLogger().error(tag, ex) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.error(tag, ex) + } + + @JvmStatic + fun json(level: LoggerLevel, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().json(level, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.json(level, msg) + } + + @JvmStatic + fun json(level: LoggerLevel, subTag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().json(level, subTag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.json(level, subTag, msg) + } + + @JvmStatic + fun log(level: LoggerLevel, subTag: String, msg: String) { + NLoggerConfig.getInstance().getDefaultLogger().log(level, subTag, msg) + + if (NLoggerConfig.getInstance().getFileLogger() == null) { + return + } + + NLoggerConfig.getInstance().getFileLogger()?.log(level, subTag, msg) + } + + @JvmStatic + fun println(logLevel: LoggerLevel, subTag: String, formatJson: String) { + log(logLevel, subTag, formatJson) + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt new file mode 100644 index 0000000..ea97a54 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerConfig.kt @@ -0,0 +1,247 @@ +package cn.jesse.nativelogger + +import android.os.Environment +import android.text.TextUtils +import cn.jesse.nativelogger.formatter.SimpleFormatter +import cn.jesse.nativelogger.logger.AndroidLogger +import cn.jesse.nativelogger.logger.FileLogger +import cn.jesse.nativelogger.logger.LoggerLevel +import cn.jesse.nativelogger.logger.base.IFileLogger +import cn.jesse.nativelogger.logger.base.ILogger +import cn.jesse.nativelogger.util.CrashWatcher +import java.io.File +import java.util.logging.Formatter + +/** + * NLogger配置器 + */ +class NLoggerConfig { + @Volatile + private var builder: Builder? = null + private val defaultLogger: ILogger = AndroidLogger("NLogger") + @Volatile + private var fileLogger: ILogger? = null + + /** + * build builder to android logger & file logger & exception watcher + * @param builder + * @return + */ + @Synchronized + private fun build(builder: Builder): NLoggerConfig? { + if (builder.isCatchException) { + CrashWatcher.getInstance().init() + CrashWatcher.getInstance().setListener(builder.uncaughtExceptionListener) + } else { + CrashWatcher.getInstance().setListener(null) + } + defaultLogger.setTag(builder.tag) + defaultLogger.setLevel(builder.loggerLevel) + + if (!builder.isFileLoggerEnable) { + fileLogger = null + return mInstance + } + if (fileLogger == null) { + fileLogger = FileLogger(builder.tag) + } else { + fileLogger!!.setTag(builder.tag) + } + fileLogger!!.setLevel(builder.loggerLevel) + val iFileLogger = fileLogger as IFileLogger? + iFileLogger!!.setFilePathAndFormatter(builder.fileDirectory, builder.fileFormatter, builder.expiredPeriod) + return mInstance + } + + /** + * instance builder + * @return + */ + fun builder(): Builder { + if (builder == null) { + synchronized(NLoggerConfig::class.java) { + if (builder == null) { + builder = Builder() + } + } + } + return builder!! + } + + /** + * get android logger + * + */ + fun getDefaultLogger(): ILogger { + return this.defaultLogger + } + + /** + * get file logger + * + */ + fun getFileLogger(): ILogger? { + return this.fileLogger + } + + companion object { + @Volatile + private var mInstance: NLoggerConfig? = null + + /** + * get instance + * @return + */ + @JvmStatic + fun getInstance(): NLoggerConfig { + if (mInstance == null) { + synchronized(NLoggerConfig::class.java) { + if (mInstance == null) { + mInstance = NLoggerConfig() + } + } + } + return mInstance!! + } + + /** + * init NLoggerConfig from annotation + * @param obj + */ + @JvmStatic + fun init(obj: Any) { + val clazz = obj.javaClass + if (!clazz.isAnnotationPresent(Logger::class.java)) { + return + } + + val inject = clazz.getAnnotation(Logger::class.java) as Logger + val level: LoggerLevel = inject.level + + NLoggerConfig.getInstance() + .builder() + .tag(inject.tag) + .loggerLevel(level) + .build() + } + } + + inner class Builder { + var tag = NLoggerConfig::class.java.simpleName + var loggerLevel = LoggerLevel.WARN + var isCatchException = false + var uncaughtExceptionListener: ((thread: Thread?, ex: Throwable?) -> Unit)? = null + var isFileLoggerEnable = false + var fileDirectory = Environment.getExternalStorageDirectory().path + "/native.logs/" + var fileFormatter: Formatter = SimpleFormatter() + var expiredPeriod = 1 + + /** + * init builder , prepare env + * + */ + init { + val filePath = File(fileDirectory) + if (!filePath.exists()) { + filePath.mkdirs() + } + + uncaughtExceptionListener = { _, _ -> + android.os.Process.killProcess(android.os.Process.myPid()) + } + } + + /** + * set tag + * + * @throws IllegalArgumentException if the tag is null | empty + */ + fun tag(tag: String): Builder { + if (TextUtils.isEmpty(tag)) { + throw IllegalArgumentException("unexpected tag") + } + + this.tag = tag + return this + } + + /** + * catch uncaught exception + * + */ + fun catchException(enable: Boolean, listener: ((thread: Thread?, ex: Throwable?) -> Unit)?): Builder { + this.isCatchException = enable + this.uncaughtExceptionListener = listener + return this + } + + /** + * set the logger level + * + */ + fun loggerLevel(level: LoggerLevel): Builder { + this.loggerLevel = level + return this + } + + /** + * set file logger enable + * + */ + fun fileLogger(enable: Boolean): Builder { + this.isFileLoggerEnable = enable + return this + } + + /** + * set pack log period + * + * @throws IllegalArgumentException if the path is empty + */ + fun fileDirectory(path: String): Builder { + if (TextUtils.isEmpty(path)) { + throw IllegalArgumentException("unexpected path") + } + + val filePath = File(path) + if (!filePath.exists() && !filePath.mkdirs()) { + NLoggerConfig.getInstance().defaultLogger.error(tag, "can not make dir, please check permission") + } + + this.fileDirectory = path + return this + } + + /** + * set file logger formatter + * + * @throws IllegalArgumentException if the formatter is null + */ + fun fileFormatter(formatter: Formatter?): Builder { + if (null == formatter) { + throw IllegalArgumentException("unexpected file formatter") + } + + this.fileFormatter = formatter + return this + } + + /** + * set the period of file expired + * + * @throws IllegalArgumentException if the period <= 0 + */ + fun expiredPeriod(period: Int): Builder { + if (period <= 0) { + throw IllegalArgumentException("unexpected period : $period") + } + + expiredPeriod = period + return this + } + + fun build(): NLoggerConfig? { + return mInstance!!.build(this) + } + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt new file mode 100644 index 0000000..c008ac5 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/NLoggerError.kt @@ -0,0 +1,38 @@ +package cn.jesse.nativelogger + +/** + * NLogger 通用错误类 + * + * @author Jesse + */ +class NLoggerError : Error { + + /** + * Constructs a new {@code Error} with the current stack trace and the + * specified detail message. + * + * @param detailMessage + * the detail message for this error. + */ + constructor(detailMessage : String) : super(detailMessage) + + /** + * Constructs a new {@code Error} with the current stack trace and the + * specified cause. + * + * @param throwable + * the cause of this error. + */ + constructor(throwable : Throwable) : super(throwable) + + /** + * Constructs a new {@code Error} with the current stack trace, the + * specified detail message and the specified cause. + * + * @param detailMessage + * the detail message for this error. + * @param throwable + * the cause of this error. + */ + constructor(detailMessage: String, throwable: Throwable) : super(detailMessage, throwable) +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt new file mode 100644 index 0000000..808477a --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/SimpleFormatter.kt @@ -0,0 +1,34 @@ +package cn.jesse.nativelogger.formatter + +import java.text.MessageFormat +import java.util.* +import java.util.logging.Formatter +import java.util.logging.LogRecord + +/** + * 文件日志默认格式化工具 + * + * @author Jesse + */ +class SimpleFormatter : Formatter() { + + private val LINE_SEPARATOR = "\n" + + /** + * Converts a object into a human readable string + * representation. + * + */ + override fun format(r: LogRecord): String { + val sb = StringBuilder() + sb.append(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm:ss} ", *arrayOf(Date(r.millis)))) + sb.append(r.loggerName).append(": ") + sb.append(r.level.name).append(LINE_SEPARATOR) + sb.append(formatMessage(r)).append(LINE_SEPARATOR) + if (r.thrown != null) { + sb.append("Throwable occurred: ") + sb.append(TagFormatter.format(r.thrown)) + } + return sb.toString() + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt new file mode 100644 index 0000000..1e3c52c --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/formatter/TagFormatter.kt @@ -0,0 +1,95 @@ +package cn.jesse.nativelogger.formatter + +import android.util.Log +import cn.jesse.nativelogger.util.CloseUtil +import java.io.PrintWriter +import java.io.StringWriter + +/** + * 各种格式化日志工具 + * + * @author Jesse + */ +object TagFormatter { + private val RESULT_UNEXPECTED_FORMAT = "unexpected format" + private val WITH = " with " + + /** + * 按照 %s : %s格式化日志 + * + * @param subTag 子Tag + * @param msg 日志信息 + */ + fun format(subTag: String, msg: String) = try { + String.format("%s : %s", subTag, msg) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + } + + /** + * 扩展日志格式 %s $子格式 + * @param subTag 子Tag + * @param format 子格式 + * @param arg 被子格式格式的对象 + */ + fun format(subTag: String, format: String, arg: Any) = try { + String.format("%s : $format", subTag, arg) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + WITH + format + } + + /** + * 扩展日志格式 %s $子格式 + * @param subTag 子Tag + * @param format 子格式 + * @param argA 被子格式格式的对象A + * @param argB 被子格式格式的对象B + */ + fun format(subTag: String, format: String, argA: Any, argB: Any) = try { + String.format("%s : $format", subTag, argA, argB) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + WITH + format + } + + /** + * 扩展日志格式 %s $子格式 + * @param subTag 子Tag + * @param format 子格式 + * @param args 可变参数组 + */ + fun format(subTag: String, format: String, vararg args: Any) = try { + String.format("%s : %s", subTag, String.format(format, *args)) + } catch (e: Exception) { + Log.e(subTag, RESULT_UNEXPECTED_FORMAT, e) + RESULT_UNEXPECTED_FORMAT + WITH + format + } + + /** + * 格式化异常信息 + * + * @param t 异常 + */ + fun format(t: Throwable?): String { + var result = "" + + if (null == t) { + return result + } + + var pw: PrintWriter? = null + + try { + val sw = StringWriter() + pw = PrintWriter(sw) + t.printStackTrace(pw) + result = sw.toString() + } finally { + CloseUtil.close(pw) + } + + return result + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt new file mode 100644 index 0000000..4864349 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/AndroidLogger.kt @@ -0,0 +1,251 @@ +package cn.jesse.nativelogger.logger + +import android.util.Log +import cn.jesse.nativelogger.formatter.TagFormatter +import cn.jesse.nativelogger.logger.base.AbstractLogger + +/** + * Android 日志管理器实现 + * + * @author Jesse + */ +class AndroidLogger(tag: String) : AbstractLogger(tag) { + private var debugEnable = true + private var infoEnable = true + private var warningEnable = true + private var errorEnable = true + + override fun setLevel(level: LoggerLevel) { + when (level) { + LoggerLevel.DEBUG -> { + // default + } + LoggerLevel.INFO -> { + debugEnable = false + } + LoggerLevel.WARN -> { + debugEnable = false + infoEnable = false + } + LoggerLevel.ERROR -> { + debugEnable = false + infoEnable = false + warningEnable = false + } + LoggerLevel.OFF -> { + debugEnable = false + infoEnable = false + warningEnable = false + errorEnable = false + } + } + } + + override fun isDebugEnabled(): Boolean { + return debugEnable + } + + override fun debug(msg: String) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, msg) + } + + override fun debug(subTag: String, msg: String) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, msg)) + } + + override fun debug(subTag: String, format: String, arg: Any) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun debug(subTag: String, format: String, argA: Any, argB: Any) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun debug(subTag: String, format: String, vararg arguments: Any) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun debug(subTag: String, t: Throwable) { + if (!isDebugEnabled()) { + return + } + + Log.d(mTag, subTag + " " + TagFormatter.format(t)) + } + + override fun isInfoEnabled(): Boolean { + return infoEnable + } + + override fun info(msg: String) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, msg) + } + + override fun info(subTag: String, msg: String) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, msg)) + } + + override fun info(subTag: String, format: String, arg: Any) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun info(subTag: String, format: String, argA: Any, argB: Any) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun info(subTag: String, format: String, vararg arguments: Any) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun info(subTag: String, t: Throwable) { + if (!isInfoEnabled()) { + return + } + + Log.i(mTag, subTag + " " + TagFormatter.format(t)) + } + + override fun isWarnEnabled(): Boolean { + return warningEnable + } + + override fun warn(msg: String) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, msg) + } + + override fun warn(subTag: String, msg: String) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, msg)) + } + + override fun warn(subTag: String, format: String, arg: Any) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun warn(subTag: String, format: String, vararg arguments: Any) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun warn(subTag: String, format: String, argA: Any, argB: Any) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun warn(subTag: String, t: Throwable) { + if (!isWarnEnabled()) { + return + } + + Log.w(mTag, subTag + " " + TagFormatter.format(t)) + } + + override fun isErrorEnabled(): Boolean { + return errorEnable + } + + override fun error(msg: String) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, msg) + } + + override fun error(subTag: String, msg: String) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, msg)) + } + + override fun error(subTag: String, format: String, arg: Any) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, format, arg)) + } + + override fun error(subTag: String, format: String, argA: Any, argB: Any) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, format, argA, argB)) + } + + override fun error(subTag: String, format: String, vararg arguments: Any) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, TagFormatter.format(subTag, format, *arguments)) + } + + override fun error(subTag: String, t: Throwable) { + if (!isErrorEnabled()) { + return + } + + Log.e(mTag, subTag + " " + TagFormatter.format(t)) + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt new file mode 100644 index 0000000..fee16d7 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/FileLogger.kt @@ -0,0 +1,336 @@ +package cn.jesse.nativelogger.logger + +import android.os.Handler +import android.os.HandlerThread +import cn.jesse.nativelogger.formatter.TagFormatter +import cn.jesse.nativelogger.logger.base.AbstractLogger +import cn.jesse.nativelogger.logger.base.IFileLogger +import cn.jesse.nativelogger.util.DateUtil +import cn.jesse.nativelogger.util.ZipUtil +import java.io.File +import java.io.IOException +import java.util.logging.* + +/** + * 文件日志管理器实现 + */ +class FileLogger(tag: String) : AbstractLogger(tag), IFileLogger { + internal val logger: Logger + private lateinit var logDir: String + private lateinit var formatter: Formatter + private var expiredPeriod: Int = 0 + + private var handler: Handler + + init { + val fileLoggerThread = HandlerThread(FileLogger::class.java.simpleName) + fileLoggerThread.start() + handler = Handler(fileLoggerThread.looper) + + this.logger = Logger.getLogger(tag) + logger.useParentHandlers = false + } + + /* + * Java文件操作 获取不带扩展名的文件名 + * + * Created on: 2011-8-2 + * Author: blueeagle + */ + fun getFileNameNoEx(filename: String): String { + if (filename.isNotEmpty()) { + val dot = filename.indexOf('.') + if (dot > -1 && dot < filename.length) { + return filename.substring(0, dot) + } + } + return filename + } + + override fun setFilePathAndFormatter(directory: String, formatter: Formatter, expiredPeriod: Int) { + this.logDir = directory + this.formatter = formatter + this.expiredPeriod = expiredPeriod + + if (!logDir.endsWith("/")) + logDir += "/" + val f = File(logDir) + if (f.isDirectory && !f.exists()) { + f.mkdirs() + } + DateUtil.DeleteFileDate(expiredPeriod, f) + val file = File(logDir + DateUtil.getCurrentDate()) + val fh: FileHandler + try { + fh = FileHandler(file.toString(), true) + fh.formatter = formatter + logger.addHandler(fh) + } catch (e: IOException) { + //unused + error(this.javaClass.simpleName, e) + } + handler.post { ZipUtil.getSuitableFilesWithClear(directory, expiredPeriod) } + } + + override fun logDirectory(): String { + return this.logDir + } + + override fun fileFormatter(): Formatter { + return this.formatter + } + + override fun expiredPeriod(): Int { + return this.expiredPeriod + } + + override fun zipLogs(listener: (succeed: Boolean, target: String) -> Unit) { + handler.post { + var result = false + val targetZipFileName = logDir + DateUtil.getCurrentDate() + ZipUtil.SUFFIX_ZIP + try { + val zipFile = File(targetZipFileName) + if (zipFile.exists() && !zipFile.delete()) { + error(tag(), "can not delete exist zip file!") + } + result = ZipUtil.zipFiles( + ZipUtil.getSuitableFilesWithClear(logDir, expiredPeriod), + zipFile, DateUtil.getCurrentDate() + ) + } catch (e: Exception) { + error(tag(), e) + } + + listener(result, targetZipFileName) + } + } + + override fun setLevel(level: LoggerLevel) { + when (level) { + LoggerLevel.OFF -> logger.level = Level.OFF + LoggerLevel.ERROR -> logger.level = Level.SEVERE + LoggerLevel.WARN -> logger.level = Level.WARNING + LoggerLevel.INFO -> logger.level = Level.INFO + LoggerLevel.DEBUG -> logger.level = Level.FINE + } + } + + @Synchronized + private fun log(level: Level, msg: String, t: Throwable?) { + val record = LogRecord(level, msg) + record.sourceClassName=tag() + record.loggerName = tag() + record.thrown = t + logger.log(record) + } + + override fun isDebugEnabled(): Boolean { + return logger.isLoggable(Level.FINE) + } + + override fun debug(msg: String) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, msg, null) } + } + + override fun debug(subTag: String, msg: String) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, msg), null) } + } + + override fun debug(subTag: String, format: String, arg: Any) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, format, arg), null) } + } + + override fun debug(subTag: String, format: String, argA: Any, argB: Any) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun debug(subTag: String, format: String, vararg arguments: Any) { + if (!isDebugEnabled()) { + return + } + + handler.post { log(Level.FINE, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun debug(subTag: String, t: Throwable) { + if (!isDebugEnabled()) { + return + } + + log(Level.FINE, subTag, t) + } + + override fun isInfoEnabled(): Boolean { + return logger.isLoggable(Level.INFO) + } + + override fun info(msg: String) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, msg, null) } + } + + override fun info(subTag: String, msg: String) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, msg), null) } + } + + override fun info(subTag: String, format: String, arg: Any) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, format, arg), null) } + } + + override fun info(subTag: String, format: String, argA: Any, argB: Any) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun info(subTag: String, format: String, vararg arguments: Any) { + if (!isInfoEnabled()) { + return + } + + handler.post { log(Level.INFO, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun info(subTag: String, t: Throwable) { + if (!isInfoEnabled()) { + return + } + + log(Level.INFO, subTag, t) + } + + + override fun isWarnEnabled(): Boolean { + return logger.isLoggable(Level.WARNING) + } + + override fun warn(msg: String) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, msg, null) } + } + + override fun warn(subTag: String, msg: String) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, msg), null) } + } + + override fun warn(subTag: String, format: String, arg: Any) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, format, arg), null) } + } + + override fun warn(subTag: String, format: String, vararg arguments: Any) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun warn(subTag: String, format: String, argA: Any, argB: Any) { + if (!isWarnEnabled()) { + return + } + + handler.post { log(Level.WARNING, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun warn(subTag: String, t: Throwable) { + if (!isWarnEnabled()) { + return + } + + log(Level.WARNING, subTag, t) + } + + + override fun isErrorEnabled(): Boolean { + return logger.isLoggable(Level.SEVERE) + } + + override fun error(msg: String) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, msg, null) } + } + + override fun error(subTag: String, msg: String) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, msg), null) } + } + + override fun error(subTag: String, format: String, arg: Any) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, format, arg), null) } + } + + override fun error(subTag: String, format: String, argA: Any, argB: Any) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, format, argA, argB), null) } + } + + override fun error(subTag: String, format: String, vararg arguments: Any) { + if (!isErrorEnabled()) { + return + } + + handler.post { log(Level.SEVERE, TagFormatter.format(subTag, format, *arguments), null) } + } + + override fun error(subTag: String, t: Throwable) { + if (!isErrorEnabled()) { + return + } + + log(Level.SEVERE, subTag, t) + } +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt new file mode 100644 index 0000000..6bed0cb --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/LoggerLevel.kt @@ -0,0 +1,14 @@ +package cn.jesse.nativelogger.logger + +/** + * 日志等级 枚举 + * + * @author Jesse + */ +enum class LoggerLevel { + DEBUG, + INFO, + WARN, + ERROR, + OFF +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt new file mode 100644 index 0000000..a5a16a3 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/AbstractLogger.kt @@ -0,0 +1,172 @@ +package cn.jesse.nativelogger.logger.base + +import cn.jesse.nativelogger.NLoggerError +import cn.jesse.nativelogger.logger.LoggerLevel +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + +/** + * Logger抽象类, 提供部分默认逻辑 + * + * @author Jesse + */ +abstract class AbstractLogger(tag: String) : ILogger { + protected open var mTag: String = "AbstractLogger" + + init { + mTag = tag + } + + override fun tag(): String { + return mTag + } + + + override fun setTag(tag: String) { + this.mTag = tag + } + + override fun isEnabled(level: LoggerLevel): Boolean { + return when (level) { + LoggerLevel.DEBUG -> isDebugEnabled() + LoggerLevel.INFO -> isInfoEnabled() + LoggerLevel.WARN -> isWarnEnabled() + LoggerLevel.ERROR -> isErrorEnabled() + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, t: Throwable) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, t) + LoggerLevel.INFO -> info(subTag, t) + LoggerLevel.WARN -> warn(subTag, t) + LoggerLevel.ERROR -> error(subTag, t) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, msg: String) { + when (level) { + LoggerLevel.DEBUG -> debug(msg) + LoggerLevel.INFO -> info(msg) + LoggerLevel.WARN -> warn(msg) + LoggerLevel.ERROR -> error(msg) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, msg: String) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, msg) + LoggerLevel.INFO -> info(subTag, msg) + LoggerLevel.WARN -> warn(subTag, msg) + LoggerLevel.ERROR -> error(subTag, msg) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, format: String, arg: Any) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, format, arg) + LoggerLevel.INFO -> info(subTag, format, arg) + LoggerLevel.WARN -> warn(subTag, format, arg) + LoggerLevel.ERROR -> error(subTag, format, arg) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, format: String, argA: Any, argB: Any) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, format, argA, argB) + LoggerLevel.INFO -> info(subTag, format, argA, argB) + LoggerLevel.WARN -> warn(subTag, format, argA, argB) + LoggerLevel.ERROR -> error(subTag, format, argA, argB) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun log(level: LoggerLevel, subTag: String, format: String, vararg arguments: Any) { + when (level) { + LoggerLevel.DEBUG -> debug(subTag, format, *arguments) + LoggerLevel.INFO -> info(subTag, format, *arguments) + LoggerLevel.WARN -> warn(subTag, format, *arguments) + LoggerLevel.ERROR -> error(subTag, format, *arguments) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun json(level: LoggerLevel, msg: String) { + if (!isEnabled(level)) { + return + } + + val json = parseJson(msg) + + when (level) { + LoggerLevel.DEBUG -> debug(json) + LoggerLevel.INFO -> info(json) + LoggerLevel.WARN -> warn(json) + LoggerLevel.ERROR -> error(json) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + override fun json(level: LoggerLevel, subTag: String, msg: String) { + if (!isEnabled(level)) + return + + val json = parseJson(msg) + + when (level) { + LoggerLevel.DEBUG -> debug(subTag, json) + LoggerLevel.INFO -> info(subTag, json) + LoggerLevel.WARN -> warn(subTag, json) + LoggerLevel.ERROR -> error(subTag, json) + else -> throw NLoggerError(ERROR_LEVEL) + } + } + + /** + * format json + * as: + *

+     * {
+     * "query": "Pizza",
+     * "locations": [
+     * 94043,
+     * 90210
+     * ]
+     * }
+ * @param json + * @return + */ + private fun parseJson(json: String?): String { + if (null == json) { + return ERROR_FORMAT + } + + try { + if (json.startsWith("{")) { + val jsonObject = JSONObject(json) + return jsonObject.toString(JSON_INDENT) + } + if (json.startsWith("[")) { + val jsonArray = JSONArray(json) + return jsonArray.toString(JSON_INDENT) + } + return ERROR_FORMAT + } catch (e: JSONException) { + error(this.javaClass.simpleName, e) + return ERROR_FORMAT + } + + } + + companion object { + private val ERROR_FORMAT = "unexpected format" + private val ERROR_LEVEL = "unexpected LoggerLevel" + private val JSON_INDENT = 2 + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt new file mode 100644 index 0000000..28e931c --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/IFileLogger.kt @@ -0,0 +1,43 @@ +package cn.jesse.nativelogger.logger.base + +import java.util.logging.Formatter + +/** + * 文件日志管理器接口 + * + * @author Jesse + */ +interface IFileLogger { + + /** + * 设置文件路径, 日志格式工具, 和日志保留时间 + * + * @param directory 路径 + * @param formatter 日志格式化工具 + * @param expiredPeriod 日志过期时间, 单位为天 + */ + fun setFilePathAndFormatter(directory: String, formatter: Formatter, expiredPeriod: Int) + + /** + * 获取日志文件存放路径 + */ + fun logDirectory(): String + + /** + * 获取文件日志格式化工具 + */ + fun fileFormatter(): Formatter + + /** + * 获取日志过期时间 + */ + fun expiredPeriod(): Int + + /** + * 压缩日志 + * + * @param listener 压缩结果回调. succeed 是否成功, target 压缩包路径 + * + */ + fun zipLogs(listener: (succeed: Boolean, target: String) -> Unit) +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt new file mode 100644 index 0000000..df4d969 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/logger/base/ILogger.kt @@ -0,0 +1,333 @@ +package cn.jesse.nativelogger.logger.base + +import cn.jesse.nativelogger.logger.LoggerLevel + +/** + * Logger 接口 + * + * @author Jesse + */ +interface ILogger { + + /** + * 设置日志tag + * + * @param tag TAG + */ + fun setTag(tag: String) + + /** + * 获取日志tag + */ + fun tag(): String + + /** + * 设置日志等级 + * + * @param level 等级枚举 + */ + fun setLevel(level: LoggerLevel) + + /** + * 判断是否可以debug + */ + fun isDebugEnabled(): Boolean + + /** + * debug 日志 + * + * @param msg 信息 + */ + fun debug(msg: String) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun debug(subTag: String, msg: String) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun debug(subTag: String, format: String, arg: Any) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun debug(subTag: String, format: String, argA: Any, argB: Any) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun debug(subTag: String, format: String, vararg arguments: Any) + + /** + * debug 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun debug(subTag: String, t: Throwable) + + /** + * 判断是否可以info + */ + fun isInfoEnabled(): Boolean + + /** + * info 日志 + * + * @param msg 信息 + */ + fun info(msg: String) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun info(subTag: String, msg: String) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun info(subTag: String, format: String, arg: Any) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun info(subTag: String, format: String, argA: Any, argB: Any) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun info(subTag: String, format: String, vararg arguments: Any) + + /** + * info 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun info(subTag: String, t: Throwable) + + /** + * 判断是否可以 warn + */ + fun isWarnEnabled(): Boolean + + /** + * warn 日志 + * + * @param msg 信息 + */ + fun warn(msg: String) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun warn(subTag: String, msg: String) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun warn(subTag: String, format: String, arg: Any) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun warn(subTag: String, format: String, vararg arguments: Any) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun warn(subTag: String, format: String, argA: Any, argB: Any) + + /** + * warn 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun warn(subTag: String, t: Throwable) + + /** + * 判断是否可以 error + */ + fun isErrorEnabled(): Boolean + + /** + * error 日志 + * + * @param msg 信息 + */ + fun error(msg: String) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param msg 信息 + */ + fun error(subTag: String, msg: String) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun error(subTag: String, format: String, arg: Any) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun error(subTag: String, format: String, argA: Any, argB: Any) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun error(subTag: String, format: String, vararg arguments: Any) + + /** + * error 日志 + * + * @param subTag 子TAG + * @param t 异常信息 + */ + fun error(subTag: String, t: Throwable) + + /** + * 判断日志管理器是否开启 + */ + fun isEnabled(level: LoggerLevel): Boolean + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param msg 日志信息 + */ + fun log(level: LoggerLevel, msg: String) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param msg 日志信息 + */ + fun log(level: LoggerLevel, subTag: String, msg: String) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arg 被格式化对象 + */ + fun log(level: LoggerLevel, subTag: String, format: String, arg: Any) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param argA 被格式化对象A + * @param argB 被格式化对象B + */ + fun log(level: LoggerLevel, subTag: String, format: String, argA: Any, argB: Any) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param format 日志格式化公式 eg. %s %d + * @param arguments 可变参数组 + */ + fun log(level: LoggerLevel, subTag: String, format: String, vararg arguments: Any) + + /** + * 根据日志等级打印对应信息 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param t 异常信息 + */ + fun log(level: LoggerLevel, subTag: String, t: Throwable) + + /** + * 格式化json日志 + * + * @param level 日志等级 + * @param msg json串 + */ + fun json(level: LoggerLevel, msg: String) + + /** + * 格式化json日志 + * + * @param level 日志等级 + * @param subTag 子TAG + * @param msg json串 + */ + fun json(level: LoggerLevel, subTag: String, msg: String) +} \ No newline at end of file diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt new file mode 100644 index 0000000..b723976 --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CloseUtil.kt @@ -0,0 +1,27 @@ +package cn.jesse.nativelogger.util + +import java.io.Closeable +import java.io.IOException + +import cn.jesse.nativelogger.NLogger + +/** + * 关闭各种 + * + * @author Jesse + */ +object CloseUtil { + + /** + * 关闭Closeable对象 + * + * @param closeable 对象 + */ + fun close(closeable: Closeable?) { + try { + closeable?.close() + } catch (e: IOException) { + NLogger.e("close", e) + } + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt new file mode 100644 index 0000000..7c2255c --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/CrashWatcher.kt @@ -0,0 +1,45 @@ +package cn.jesse.nativelogger.util + +/** + * 监听全局异常, 捕获并回调 + * + * @author Jesse + */ +class CrashWatcher private constructor() : Thread.UncaughtExceptionHandler { + + private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null + private var listener: ((thread: Thread?, ex: Throwable?) -> Unit)? = null + + fun init() { + if (null != mDefaultHandler) { + return + } + + mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler() + Thread.setDefaultUncaughtExceptionHandler(this) + } + + fun setListener(listener: ((thread: Thread?, ex: Throwable?) -> Unit)?) { + this.listener = listener + } + + override fun uncaughtException(t: Thread, e: Throwable) { + listener?.invoke(t, e) + } + + companion object { + private var mInstance: CrashWatcher? = null + + fun getInstance(): CrashWatcher { + if (null == mInstance) { + synchronized(CrashWatcher::class.java) { + if (null == mInstance) { + mInstance = CrashWatcher() + } + } + } + + return mInstance!! + } + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt new file mode 100644 index 0000000..491bb0a --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/DateUtil.kt @@ -0,0 +1,119 @@ +package cn.jesse.nativelogger.util + +import java.io.File +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.* + +/** + * 日期工具类 + * + * @author Jesse + */ +object DateUtil { + private val TEMPLATE_DATE = "yyyy-MM-dd" + + /** + * 以yyyy-MM-dd格式获取当前时间 + */ + fun getCurrentDate(): String { + val sdf = SimpleDateFormat(TEMPLATE_DATE) + return sdf.format(System.currentTimeMillis()) + } + + + fun DeleteFileDate(days: Int, file: File) { + var start = getCurrentDateBefor(days) + var end = getCurrentDate() + val sdf = SimpleDateFormat(TEMPLATE_DATE) + var dStart: Date? + var dEnd: Date? + try { + dStart = sdf.parse(start) + dEnd = sdf.parse(end) + val dateList = findDates(dStart!!, dEnd!!) + var f = file.listFiles() + if (f != null && !f.isEmpty()) + for (f1 in f) { + var flag = false + for (date in dateList) { + if (f1.name.startsWith(sdf.format(date))) { + flag=true + } + } + if (!flag){ + f1.delete() + } + } + } catch (e: ParseException) { + e.printStackTrace() + } catch (e1: Exception) { + e1.printStackTrace() + } + } + + //JAVA获取某段时间内的所有日期 + fun findDates(dStart: Date, dEnd: Date): List { + val cStart = Calendar.getInstance() + cStart.time = dStart + val dateList = ArrayList() + //别忘了,把起始日期加上 + dateList.add(dStart) + // 此日期是否在指定日期之后 + while (dEnd.after(cStart.time)) { + // 根据日历的规则,为给定的日历字段添加或减去指定的时间量 + cStart.add(Calendar.DAY_OF_MONTH, 1) + dateList.add(cStart.time) + } + return dateList + } + + fun isValidDate(str: String): Boolean { + var convertSuccess = true + // 指定日期格式为四位年/两位月份/两位日期,注意yyyy/MM/dd区分大小写; + val format = SimpleDateFormat(TEMPLATE_DATE) + try { + // 设置lenient为false. + // 否则SimpleDateFormat会比较宽松地验证日期,比如2007/02/29会被接受,并转换成2007/03/01 + format.isLenient = false + format.parse(str) + } catch (e: ParseException) { + // e.printStackTrace(); + // 如果throw java.text.ParseException或者NullPointerException,就说明格式不对 + convertSuccess = false + } + + return convertSuccess + } + + fun getCurrentDateBefor(days: Int): String { + val calendar = Calendar.getInstance() +//今天 + System.out.println(calendar.getTime()) +//10天前 + calendar.add(Calendar.DATE, -days) + System.out.println(calendar.getTime()) + val sdf = SimpleDateFormat(TEMPLATE_DATE) + return sdf.format(calendar.time) + } + + /** + * @Title: isInDate + * @Description: 判断一个时间段(YYYY-MM-DD)是否在一个区间 + * @param @param date + * @param @param strDateBegin + * @param @param strDateEnd + * @param @return 设定文件 + * @return boolean 返回类型 + * @throws + */ + fun isInDate(strDate: String, strDateBegin: String, strDateEnd: String): Boolean { + // 截取当前时间年月日 转成整型 + val tempDate = Integer.parseInt(strDate.replace("-", "")) + // 截取开始时间年月日 转成整型 + val tempDateBegin = Integer.parseInt(strDateBegin.replace("-", "")) + // 截取结束时间年月日 转成整型 + val tempDateEnd = Integer.parseInt(strDateEnd.replace("-", "")) + return (tempDate in tempDateBegin..tempDateEnd) + } +} diff --git a/nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt b/nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt new file mode 100644 index 0000000..ef4948d --- /dev/null +++ b/nativelogger/src/main/java/cn/jesse/nativelogger/util/ZipUtil.kt @@ -0,0 +1,119 @@ +package cn.jesse.nativelogger.util + +import android.text.TextUtils +import cn.jesse.nativelogger.NLogger +import java.io.* +import java.nio.charset.Charset +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +/** + * 压缩工具 + * + * @author Jesse + */ +object ZipUtil { + val SUFFIX_ZIP = ".zip" + private val BUFF_SIZE = 1024 * 1024 + private val SUFFIX_LOCK = ".lck" + + /** + * get suitable files from path depend on pack num ,clear redundant files + * + * @param path source files path + * @param expiredPeriod expired file period + */ + fun getSuitableFilesWithClear(path: String, expiredPeriod: Int): Collection { + val files = ArrayList() + val file = File(path) + val subFile = file.listFiles() + if (subFile != null && !subFile.isEmpty()) { + for (item in subFile) { + if (item.isDirectory) { + continue + } + + val expired = expiredPeriod * 24 * 60 * 60 * 1000L + if ((System.currentTimeMillis() - item.lastModified() > expired) && !item.delete()) { + NLogger.e("can not delete expired file " + item.name) + } + + if (item.name.endsWith(SUFFIX_LOCK) || item.name.endsWith(SUFFIX_ZIP)) { + continue + } + + files.add(item) + + } + } + return files + } + + /** + * zip files + * + * @param resFileList zip from files + * @param zipFile zip to file + * @param comment comment of target file + */ + @Throws(IOException::class) + fun zipFiles(resFileList: Collection?, zipFile: File, comment: String): Boolean { + if (null == resFileList || resFileList.isEmpty()) { + return false + } + + val zipOutputStream = ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile), BUFF_SIZE)) + for (resFile in resFileList) { + zipFile(resFile, zipOutputStream, "") + } + + if (!TextUtils.isEmpty(comment)) { + zipOutputStream.setComment(comment) + } + + CloseUtil.close(zipOutputStream) + return true + } + + /** + * zip file + * + * @param resFile zip from file + * @param zipOut zip to file + * @param rootPath target file path + */ + @Throws(IOException::class) + fun zipFile(resFile: File, zipOut: ZipOutputStream, rootPath: String) { + + var separator = "" + if (rootPath.isNotEmpty()) { + separator = File.separator + } + + var filePath = rootPath + separator + resFile.name + filePath = String(filePath.toByteArray(), Charset.forName("GB2312")) + + if (resFile.isDirectory) { + val fileList = resFile.listFiles() + + for (file in fileList!!) { + zipFile(file, zipOut, filePath) + } + return + } + + val buffer = ByteArray(BUFF_SIZE) + val bis = BufferedInputStream(FileInputStream(resFile), BUFF_SIZE) + zipOut.putNextEntry(ZipEntry(filePath)) + var realLength = bis.read(buffer) + + while (realLength != -1) { + zipOut.write(buffer, 0, realLength) + realLength = bis.read(buffer) + } + + CloseUtil.close(bis) + zipOut.flush() + zipOut.closeEntry() + } +} diff --git a/nativelogger/uploadAliRelease.gradle b/nativelogger/uploadAliRelease.gradle new file mode 100644 index 0000000..14e70cd --- /dev/null +++ b/nativelogger/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'nativelogger' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/nativelogger/uploadAliSnapshot.gradle b/nativelogger/uploadAliSnapshot.gradle new file mode 100644 index 0000000..cbf5026 --- /dev/null +++ b/nativelogger/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'nativelogger' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/nativelogger/uploadLocal.gradle b/nativelogger/uploadLocal.gradle new file mode 100644 index 0000000..285ca29 --- /dev/null +++ b/nativelogger/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'nativelogger' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/qrutill/.gitignore b/qrutill/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/qrutill/.gitignore @@ -0,0 +1 @@ +/build diff --git a/qrutill/build.gradle b/qrutill/build.gradle new file mode 100644 index 0000000..d15e1c2 --- /dev/null +++ b/qrutill/build.gradle @@ -0,0 +1,52 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +version = '1.0.4' // 版本 +group = rootProject.groupId +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation rootProject.ext.dependencies["xdroid-mvp"] +// api project(path: ':mvp') + api rootProject.ext.dependencies["zxing"] +} + +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} +artifacts { + archives sourcesJar + archives javadocJar +} +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/qrutill/consumer-rules.pro b/qrutill/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/qrutill/proguard-rules.pro b/qrutill/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/qrutill/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/qrutill/src/main/AndroidManifest.xml b/qrutill/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a419a71 --- /dev/null +++ b/qrutill/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java b/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java new file mode 100644 index 0000000..91e7de3 --- /dev/null +++ b/qrutill/src/main/java/com/inlee/qrutill/QRCodeTool.java @@ -0,0 +1,414 @@ +package com.inlee.qrutill; + +import android.graphics.*; +import android.text.TextUtils; + +import cn.droidlover.xdroidmvp.net.NetError; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; +import com.inlee.qrutill.bean.QRCodeBean; + +import io.reactivex.*; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; + +import org.reactivestreams.Publisher; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +/** + * Created by dingyi on 2016/12/1. + */ + +public class QRCodeTool { + /** + * 线程切换 + * + * @return + */ + public static FlowableTransformer getScheduler() { + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .unsubscribeOn(AndroidSchedulers.mainThread()); + } + }; + } + + /** + * 异常处理变换 + * + * @return + */ + public static FlowableTransformer getApiTransformer() { + + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.flatMap(new Function>() { + @Override + public Publisher apply(T model) throws Exception { + if (model == null) { + return Flowable.error(new NetError("无数据", NetError.NoDataError)); + } else { + return Flowable.just(model); + } + } + }); + } + }; + } + + public Flowable saveQrCode(final String content, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { + return makeFlowable(callableSaveQrCode(content, 0xff000000, 0xffffffff, widthPix, heightPix, logoBm, filePath)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable saveQrCode(final String content, final int qrColor, final int backColor, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { + return makeFlowable(callableSaveQrCode(content, qrColor, backColor, widthPix, heightPix, logoBm, filePath)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> saveQrCodes(List contents, final int widthPix, final int heightPix, final String filePath) { + return makeFlowable(callableSaveQrCodes(contents, 0xff000000, 0xffffffff, widthPix, heightPix, filePath)); + } + + public Flowable> saveQrCodes(List contents, final int qrColor, final int backColor, final int widthPix, final int heightPix, final String filePath) { + return makeFlowable(callableSaveQrCodes(contents, qrColor, backColor, widthPix, heightPix, filePath)); + } + + public Flowable getLogoQrCode(final Bitmap src, final Bitmap logo) { + return makeFlowable(callableGetLogoQrCode(src, logo)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable getQrCode(final String qrCode, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCode(qrCode, 0xff000000, 0xffffffff, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable getQrCode(final String qrCode, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCode(qrCode, qrColor, backColor, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodes(final List qrCodes, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCodes(qrCodes, 0xff000000, 0xffffffff, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodes(final List qrCodes, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + return makeFlowable(callableGetQrCodes(qrCodes, qrColor, backColor, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodesforShop(String memberId, final List shops, final int widthPix, final int heightPix) { + List qrCodes = new ArrayList<>(); + for (String shop : shops) { + qrCodes.add(shop + "&staffMId=" + memberId); + } + return makeFlowable(callableGetQrCodes(qrCodes, 0xff000000, 0xffffffff, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodesforShop(String memberId, final List shops, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + List qrCodes = new ArrayList<>(); + for (String shop : shops) { + qrCodes.add(shop + "&staffMId=" + memberId); + } + return makeFlowable(callableGetQrCodes(qrCodes, qrColor, backColor, widthPix, heightPix)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodesforShop(final String shop, final int widthPix, final int heightPix, boolean flag) { + List qrCodes = new ArrayList<>(); + qrCodes.add(shop); + return makeFlowable(callableGetQrCodes(qrCodes, 0xff000000, 0xffffffff, widthPix, heightPix, flag)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + public Flowable> getQrCodesforShop(final String shop, final int qrColor, final int backColor, final int widthPix, final int heightPix, boolean flag) { + List qrCodes = new ArrayList<>(); + qrCodes.add(shop); + return makeFlowable(callableGetQrCodes(qrCodes, qrColor, backColor, widthPix, heightPix, flag)) + .subscribeOn(Schedulers.io()).delay(1, TimeUnit.SECONDS); + } + + + private Callable callableSaveQrCode(final String content, final int qrColor, final int backColor, final int widthPix, final int heightPix, final Bitmap logoBm, final String filePath) { + return new Callable() { + @Override + public Object call() throws Exception { + return createQRImage(content, qrColor, backColor, widthPix, heightPix, logoBm, filePath); + } + }; + } + + private Callable> callableSaveQrCodes(final List contents, final int qrColor, final int backColor, final int widthPix, final int heightPix, final String filePath) { + return new Callable>() { + @Override + public List call() throws Exception { + List list = new ArrayList(); + for (int i = 0; i < contents.size(); i++) { + list.add(createQRImage(contents.get(i).getQRCodeUrl(), qrColor, backColor, widthPix, heightPix, null, filePath + "/" + contents.get(i).getFileName() + ".jpg")); + } + return list; + } + }; + } + + //flie:要删除的文件夹的所在位置 + private void deleteFile(File file) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + for (int i = 0; i < files.length; i++) { + File f = files[i]; + deleteFile(f); + } + file.delete();//如要保留文件夹,只删除文件,请注释这行 + } else if (file.exists()) { + file.delete(); + } + } + + private Callable callableGetLogoQrCode(final Bitmap src, final Bitmap logo) { + return new Callable() { + @Override + public Bitmap call() throws Exception { + return addLogo(src, logo); + } + }; + } + + + private Callable callableGetQrCode(final String qrCode, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + return new Callable() { + @Override + public Bitmap call() throws Exception { + return createQRImage(qrCode, widthPix, heightPix, qrColor, backColor); + } + }; + } + + private Callable> callableGetQrCodes(final List qrCodes, final int qrColor, final int backColor, final int widthPix, final int heightPix) { + return new Callable>() { + @Override + public List call() throws Exception { + List bitmaps = new ArrayList<>(); + for (String qrCode : qrCodes) { + bitmaps.add(addLogo(createQRImage(qrCode, qrColor, backColor, widthPix, heightPix), null)); + } + return bitmaps; + } + }; + } + + + private Callable> callableGetQrCodes(final List qrCodes, final int qrColor, final int backColor, final int widthPix, final int heightPix, final boolean flag) { + return new Callable>() { + @Override + public List call() throws Exception { + List bitmaps = new ArrayList<>(); + for (String qrCode : qrCodes) { + if (flag) { + bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor), null)); + } else { + bitmaps.add(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor)); + } + } + return bitmaps; + } + }; + } + + private Callable> callableGetQrCodes(final List qrCodes, final int widthPix, final int heightPix, final boolean flag, final int qrColor, final int backColor) { + return new Callable>() { + @Override + public List call() throws Exception { + List bitmaps = new ArrayList<>(); + for (String qrCode : qrCodes) { + if (flag) { + bitmaps.add(addLogo(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor), null)); + } else { + bitmaps.add(createQRImage(qrCode, widthPix, heightPix, qrColor, backColor)); + } + } + return bitmaps; + } + }; + } + + /** + * 生成二维码Bitmap + * + * @param content 内容 + * @param widthPix 图片宽度 + * @param heightPix 图片高度 + * @param logoBm 二维码中心的Logo图标(可以为null) + * @param filePath 用于存储二维码图片的文件路径 + * @return 生成二维码及保存文件是否成功 + */ + private Bitmap createQRImage(String content, int qrColor, int backColor, int widthPix, int heightPix, Bitmap logoBm, String filePath) { + try { + if (content == null || "".equals(content)) { + return null; + } + //配置参数 + Map hints = new HashMap<>(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + //容错级别 + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + //设置空白边距的宽度 +// hints.put(EncodeHintType.MARGIN, 2); //default is 4 + + // 图像数据转换,使用了矩阵转换 + BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); + int[] pixels = new int[widthPix * heightPix]; + // 下面这里按照二维码的算法,逐个生成二维码的图片, + // 两个for循环是图片横列扫描的结果 + for (int y = 0; y < heightPix; y++) { + for (int x = 0; x < widthPix; x++) { + if (bitMatrix.get(x, y)) { +// pixels[y * widthPix + x] = 0xff000000; + pixels[y * widthPix + x] = qrColor; +// pixels[y * widthPix + x] = 0xFFFF0000; + } else { +// pixels[y * widthPix + x] = 0xffffffff; + pixels[y * widthPix + x] = backColor; + } + } + } + + // 生成二维码图片的格式,使用ARGB_8888 + Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); + + if (logoBm != null) { + bitmap = addLogo(bitmap, logoBm); + } + + //必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大! + if (bitmap == null) { + return null; + } + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(filePath)); + return bitmap; + } catch (WriterException | IOException e) { + e.printStackTrace(); + } + + return null; + } + + /** + * 在二维码中间添加Logo图案 + */ + private Bitmap addLogo(Bitmap src, Bitmap logo) { + if (src == null) { + return null; + } + if (logo == null) { + return src; + } + //获取图片的宽高 + int srcWidth = src.getWidth(); + int srcHeight = src.getHeight(); + int logoWidth = logo.getWidth(); + int logoHeight = logo.getHeight(); + if (srcWidth == 0 || srcHeight == 0) { + return null; + } + if (logoWidth == 0 || logoHeight == 0) { + return src; + } + //logo大小为二维码整体大小的1/5 + float scaleFactor = srcWidth * 1.0f / 5 / logoWidth; + Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888); + try { + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2); + canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null); + canvas.save(); + canvas.restore(); + } catch (Exception e) { + bitmap = null; + e.getStackTrace(); + } + return bitmap; + } + + private Bitmap createQRImage(String qrCode, int widthPix, int heightPix, int qrColor, int backColor) { + Bitmap bitmap = null; + try { + //判断URL合法性 + if (qrCode == null || "".equals(qrCode) || qrCode.length() < 1) { + return bitmap; + } + Hashtable hints = new Hashtable(); + hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); + //图像数据转换,使用了矩阵转换 + BitMatrix bitMatrix = new QRCodeWriter().encode(qrCode, BarcodeFormat.QR_CODE, widthPix, heightPix, hints); + int[] pixels = new int[widthPix * heightPix]; + //下面这里按照二维码的算法,逐个生成二维码的图片, + //两个for循环是图片横列扫描的结果 + for (int y = 0; y < heightPix; y++) { + for (int x = 0; x < widthPix; x++) { + if (bitMatrix.get(x, y)) { + pixels[y * widthPix + x] = qrColor; +// pixels[y * widthPix + x] = 0xfffd0000; +// pixels[y * widthPix + x] = 0xff3399ff; + } else { + pixels[y * widthPix + x] = backColor; + } + } + } + //生成二维码图片的格式,使用ARGB_8888 + bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix); + //显示到一个ImageView上面 + } catch (WriterException e) { + e.printStackTrace(); + } + return bitmap; + } + + private Flowable makeFlowable(final Callable func) { + return Flowable.create(new FlowableOnSubscribe() { + @Override + public void subscribe(FlowableEmitter e) throws Exception { + try { + e.onNext(func.call()); + } catch (Exception e1) { + } + } + }, BackpressureStrategy.ERROR); + } + + private Observable makeObservable(final Callable func) { + return Observable.create(new ObservableOnSubscribe() { + + @Override + public void subscribe(ObservableEmitter subscriber) throws Exception { + try { + subscriber.onNext(func.call()); + } catch (Exception e) { + } + } + }); + } +} diff --git a/qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java b/qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java new file mode 100644 index 0000000..630a59b --- /dev/null +++ b/qrutill/src/main/java/com/inlee/qrutill/bean/QRCodeBean.java @@ -0,0 +1,7 @@ +package com.inlee.qrutill.bean; + +public interface QRCodeBean { + String getFileName(); + + String getQRCodeUrl(); +} diff --git a/qrutill/uploadAliRelease.gradle b/qrutill/uploadAliRelease.gradle new file mode 100644 index 0000000..5bea87b --- /dev/null +++ b/qrutill/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-qr-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/qrutill/uploadAliSnapshot.gradle b/qrutill/uploadAliSnapshot.gradle new file mode 100644 index 0000000..5cd5ee7 --- /dev/null +++ b/qrutill/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-qr-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/qrutill/uploadLocal.gradle b/qrutill/uploadLocal.gradle new file mode 100644 index 0000000..ffaca5f --- /dev/null +++ b/qrutill/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-qr-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4329383..043ba28 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':mvp', ':test' +include ':mvp',':xrecycler',':utill',':nativelogger' ,':qrutill' diff --git a/test/build.gradle b/test/build.gradle index f10b251..956030e 100644 --- a/test/build.gradle +++ b/test/build.gradle @@ -19,8 +19,7 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - annotationProcessor rootProject.ext.dependencies["butterknife-apt"] - implementation project(":mvp") + api fileTree(dir: 'libs', include: ['*.jar']) + api "com.lennon.utill:xdroid-mvp:1.0.1" } diff --git a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java index a6810ae..aad4c6f 100644 --- a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java +++ b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/BRecAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.test.base; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.View; import cn.droidlover.xdroidmvp.base.SimpleRecAdapter; diff --git a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java index 65076ba..ca5a5aa 100644 --- a/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java +++ b/test/src/main/java/cn/droidlover/xdroidmvp/test/base/DRecAdapter.java @@ -1,7 +1,7 @@ package cn.droidlover.xdroidmvp.test.base; import android.content.Context; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java b/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java index d3c60d2..22819e4 100644 --- a/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java +++ b/test/src/main/java/cn/droidlover/xdroidmvp/test/router/StartActivity.java @@ -2,8 +2,8 @@ import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.ActivityOptionsCompat; +import androidx.core.app.ActivityOptionsCompat; import cn.droidlover.xdroidmvp.mvp.XActivity; import cn.droidlover.xdroidmvp.router.Router; diff --git a/utill/build.gradle b/utill/build.gradle new file mode 100644 index 0000000..3f664ad --- /dev/null +++ b/utill/build.gradle @@ -0,0 +1,56 @@ +apply plugin: 'com.android.library' + +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-parcelize' +version = '1.6.41' // 版本 +group = rootProject.groupId +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } +} +dependencies { + api fileTree(include: ['*.jar'], dir: 'libs') + + api rootProject.ext.dependencies["xdroid-mvp"] + + api rootProject.ext.dependencies["PersistentCookieJar"] + api rootProject.ext.dependencies["qiujuer-ui"] + api rootProject.ext.dependencies["multidex"] + api rootProject.ext.dependencies["exifinterface"] + api rootProject.ext.dependencies["constraintlayout"] + + api rootProject.ext.dependencies["navigation-fragment-ktx"] + api rootProject.ext.dependencies["navigation-ui-ktx"] + api rootProject.ext.dependencies["navigation-ui"] + api rootProject.ext.dependencies["navigation-fragment"] +} + +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} +artifacts { + archives javadocJar + archives sourcesJar +} +//apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/utill/consumer-rules.pro b/utill/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/utill/proguard-rules.pro b/utill/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/utill/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/utill/src/main/AndroidManifest.xml b/utill/src/main/AndroidManifest.xml new file mode 100644 index 0000000..d4ae61b --- /dev/null +++ b/utill/src/main/AndroidManifest.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt b/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt new file mode 100644 index 0000000..c0a6f7b --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/adapter/BaseViewAdapter.kt @@ -0,0 +1,32 @@ +package com.lennon.cn.utill.adapter + +import android.content.Context +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import cn.droidlover.xdroidmvp.base.SimpleRecAdapter +import com.lennon.cn.utill.bean.BaseViewBean +import lennon.com.utill.R + +class BaseViewAdapter(context: Context) : SimpleRecAdapter(context) { + override fun newViewHolder(itemView: View): ViewHolder { + return ViewHolder(itemView) + } + + override fun getLayoutId(): Int { + return R.layout.item_base_view + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + val d = data[position] + d.loadIcon(holder.icon) + d.loadName(holder.name) + } + + class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { + val icon = v.findViewById(R.id.icon) + val name = v.findViewById(R.id.name) + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt b/utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt new file mode 100644 index 0000000..d62903a --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/adapter/StringItemAdapter.kt @@ -0,0 +1,81 @@ +package com.lennon.cn.utill.adapter + +import android.content.Context +import android.graphics.Typeface +import android.util.TypedValue +import android.view.View +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import cn.droidlover.xdroidmvp.base.SimpleRecAdapter +import com.lennon.cn.utill.bean.StringBean +import com.lennon.cn.utill.utill.TeminalDevice +import com.lennon.cn.utill.utill.Utill +import lennon.com.utill.R + +/** + * Created by lennon on 2017/9/20. + */ + +open class StringItemAdapter(context: Context) : + SimpleRecAdapter.ViewHolder>(context) { + + private var textSize: List? = null + private var typefaces: List? = null + private var colors: List? = null + fun setTypefaces(typefaces: List) { + this.typefaces = typefaces + } + + fun setColors(colors: List) { + this.colors = colors + } + + fun setTextSize(textSize: List) { + this.textSize = textSize + } + + override fun newViewHolder(itemView: View): ViewHolder { + return ViewHolder(itemView) + } + + override fun getLayoutId(): Int { + return R.layout.item_string_item + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + holder.name.text = data[position].itemString + holder.name.setBackgroundResource(R.color.color_ffffff) + holder.name.setTextColor(Utill.getColor(context.resources, R.color.color_0F83F0)) + if (typefaces != null && typefaces!!.isNotEmpty()) { + holder.name.typeface = typefaces!![position % typefaces!!.size] + } + if (colors != null && colors!!.isNotEmpty()) { + holder.name.setBackgroundColor(Utill.getColor(context.resources, colors!![position % colors!!.size])) + } + if (textSize != null && textSize!!.isNotEmpty()) { + holder.name.textSize = TeminalDevice.dip2px( + context, + textSize!![position % textSize!!.size].toFloat() + ).toFloat() + } + } + + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + internal var name = itemView.findViewById(R.id.name) as TextView + + fun upBack(resource: Int, textColor: Int) { + name.setBackgroundResource(resource) + name.setTextColor(Utill.getColor(context.resources, textColor)) + } + + fun unselect() { + upBack(R.drawable.conner_f2f2f2, R.color.color_333333) + } + + fun select() { + upBack(R.drawable.conner_fd0202, R.color.color_ffffff) + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt new file mode 100644 index 0000000..7fde0cc --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseActivity.kt @@ -0,0 +1,264 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.Toast +import androidx.viewbinding.ViewBinding +import cn.droidlover.xdroidmvp.log.XLog +import cn.droidlover.xdroidmvp.mvp.XActivity +import cn.droidlover.xdroidmvp.mvp.XPresent +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable + +import com.lennon.cn.utill.conf.Lennon +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener +import com.lennon.cn.utill.utill.AppStatusManager +import com.lennon.cn.utill.utill.StatusBarUtil +import com.lennon.cn.utill.utill.Utill +import java.lang.Exception + + +abstract class BaseActivity

, E : ViewBinding> : XActivity(), + BaseView

{ + private var TAG = javaClass.simpleName + private var dialog: CustomProgressDialog? = null + + /** * 设置当前窗口亮度 * @param brightness */ + fun setWindowBrightness(brightness: Float) { + val window = window + val lp = window.attributes + lp.screenBrightness = brightness + window.attributes = lp + } + + /** + * 获取当前屏幕亮度 + */ + fun getBrightness(): Int { + val lp = window?.attributes + if (lp != null) { + //screenBrightness 默认为-1 + if (lp.screenBrightness < 0) return 0 + return (lp.screenBrightness * 255).toInt() + } + return 0 + } + + /** + * 隐藏虚拟按键,并且设置成全屏 + */ + fun hideBottomUIMenu() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { // lower api + val v = this.window.decorView + v.systemUiVisibility = View.GONE + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + //for new api versions. + val decorView = window.decorView + val uiOptions = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar + or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar + or View.SYSTEM_UI_FLAG_IMMERSIVE) + decorView.systemUiVisibility = uiOptions + window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) + } + } + + override fun showProgressDialog(msg: String) { + if (dialog != null) { + dialog!!.dismiss() + } + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + override fun closeProgressDialog() { + if (null != dialog) { + dialog!!.dismiss() + } + dialog = null + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(this) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, 2000) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(this) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun showLoading(visibility: Int) { + } + + override fun showLoadingError(errorType: NetError) { + + } + + override fun getContext(): Context { + return this + } + + override fun onCreate(savedInstanceState: Bundle?) { + XLog.e("$TAG:onCreate") +// Thread.setDefaultUncaughtExceptionHandler(CrashHandler(this)) + Lennon.useDensity(this) +// window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) + super.onCreate(savedInstanceState) + if (checkAppStatus()) { + BaseApplication.restart(this) + finish() + } else { + BaseApplication.addActivity(this) + try { + if ((this.findViewById(android.R.id.content) as ViewGroup).getChildAt(0) != null) { + StatusBarUtil.setColorNoTranslucent( + this, + Utill.getColor(resources, getMTitleColor()) + ) + // StatusBarUtil.setImmersiveStatusBar(this, false) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + open fun getMTitleColor(): Int { + return Lennon.getTitleColor() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + XLog.e("$TAG:onActivityResult($requestCode,$resultCode)") + super.onActivityResult(requestCode, resultCode, data) + } + + + override fun onStart() { + super.onStart() + XLog.e("$TAG:onStart()") + } + + override fun onDestroy() { + BaseApplication.exitActivity(this) + XLog.e("$TAG:onDestroy()") + super.onDestroy() + } + + override fun onPause() { + XLog.e("$TAG:onPause()") + super.onPause() + } + + override fun onResume() { + XLog.e("$TAG:onResume()") + super.onResume() + } + + override fun onBackPressed() { + XLog.e("$TAG:onBackPressed()") + super.onBackPressed() + } + + override fun onPostResume() { + XLog.e("$TAG:onPostResume()") + super.onPostResume() + } + + override fun onStop() { + XLog.e("$TAG:onStop()") + super.onStop() + } + + private fun checkAppStatus(): Boolean { + XLog.e(TAG + ":" + AppStatusManager.getInstance().appStatus) + return AppStatusManager.getInstance().appStatus == AppStatusManager.AppStatusConstant.APP_FORCE_KILLED + } + + override fun getActivity(): Activity { + return this + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java b/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java new file mode 100644 index 0000000..e60e163 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApi.java @@ -0,0 +1,49 @@ +package com.lennon.cn.utill.base; + + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +import cn.droidlover.xdroidmvp.net.IModel; +import cn.droidlover.xdroidmvp.net.XApi; +import io.reactivex.Flowable; + +/** + * Created by lennon on 2017/8/10. + */ + +public abstract class BaseApi { + public T service; + protected Class tClass; + + public BaseApi(String url) { + tClass = (Class) getModelClass(0); + try { + service = XApi.getInstance().getRetrofit(url, true).create(tClass); + } catch (Exception e) { + e.printStackTrace(); + service = XApi.getInstance().getRetrofit(url, true).create(tClass); + } + } + + public final Flowable compose(Flowable flowable) { + return flowable.compose(XApi.getApiTransformer()) + .compose(XApi.getScheduler()); + } + + //泛型类作为父类,可以获取子类的所有实际参数的类型 + @SuppressWarnings("unchecked") + public Class getModelClass(int index) { + Class modelClass; + // 得到泛型父类 + Type genType = this.getClass().getGenericSuperclass(); + //一个泛型类可能有多个泛型形参,比如ClassName 这里有两个泛型形参T和K,Class Name 这里只有1个泛型形参T + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + if (params.length - 1 < index) { + modelClass = null; + } else { + modelClass = (Class) params[index]; + } + return modelClass; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt new file mode 100644 index 0000000..678bc5d --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseApplication.kt @@ -0,0 +1,199 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources +import android.text.TextUtils +import androidx.multidex.MultiDex +import androidx.multidex.MultiDexApplication +import cn.droidlover.xdroidmvp.cache.SharedPref +import com.lennon.cn.utill.conf.Lennon +import com.lennon.cn.utill.utill.DensityUtils +import com.lennon.cn.utill.utill.Utill +import java.io.BufferedReader +import java.io.FileReader +import java.util.* + +/** + * Created by lennon on 2017/7/26. + */ +open class BaseApplication : MultiDexApplication() { + + override fun attachBaseContext(base: Context) { + super.attachBaseContext(base) + MultiDex.install(base) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + if (newConfig.fontScale != 1f) + //非默认值 + resources + super.onConfigurationChanged(newConfig) + } + + override fun getResources(): Resources { + val res = super.getResources() + if (res.configuration.fontScale != 1f) {//非默认值 + val newConfig = Configuration() + newConfig.setToDefaults()//设置默认 + res.updateConfiguration(newConfig, res.displayMetrics) + } + return res + } + + override fun onCreate() { + DensityUtils.setDensity(this) + appliction = this + super.onCreate() +// NLoggerConfig.getInstance() +// .builder() +// .tag("APP") +// .loggerLevel(LoggerLevel.INFO) +// .fileLogger(true) +// .fileDirectory(getDataFile() + "/logs") +// .fileFormatter(SimpleFormatter()) +// .expiredPeriod(7) +// .catchException(true) { _, throwable -> +// getCuttureActivity()?.finish() +// val t = if (throwable != null) { +// throwable +// } else { +// Throwable("未知异常") +// } +// t.printStackTrace() +// getCuttureActivity()!!.runOnUiThread { +// // 使用Toast来显示异常信息 +// object : Thread() { +// override fun run() { +// Looper.prepare() +// Toast.makeText( +// getCuttureActivity(), +// "很抱歉,程序出现异常,即将退出,请前往 我的 上传日志并联系客服:" + t.message, +// Toast.LENGTH_LONG +// ).show() +// Looper.loop() +// } +// }.start() +// +// Toast.makeText( +// getCuttureActivity(), +// "非常抱歉,程序出错了,请前往 我的 上传日志并联系客服:" + t.message, +// Toast.LENGTH_SHORT +// ).show() +// XLog.e("uncaughtException", t.message) +// Lennon.restartApp() +// AppExit() +// } +// }.build() + } + + companion object { + private var list: ArrayList? = null + private val loger = true + private var appliction: BaseApplication? = null + + /** + * 获取进程号对应的进程名 + * + * @param pid 进程号 + * @return 进程名 + */ + fun getProcessName(pid: Int): String? { + var reader: BufferedReader? = null + try { + reader = BufferedReader(FileReader("/proc/" + pid + "/cmdline")) + var processName = reader.readLine() + if (!TextUtils.isEmpty(processName)) { + processName = processName.trim() + } + return processName + } catch (throwable: Throwable) { + throwable.printStackTrace() + } finally { + try { + if (reader != null) { + reader.close() + } + } catch (exception: Exception) { + exception.printStackTrace() + } + } + return null + } + + fun isLoger(): Boolean { + return loger + } + + fun context(): Context? { + return appliction + } + + + fun getDataFile(): String { + Utill.makeDir(Lennon.getFilePathName()) + return Lennon.getFilePathName() + } + + fun addActivity(activity: Activity) { + if (list == null) + list = ArrayList() + list!!.add(activity) + } + + fun exitActivity(activity: Activity?) { + if (list != null) { +// list!!.remove(activity) + list!!.remove(activity) + } + } + + + fun getCuttureActivity(): Activity? { + return if (list == null || list!!.size == 0) null else list!![list!!.size - 1] + } + + + fun AppExit() { + Lennon.appExit() + for (activity in list!!) { + activity.finish() + } + System.exit(0) + } + + fun clean() { + Lennon.clean() + SharedPref.getInstance(context()).clear() + } + + + fun resources(): Resources { + return context()!!.resources + } + + + fun finshActivity() { + for (activity in list!!) { + activity.finish() + } + } + + fun restart(context: Context) { + if (list != null) + list!!.clear() + getAppliction()?.restart(context) + } + + fun getAppliction(): BaseApplication? { + return appliction + } + } + + open fun restart(context: Context) { + + } + + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt new file mode 100644 index 0000000..fce9864 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseFragment.kt @@ -0,0 +1,188 @@ +@file:Suppress("FINITE_BOUNDS_VIOLATION_IN_JAVA") + +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.viewbinding.ViewBinding +import cn.droidlover.xdroidmvp.log.XLog + +import cn.droidlover.xdroidmvp.mvp.XFragment +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener + +abstract class BaseFragment

, E : ViewBinding> : XFragment(), + BaseView

{ + + private var rootView: View? = null + private var dialog: CustomProgressDialog? = null + private var mActivity: Activity? = null + override fun showLoading(visibility: Int) { + } + + override fun showLoadingError(errorType: NetError) { + + } + protected fun findViewById(i: Int): T { + return rootView!!.findViewById(i) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + XLog.e(getName() + " onCreateView") + rootView = super.onCreateView(inflater, container, savedInstanceState) + return rootView + } + + /** + * 可见时的回调方法 + */ + open fun onVisible() { + XLog.e(getName() + " onVisible") + } + + /** + * 不可见时的回调方法 + */ + fun onInvisible() { + XLog.e(getName() + " onInvisible") + } + + override fun showProgressDialog(msg: String) { + if (dialog != null) + dialog!!.dismiss() + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + + override fun onResume() { + super.onResume() + XLog.e(getName() + " onResume") + onVisible() + } + + + protected fun getName(): String { + return javaClass.simpleName + } + + override fun closeProgressDialog() { + if (null != dialog) + dialog!!.dismiss() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed({ + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + Handler().postDelayed({ + dialog.dismiss() + runnable.run() + }, 2000) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second).show() + } + + override fun onStop() { + XLog.e(getName() + " onStop") + super.onStop() + } + + override fun onAttach(activity: Activity) { + XLog.e(getName() + " onAttach") + super.onAttach(activity) + mActivity = activity + } + + override fun onPause() { + XLog.e(getName() + " onPause") + super.onPause() + onInvisible() + } + + override fun getContext(): Context? { + return if (mActivity != null) { + mActivity + } else { + BaseApplication.getCuttureActivity() + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java b/utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java new file mode 100644 index 0000000..fca5aac --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresent.java @@ -0,0 +1,13 @@ +package com.lennon.cn.utill.base; + +import cn.droidlover.xdroidmvp.mvp.IView; +import cn.droidlover.xdroidmvp.mvp.XPresent; +import org.jetbrains.annotations.Nullable; + +public abstract class BasePresent extends XPresent { + + public BasePresent(V v){ + attachV(v); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt new file mode 100644 index 0000000..116b1fa --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BasePresentation.kt @@ -0,0 +1,151 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.os.Handler +import android.view.Display +import android.widget.Toast +import androidx.viewbinding.ViewBinding +import cn.droidlover.xdroidmvp.log.XLog +import cn.droidlover.xdroidmvp.mvp.XPresentation +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable +import com.lennon.cn.utill.conf.Lennon +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.CustomProgressDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener + +abstract class BasePresentation

,E: ViewBinding>(context: Context, display: Display) : + XPresentation(context, display), BaseView

{ + private var TAG = javaClass.simpleName + private var dialog: CustomProgressDialog? = null + private var listener: ChangeListener? = null + + interface ChangeListener { + fun onCancel(basePresentation: BasePresentation<*,*>) + fun onDismiss(basePresentation: BasePresentation<*,*>) + fun onShow(basePresentation: BasePresentation<*,*>) + } + + fun setListener(listener: ChangeListener?) { + this.listener = listener + } + + override fun showProgressDialog(msg: String) { + if (dialog != null) dialog!!.dismiss() + dialog = CustomProgressDialog(getContext()) + dialog!!.setMessage(msg) + dialog!!.show() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setOnCancelListener { listener?.onCancel(this) } + setOnDismissListener { listener?.onDismiss(this) } + setOnShowListener { listener?.onShow(this) } + } + + override fun closeProgressDialog() { + if (null != dialog) dialog!!.dismiss() + } + + override fun toast(msg: String, second: Int) { + val dialog = CommonAlertDialog(context) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String) { + toast(msg, true) + } + + override fun toast(msg: String, second: Int, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + dialog.show() + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, second * 1000L) + Toast.makeText(getContext(), msg, second) + .show() + } + + override fun toast(msg: String, runnable: ToastRunnable) { + val dialog = CommonAlertDialog(getContext()) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.show() + dialog.setCanceledOnTouchOutside(false) + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + runnable.run() + } + }) + Handler().postDelayed(Runnable { + dialog.dismiss() + runnable.run() + }, 2000L) + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun toast(msg: String, flag: Boolean) { + if (flag) { + val dialog = CommonAlertDialog(context) + dialog.setMsg(msg) + dialog.disableCancle() + dialog.setCanceledOnTouchOutside(false) + dialog.show() + dialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + dialog.dismiss() + } + }) + } + Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT) + .show() + } + + override fun showLoading(visibility: Int) { + } + + override fun showLoadingError(errorType: NetError) { + + } + + override fun getActivity(): Activity? { + return null + } + + override fun bindEvent() { + } + + override fun getOptionsMenuId(): Int { + return 0 + } + +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt new file mode 100644 index 0000000..a20f887 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/base/BaseView.kt @@ -0,0 +1,21 @@ +package com.lennon.cn.utill.base + +import android.app.Activity +import android.content.Context +import cn.droidlover.xdroidmvp.mvp.IView +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.bean.ToastRunnable + +interface BaseView

?> : IView

{ + + fun toast(msg: String, second: Int) + fun toast(msg: String) + fun toast(msg: String, runnable: ToastRunnable) + fun toast(msg: String, second: Int, runnable: ToastRunnable) + fun toast(msg: String, flag: Boolean) + fun showLoading(visibility: Int) + fun showLoadingError(errorType: NetError) + fun getContext(): Context? + + fun getActivity(): Activity? +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java b/utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java new file mode 100644 index 0000000..d36d0ff --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/BaseViewBean.java @@ -0,0 +1,14 @@ +package com.lennon.cn.utill.bean; + +import android.widget.ImageView; +import android.widget.TextView; + +public interface BaseViewBean { + public void onClick(); + + public void onLongClick(); + + public void loadIcon(ImageView icon); + + public void loadName(TextView name); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/Download.java b/utill/src/main/java/com/lennon/cn/utill/bean/Download.java new file mode 100644 index 0000000..83b908f --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/Download.java @@ -0,0 +1,73 @@ +package com.lennon.cn.utill.bean; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.Serializable; + +/** + * Created by JokAr on 16/7/5. + */ +public class Download implements Parcelable,Serializable { + + private int progress; + private long currentFileSize; + private long totalFileSize; + + public int getProgress() { + return progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } + + public long getCurrentFileSize() { + return currentFileSize; + } + + public void setCurrentFileSize(long currentFileSize) { + this.currentFileSize = currentFileSize; + } + + public long getTotalFileSize() { + return totalFileSize; + } + + public void setTotalFileSize(long totalFileSize) { + this.totalFileSize = totalFileSize; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(this.progress); + dest.writeLong(this.currentFileSize); + dest.writeLong(this.totalFileSize); + } + + public Download() { + } + + protected Download(Parcel in) { + this.progress = in.readInt(); + this.currentFileSize = in.readLong(); + this.totalFileSize = in.readLong(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Download createFromParcel(Parcel source) { + return new Download(source); + } + + @Override + public Download[] newArray(int size) { + return new Download[size]; + } + }; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java b/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java new file mode 100644 index 0000000..1b9a223 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/HttpEntity.java @@ -0,0 +1,72 @@ +package com.lennon.cn.utill.bean; + +import java.io.Serializable; +import java.util.Collection; + +import cn.droidlover.xdroidmvp.net.IModel; + +/** + * Created by dingyi on 2016/11/30. + */ + +public class HttpEntity implements IModel, Serializable { + private String msg; + private String code; + private T data; + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public boolean isSuccess() { + return "0000".equals(code); + } + + @Override + public String getErrorCode() { + return code; + } + + @Override + public boolean isNull() { + if (!"0000".equals(code)) { + return data == null && !"1000".equals(code); + } + return false; + } + + @Override + public boolean isAuthError() { + return "1000".equals(code); + } + + @Override + public boolean isBizError() { + return !"0000".equals(code); + } + + @Override + public String getErrorMsg() { + return msg; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java b/utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java new file mode 100644 index 0000000..41520f1 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/StringBean.java @@ -0,0 +1,9 @@ +package com.lennon.cn.utill.bean; + +/** + * Created by lennon on 2018/5/7. + */ + +public interface StringBean { + String getItemString(); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java b/utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java new file mode 100644 index 0000000..bde8b80 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/bean/ToastRunnable.java @@ -0,0 +1,21 @@ +package com.lennon.cn.utill.bean; + +public abstract class ToastRunnable implements Runnable { + private boolean executed; + + public ToastRunnable() { + executed = false; + } + + @Override + public void run() { + if (executed) { + } else { + function(); + } + executed = true; + + } + + protected abstract void function(); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/ACache.java b/utill/src/main/java/com/lennon/cn/utill/cache/ACache.java new file mode 100644 index 0000000..caaed21 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/cache/ACache.java @@ -0,0 +1,72 @@ +package com.lennon.cn.utill.cache; + +import android.content.Context; + +import cn.droidlover.xdroidmvp.cache.DiskCache; +import cn.droidlover.xdroidmvp.cache.ICache; +import cn.droidlover.xdroidmvp.cache.MemoryCache; + +public class ACache implements ICache { + DiskCache diskCache; + MemoryCache memoryCache; + private static ACache aCache; + + private ACache(Context context) { + diskCache = DiskCache.getInstance(context); + memoryCache = MemoryCache.getInstance(); + } + + public static ACache getInstance(Context context) { + if (aCache == null) { + aCache = new ACache(context); + } + return aCache; + + } + +// @Override +// public synchronized void put(String key, String value) { +// memoryCache.put(key, value); +// diskCache.put(key, value); +// } + + @Override + public void put(String key, Object value) { + memoryCache.put(key, value); + diskCache.put(key, value); + } + + @Override + public String get(String key) { + try { + if (memoryCache.contains(key)) { + return memoryCache.get(key).toString(); + } else if (diskCache.contains(key)) { + String s = diskCache.get(key); + memoryCache.put(key, s); + return s; + } + return ""; + } catch (Exception e) { + e.printStackTrace(); + return ""; + } + } + + @Override + public void remove(String key) { + memoryCache.remove(key); + diskCache.remove(key); + } + + @Override + public boolean contains(String key) { + return memoryCache.contains(key) || diskCache.contains(key); + } + + @Override + public void clear() { + memoryCache.clear(); + diskCache.clear(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java new file mode 100644 index 0000000..ef5b64c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataCache.java @@ -0,0 +1,145 @@ +package com.lennon.cn.utill.cache; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.lennon.cn.utill.base.BaseApplication; +import com.lennon.cn.utill.base.BaseView; +import com.lennon.cn.utill.bean.HttpEntity; +import com.lennon.cn.utill.bean.ToastRunnable; +import com.lennon.cn.utill.conf.Lennon; +import com.trello.rxlifecycle3.LifecycleProvider; + +import java.lang.reflect.Type; + +import androidx.annotation.NonNull; +import cn.droidlover.xdroidmvp.cache.SharedPref; +import cn.droidlover.xdroidmvp.mvp.IView; +import cn.droidlover.xdroidmvp.net.ApiSubscriber; +import cn.droidlover.xdroidmvp.net.NetError; +import cn.droidlover.xdroidmvp.net.XApi; +import io.reactivex.Flowable; + +/** + * Created by lennon on 2017/10/18. + */ + +public class DataCache { + BaseView activity; + String dataKey; + Flowable flowable; + LifecycleProvider lifecycleProvider; + DataCallBack dataCallBack; + ACache maCache; + protected Type tClass; + private boolean refresh = false; + + public static void initDataCache(String DataCacheKey) { + SharedPref.getInstance(BaseApplication.Companion.context()).putString("DataCacheKey", DataCacheKey); + } + + public DataCache(BaseView activity, String dataKey, Flowable flowable, + LifecycleProvider lifecycleProvider, DataCallBack dataCallBack, Type tClass) { + this.tClass = tClass; + this.activity = activity; + this.dataKey = dataKey; + this.flowable = flowable; + this.lifecycleProvider = lifecycleProvider; + this.dataCallBack = dataCallBack; + this.maCache = ACache.getInstance(BaseApplication.Companion.context()); + } + + public void getData() { + T t = getLocalData(); + if (t == null) { + activity.showProgressDialog("加载中……"); + } else { + if (dataCallBack != null) { + dataCallBack.upView(t); + } + } + getDataFromNet(); + } + + public void refresh() { + refresh = true; + getDataFromNet(); + } + + String getKey(String a) { + return a + SharedPref.getInstance(BaseApplication.Companion.context()).getString("DataCacheKey", ""); + } + + void saveData(T t) { + maCache.put(getKey(dataKey), new Gson().toJson(t)); + } + + T getLocalData() { + if (TextUtils.isEmpty(maCache.get(getKey(dataKey)))) { + return null; + } + + T t = new Gson().fromJson(maCache.get(getKey(dataKey)), tClass); + return t; + } + + void getDataFromNet() { + flowable.compose(XApi.getApiTransformer()) + .compose(XApi.getScheduler()) + .compose(lifecycleProvider.bindToLifecycle()) + .subscribe(new ApiSubscriber(activity) { + @Override + protected void onFail(NetError error) { + if (getLocalData() != null) { + dataCallBack.netError(error); + } else { + dataCallBack.netErrorForNoData(error); + } + activity.closeProgressDialog(); + if (error.getType() == NetError.AuthError) { + activity.toast("登陆失效", new ToastRunnable() { + + @Override + protected void function() { + Lennon.Companion.requserLogin(); + } + }); + return; + } + } + + @Override + protected boolean useCommonErrorHandler() { + return false; + } + + @Override + public void onNext(T o) { + if (getLocalData() != null) { + String a = new Gson().toJson(getLocalData()); + String b = new Gson().toJson(o); + if (a.equals(b) && refresh) { + dataCallBack.upView(o); + } else if (a.equals(b) && !refresh) { + + } else { + dataCallBack.upView(o); + saveData(o); + } + } else { + dataCallBack.upView(o); + saveData(o); + } + activity.closeProgressDialog(); + } + }); + } + + public interface DataCallBack { + void upView(@NonNull T t); + + void netErrorForNoData(@NonNull NetError error); + + void netError(@NonNull NetError error); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java new file mode 100644 index 0000000..ea04b5b --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/cache/DataListCache.java @@ -0,0 +1,172 @@ +package com.lennon.cn.utill.cache; + +import android.text.TextUtils; + +import cn.droidlover.xdroidmvp.net.NetError; + +import com.google.gson.Gson; +import com.lennon.cn.utill.base.BaseApplication; +import com.lennon.cn.utill.base.BaseView; +import com.lennon.cn.utill.bean.HttpEntity; +import com.lennon.cn.utill.bean.ToastRunnable; +import com.lennon.cn.utill.conf.Lennon; +import com.trello.rxlifecycle3.LifecycleProvider; + +import java.lang.reflect.Type; + +import androidx.annotation.NonNull; + +import cn.droidlover.xdroidmvp.cache.SharedPref; +import cn.droidlover.xdroidmvp.net.ApiSubscriber; +import cn.droidlover.xdroidmvp.net.XApi; +import io.reactivex.Flowable; + +/** + * @author lennon + * @date 2017/10/18 + */ + +public class DataListCache { + BaseView activity; + String dataKey; + LifecycleProvider lifecycleProvider; + DataCallBack dataCallBack; + ACache maCache; + int startPage = 1; + int page = 0; + protected Type tClass; + private boolean refresh = false; + + public static void initDataCache(String DataCacheKey) { + SharedPref.getInstance(BaseApplication.Companion.context()).putString("DataCacheKey", DataCacheKey); + } + + public DataListCache(BaseView activity, String dataKey, int startPage, + LifecycleProvider lifecycleProvider, DataCallBack dataCallBack, Type tClass) { + this.tClass = tClass; + this.activity = activity; + this.dataKey = dataKey; + this.startPage = startPage; + this.lifecycleProvider = lifecycleProvider; + this.dataCallBack = dataCallBack; + this.maCache = ACache.getInstance(BaseApplication.Companion.context()); + } + + private void getData(int page) { + T t = getLocalData(page); + if (t == null) { + activity.showProgressDialog("加载中……"); + } else { + if (dataCallBack != null) { + dataCallBack.upView(t, page); + } + } + getDataFromNet(page); + } + + public void getData() { + getData(startPage); + } + + public void refresh() { + refresh = true; + getDataFromNet(startPage); + } + + String getKey(String a) { + return a + SharedPref.getInstance(BaseApplication.Companion.context()).getString("DataCacheKey", ""); + } + + public void loadMore() { + getData(page + 1); + } + + void saveData(T t, int page) { + maCache.put(getKey(dataKey) + "_" + page, new Gson().toJson(t)); + } + + T getLocalData(int page) { + if (TextUtils.isEmpty(maCache.get(getKey(dataKey) + "_" + page))) { + return null; + } + T t = new Gson().fromJson(maCache.get(getKey(dataKey) + "_" + page), tClass); + return t; + } + + void getDataFromNet(final int page) { + this.page = page; + dataCallBack.getFlowable(page).compose(XApi.getApiTransformer()) + .compose(XApi.getScheduler()) + .compose(lifecycleProvider.bindToLifecycle()) + .subscribe(new ApiSubscriber(activity) { + @Override + protected void onFail(NetError error) { + if (getLocalData(page) != null) { + dataCallBack.netError(error); + } else { + dataCallBack.netErrorForNoData(error); + } + activity.closeProgressDialog(); + if (error.getType() == NetError.AuthError) { + activity.toast("登陆失效", new ToastRunnable() { + + @Override + protected void function() { + Lennon.Companion.requserLogin(); + } + }); + return; + } + } + + @Override + protected boolean useCommonErrorHandler() { + return false; + } + + @Override + public void onNext(T o) { + if (getLocalData(page) != null) { + String a = new Gson().toJson(getLocalData(page)); + String b = new Gson().toJson(o); + if (a.equals(b) && refresh) { + dataCallBack.upView(o, page); + } else if (a.equals(b) && !refresh) { + + } else { + dataCallBack.upView(o, page); + saveData(o, page); + } + } else { + dataCallBack.upView(o, page); + saveData(o, page); + } + activity.closeProgressDialog(); + } + }); + } + + public interface DataCallBack { + /** + * @param t + * @param page + */ + void upView(@NonNull T t, int page); + + /** + * @param error + */ + void netErrorForNoData(@NonNull NetError error); + + /** + * @param error + */ + void netError(@NonNull NetError error); + + /** + * @param page + * @return Flowable + */ + Flowable getFlowable(int page); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt new file mode 100644 index 0000000..76722d5 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/conf/Lennon.kt @@ -0,0 +1,121 @@ +package com.lennon.cn.utill.conf + +import android.app.Activity +import android.app.Presentation +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.utill.DensityUtils +import lennon.com.utill.R + +class Lennon { + companion object { + + var provider: LennonProvider? = null + + fun registProvider(provider: LennonProvider) { + this.provider = provider + } + + fun getFileProvide(): String { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.getFileProvide() + } + + fun appName(): String { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.appName() + } + + fun requserLogin() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.requserLogin() + } + + fun appExit() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.appExit() + } + + fun getLogo(): Int { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.getLogo() + } + + fun getTitleColor(): Int { + if (provider == null) { + throw Throwable("请先注册provider") + } + return if (provider!!.getTitleColor() > 0) { + provider!!.getTitleColor() + } else { + R.color.color_fd0202 + } + } + + fun clean() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.clean() + } + + fun isTest(): Boolean { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.isTest() + } + + fun getFilePathName(): String { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.getFilePathName() + } + + fun useDensity(activity: Activity) { + if (provider == null) { + DensityUtils.setDefault(activity) + } else { + provider!!.setDensity(activity) + } + } + + fun handleNetError(error: NetError): Boolean { + if (provider == null) { + throw Throwable("请先注册provider") + } + return provider!!.handleNetError(error) + } + + fun restartApp() { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.restartApp() + } + + fun setDensity(orientation: DensityUtils.Density) { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.setDensity(orientation) + } + + fun setPixels(pixels: Float) { + if (provider == null) { + throw Throwable("请先注册provider") + } + provider!!.setPixels(pixels) + } + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt new file mode 100644 index 0000000..b2f04dd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/conf/LennonProvider.kt @@ -0,0 +1,44 @@ +package com.lennon.cn.utill.conf + +import android.app.Activity +import cn.droidlover.xdroidmvp.net.NetError +import com.lennon.cn.utill.utill.DensityUtils + +abstract class LennonProvider { + abstract fun appName(): String + abstract fun requserLogin() + abstract fun appExit() + abstract fun getLogo(): Int + abstract fun getTitleColor(): Int + abstract fun clean() + abstract fun isTest(): Boolean + abstract fun getFilePathName(): String + private var orientation: DensityUtils.Density = DensityUtils.Density.WIDTH + private var pixels: Float = 0.0f + + fun setPixels(pixels: Float) { + this.pixels = pixels + } + + fun setDensity(orientation: DensityUtils.Density) { + this.orientation = orientation + } + + open fun setDensity(activity: Activity) { + if (useDensity()) { + if (pixels > 0.0f) { + DensityUtils.setOrientation(activity, orientation, pixels) + } else { + DensityUtils.setOrientation(activity, orientation) + } + } + } + + open fun useDensity(): Boolean { + return true + } + + abstract fun handleNetError(error: NetError): Boolean + abstract fun restartApp() + abstract fun getFileProvide(): String +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java new file mode 100644 index 0000000..978ef14 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSearchSelectDialog.java @@ -0,0 +1,260 @@ +package com.lennon.cn.utill.dialog; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.Typeface; +import android.os.IBinder; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.lennon.cn.utill.adapter.StringItemAdapter; +import com.lennon.cn.utill.bean.StringBean; +import com.lennon.cn.utill.widget.SearchView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import cn.droidlover.xrecyclerview.RecyclerItemCallback; +import cn.droidlover.xrecyclerview.XRecyclerView; +import lennon.com.utill.R; + +public class BottomSearchSelectDialog extends PopupWindow { + protected Context context; + private WindowManager wm; + private View maskView; + private SearchView dialog_search; + private XRecyclerView xRecyclerView; + private TextView cancle; + private Listener listener; + private List list; + private List typefaces; + private List colors; + private List textSize; + + public void setTypefaces(List typefaces) { + this.typefaces = typefaces; + } + + public void setColors(List colors) { + this.colors = colors; + } + + public void setTextSize(List textSize) { + this.textSize = textSize; + } + + + public void setListener(Listener listener) { + this.listener = listener; + } + + public interface Listener { + void onItemClick(int position, T model); + + boolean isResult(@NonNull String text, @NonNull T t); + + void onCancel(); + } + + public BottomSearchSelectDialog(Context context, final T... a) { + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + initType(); + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSearchSelectDialog(Context context, List typefaces, + List colors, + List textSize, final T... a) { + this.typefaces = typefaces; + this.colors = colors; + this.textSize = textSize; + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + initType(); + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSearchSelectDialog(Context context, List list) { + this.context = context; + this.list = list; + initType(); + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + private View generateCustomView() { + View root = View.inflate(context, R.layout.dialog_bottom_search_select, null); + dialog_search = root.findViewById(R.id.dialog_bottom_search_select_search); + cancle = root.findViewById(R.id.dialog_bottom_search_select_cancle); + xRecyclerView = root.findViewById(R.id.dialog_bottom_search_select_list); + xRecyclerView.verticalLayoutManager(context); + final StringItemAdapter adapter = new StringItemAdapter(context); + xRecyclerView.setAdapter(adapter); + adapter.setRecItemClick(new RecyclerItemCallback() { + @Override + public void onItemClick(int position, T model, int tag, StringItemAdapter.ViewHolder holder) { + super.onItemClick(position, model, tag, holder); + if (listener != null) { + listener.onItemClick(position, model); + } + } + }); + dialog_search.setSearchViewListener(new SearchView.SearchViewListener() { + @Override + public void onClear() { + adapter.setData(BottomSearchSelectDialog.this.list); + } + + @Override + public void onSearch(String text) { + List l = new ArrayList<>(); + if (listener != null) { + for (T t : list) { + if (listener.isResult(text, t)) { + l.add(t); + } + } + } else { + l.addAll(list); + } + adapter.setData(l); + } + + @Override + public void onResult(String text) { + + } + + @Override + public void onSearchByButton(String text) { + + } + }); + cancle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (listener != null) { + listener.onCancel(); + } + } + }); + if (list != null) { + adapter.setData(list); + } + if (typefaces != null) { + adapter.setTypefaces(typefaces); + } + if (textSize != null) { + adapter.setTextSize(textSize); + } + if (colors != null) { + adapter.setColors(colors); + } + return root; + } + + @TargetApi(23) + private void initType() { + // 解决华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层的bug。 + // android4.0及以上版本都有这个hide方法,根据jvm原理,可以直接调用,选择android6.0版本进行编译即可。 + setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); + } + + @Override + public void showAtLocation(View parent, int gravity, int x, int y) { + addMaskView(parent.getWindowToken()); + super.showAtLocation(parent, gravity, x, y); + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff) { + addMaskView(anchor.getWindowToken()); + super.showAsDropDown(anchor, xoff, yoff); + } + + @Override + public void dismiss() { + removeMaskView(); + super.dismiss(); + } + + /** + * 显示在界面的底部 + */ + public void show(Activity activity) { + showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); + } + + private void addMaskView(IBinder token) { + WindowManager.LayoutParams p = new WindowManager.LayoutParams(); + p.width = WindowManager.LayoutParams.MATCH_PARENT; + p.height = WindowManager.LayoutParams.MATCH_PARENT; + p.format = PixelFormat.TRANSLUCENT; + p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + p.token = token; + p.windowAnimations = android.R.style.Animation_Toast; + maskView = new View(context); + maskView.setBackgroundColor(0x7f000000); + maskView.setFitsSystemWindows(false); + // 华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层,导致界面卡死, + // 这里新增加按bug返回。 + // initType方法已经解决该问题,但是还是留着这个按back返回功能,防止其他手机出现华为手机类似问题。 + maskView.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + removeMaskView(); + return true; + } + return false; + } + }); + wm.addView(maskView, p); + } + + private void removeMaskView() { + if (maskView != null) { + wm.removeViewImmediate(maskView); + maskView = null; + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java new file mode 100644 index 0000000..90e7924 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/BottomSelectDialog.java @@ -0,0 +1,227 @@ +package com.lennon.cn.utill.dialog; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.PixelFormat; +import android.graphics.Typeface; +import android.os.Build; +import android.os.IBinder; +import android.view.*; +import android.widget.PopupWindow; +import android.widget.TextView; + +import androidx.annotation.ColorRes; + +import cn.droidlover.xrecyclerview.RecyclerItemCallback; +import cn.droidlover.xrecyclerview.XRecyclerView; + +import com.lennon.cn.utill.adapter.StringItemAdapter; +import com.lennon.cn.utill.bean.StringBean; + +import lennon.com.utill.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by lennon on 2018/5/16. + */ + +public class BottomSelectDialog extends PopupWindow { + protected Context context; + private WindowManager wm; + private View maskView; + private XRecyclerView xRecyclerView; + private TextView cancel; + private Listener listener; + private List list; + private List typefaces; + private List colors; + private List textSize; + + public void setTypefaces(List typefaces) { + this.typefaces = typefaces; + } + + public void setColors(List colors) { + this.colors = colors; + } + + public void setTextSize(List textSize) { + this.textSize = textSize; + } + + + public void setListener(Listener listener) { + this.listener = listener; + } + + public interface Listener { + void onItemClick(int position, T model); + + void onCancel(); + } + + public BottomSelectDialog(Context context, final T... a) { + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSelectDialog(Context context, List typefaces, + List colors, + List textSize, final T... a) { + this.typefaces = typefaces; + this.colors = colors; + this.textSize = textSize; + List t = new ArrayList<>(); + if (a != null) { + Collections.addAll(t, a); + } + this.context = context; + this.list = t; + initType(); + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public BottomSelectDialog(Context context, List list) { + this.context = context; + this.list = list; + initType(); + wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + setContentView(generateCustomView()); + setWidth(ViewGroup.LayoutParams.MATCH_PARENT); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setOutsideTouchable(true); + setFocusable(true); + setBackgroundDrawable(context.getResources().getDrawable(android.R.color.transparent)); + setAnimationStyle(R.style.Animations_BottomPush); + } + + public View generateCustomView() { + + View root = View.inflate(context, R.layout.dialog_bottom_select, null); + xRecyclerView = root.findViewById(R.id.list); + xRecyclerView.verticalLayoutManager(context); + StringItemAdapter adapter = new StringItemAdapter(context); + xRecyclerView.setAdapter(adapter); + adapter.setRecItemClick(new RecyclerItemCallback() { + @Override + public void onItemClick(int position, T model, int tag, StringItemAdapter.ViewHolder holder) { + super.onItemClick(position, model, tag, holder); + if (listener != null) { + listener.onItemClick(position, model); + } + } + }); + if (list != null) { + adapter.setData(list); + } + if (typefaces != null) { + adapter.setTypefaces(typefaces); + } + if (textSize != null) { + adapter.setTextSize(textSize); + } + if (colors != null) { + adapter.setColors(colors); + } + cancel = root.findViewById(R.id.cancel); + cancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onCancel(); + } + dismiss(); + } + }); + return root; + } + + @TargetApi(23) + private void initType() { + // 解决华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层的bug。 + // android4.0及以上版本都有这个hide方法,根据jvm原理,可以直接调用,选择android6.0版本进行编译即可。 + setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL); + } + + @Override + public void showAtLocation(View parent, int gravity, int x, int y) { + addMaskView(parent.getWindowToken()); + super.showAtLocation(parent, gravity, x, y); + } + + @Override + public void showAsDropDown(View anchor, int xoff, int yoff) { + addMaskView(anchor.getWindowToken()); + super.showAsDropDown(anchor, xoff, yoff); + } + + @Override + public void dismiss() { + removeMaskView(); + super.dismiss(); + } + + /** + * 显示在界面的底部 + */ + public void show(Activity activity) { + showAtLocation(activity.getWindow().getDecorView(), Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); + } + + private void addMaskView(IBinder token) { + WindowManager.LayoutParams p = new WindowManager.LayoutParams(); + p.width = WindowManager.LayoutParams.MATCH_PARENT; + p.height = WindowManager.LayoutParams.MATCH_PARENT; + p.format = PixelFormat.TRANSLUCENT; + p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; + p.token = token; + p.windowAnimations = android.R.style.Animation_Toast; + maskView = new View(context); + maskView.setBackgroundColor(0x7f000000); + maskView.setFitsSystemWindows(false); + // 华为手机在home建进入后台后,在进入应用,蒙层出现在popupWindow上层,导致界面卡死, + // 这里新增加按bug返回。 + // initType方法已经解决该问题,但是还是留着这个按back返回功能,防止其他手机出现华为手机类似问题。 + maskView.setOnKeyListener(new View.OnKeyListener() { + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + removeMaskView(); + return true; + } + return false; + } + }); + wm.addView(maskView, p); + } + + private void removeMaskView() { + if (maskView != null) { + wm.removeViewImmediate(maskView); + maskView = null; + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java new file mode 100644 index 0000000..540da14 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CommonAlertDialog.java @@ -0,0 +1,169 @@ +package com.lennon.cn.utill.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.*; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/23. + */ + +public class CommonAlertDialog extends Dialog { + + private TextView mTitleTv; + private TextView mMsgTv; + private TextView mSureTv; + private TextView mCancleTv; + private EditText mQtyEdt; + private EditText mContentEdt; + private boolean cancelable = true; + + private OnAlertDialogListener mOnListener; + private String mQtyString; + private int mIndex; + + public CommonAlertDialog(Context context) { + super(context, R.style.common_dialog); + setParams(); + } + + public void setIndex(int index) { + this.mIndex = index; + } + + public int getIndex() { + return mIndex; + } + + public void setTitle(String msg) { + mTitleTv.setText(msg); + } + + public void setMsg(String msg) { + mMsgTv.setVisibility(View.VISIBLE); + mQtyEdt.setVisibility(View.GONE); + mContentEdt.setVisibility(View.GONE); + mMsgTv.setText(msg); + } + + public void setSureMsg(String msg) { + mSureTv.setText(msg); + } + + public void setCancleMsg(String msg) { + mCancleTv.setText(msg); + } + + @Override + public void setCancelable(boolean flag) { + super.setCancelable(flag); + cancelable = flag; + } + + public void setQty(String text) { + mMsgTv.setVisibility(View.GONE); + mContentEdt.setVisibility(View.GONE); + mQtyEdt.setVisibility(View.VISIBLE); + mQtyString = text; + mQtyEdt.setText(text); + mQtyEdt.setSelection(text.length()); + } + + public void setContent(String content) { + mMsgTv.setVisibility(View.GONE); + mQtyEdt.setVisibility(View.GONE); + mContentEdt.setVisibility(View.VISIBLE); + mContentEdt.setText(content); + mContentEdt.setSelection(mContentEdt.getText().length()); + } + + public void setDialogListener(OnAlertDialogListener listener) { + this.mOnListener = listener; + } + + + public void disableCancle() { + mCancleTv.setVisibility(View.GONE); + } + + private void setParams() { + setContentView(R.layout.common_alert_dialog); + Window window = getWindow(); + window.setLayout(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); + mTitleTv = (TextView) findViewById(R.id.common_alert_dialog_title_tv); + mMsgTv = (TextView) findViewById(R.id.common_alert_dialog_msg_tv); + mSureTv = (TextView) findViewById(R.id.common_alert_dialog_sure_tv); + mCancleTv = (TextView) findViewById(R.id.common_alert_dialog_cancle_tv); + mQtyEdt = (EditText) findViewById(R.id.common_alert_dialog_qty_edt); + mContentEdt = (EditText) findViewById(R.id.common_alert_dialog_content_edt); + mSureTv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnListener) { + if (mMsgTv.getVisibility() == View.VISIBLE) { + mOnListener.onSure(); + } else if (mContentEdt.getVisibility() == View.VISIBLE) { + mOnListener.onSure(mContentEdt.getText().toString()); + } else { + mOnListener.onSure(mIndex, mQtyString); + } + } + } + }); + mCancleTv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnListener) { + mOnListener.onCancle(); + } + } + }); + + mQtyEdt.addTextChangedListener(mWatcher); + setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SEARCH) { + if (mOnListener != null) { + mOnListener.onCancle(); + return true; + } + return false; + } else { + return false; //默认返回 false + } + } + }); + } + + private TextWatcher mWatcher = new TextWatcher() { + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (s != null && !"".equals(s.toString())) { + mQtyString = s.toString(); + } else { + mQtyString = "0"; + } + } + }; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java new file mode 100644 index 0000000..14862a3 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomDialog.java @@ -0,0 +1,75 @@ +package com.lennon.cn.utill.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.os.Build; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; + +import lennon.com.utill.R; + +public class CustomDialog extends Dialog { + + private Context mContext; + + public CustomDialog(Context context) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + applyCompat(); + } + + public CustomDialog(Context context, int msg_resid) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setMessage(msg_resid); + applyCompat(); + } + + private void applyCompat() { + if (Build.VERSION.SDK_INT < 19) { + return; + } + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + + /** + * setMessage 提示内容 + * + * @param strMessage + */ + public void setMessage(String strMessage) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setVisibility(View.VISIBLE); + tvMsg.setText(strMessage); + } else { + tvMsg.setVisibility(View.GONE); + } + } + + /** + * setMessage 提示内容 + * + * @param resid + */ + public void setMessage(int resid) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setText(resid); + } + } + + private void setParams() { + setContentView(R.layout.custom_dialog); + // 设置全屏 + getWindow().setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java new file mode 100644 index 0000000..6a997fd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/CustomProgressDialog.java @@ -0,0 +1,127 @@ +package com.lennon.cn.utill.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.os.Build; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; + +import net.qiujuer.genius.ui.widget.Loading; + +import lennon.com.utill.R; + + +/** + * @author luoying + */ +public class CustomProgressDialog extends Dialog { + + private Context mContext; + private Loading loading; + + public CustomProgressDialog(Context context) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + applyCompat(); + } + + public CustomProgressDialog(Context context, String title, String msg) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setTitile(title); + setMessage(msg); + applyCompat(); + } + + public CustomProgressDialog(Context context, int msg_resid) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setMessage(msg_resid); + applyCompat(); + } + + public CustomProgressDialog(Context context, int title_resid, int msg_resid) { + super(context, R.style.dialog_progress); + this.mContext = context; + setParams(); + setTitile(title_resid); + setMessage(msg_resid); + applyCompat(); + } + + private void applyCompat() { + if (Build.VERSION.SDK_INT < 19) { + return; + } + getWindow().setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + loading = (Loading) findViewById(R.id.progressdialog_anim_progress); + loading.start(); + } + + /** + * setTitile 标题 + * + * @param strTitle + * @return + */ + public void setTitile(String strTitle) { + + } + + /** + * setTitile 标题 + * + * @param title_resid + * @return + */ + public void setTitile(int title_resid) { + + } + + /** + * setMessage 提示内容 + * + * @param strMessage + * @return + */ + public void setMessage(String strMessage) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setVisibility(View.VISIBLE); + tvMsg.setText(strMessage); + } else { + tvMsg.setVisibility(View.GONE); + } + } + + /** + * setMessage 提示内容 + * + * @param resid + * @return + */ + public void setMessage(int resid) { + TextView tvMsg = (TextView) findViewById(R.id.progressdialog_ms_tv); + if (tvMsg != null) { + tvMsg.setText(resid); + } + } + + private void setParams() { + setContentView(R.layout.customprogressdialog); + // 设置全屏 + getWindow().setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java b/utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java new file mode 100644 index 0000000..c6d696a --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/dialog/OnAlertDialogListener.java @@ -0,0 +1,12 @@ +package com.lennon.cn.utill.dialog; + +/** + * Created by dingyi on 2016/12/2. + */ + +public class OnAlertDialogListener { + public void onSure(){}; + public void onSure(String content){}; + public void onSure(int index, String content){}; + public void onCancle(){}; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java new file mode 100644 index 0000000..f83aea0 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AESUtils.java @@ -0,0 +1,93 @@ +package com.lennon.cn.utill.utill; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import kotlin.text.Charsets; + + +public class AESUtils { + + public static final String PASSWARDKEY_STRING = "KrOrO67PfIfjOjqe"; + private static final String IV_STRING = "16-Bytes--String"; + + public AESUtils() { + } + + public static byte[] decryptAESByte(String content, String key) throws Exception { + byte[] encryptedBytes = ByteUtil.hexToByteArray(content); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(2, secretKey, ivParameterSpec); + byte[] result = cipher.doFinal(encryptedBytes); + return result; + } + + public static String encryptAES(byte[] byteContent, String key) throws Exception { + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(1, secretKeySpec, ivParameterSpec); + byte[] encryptedBytes = cipher.doFinal(byteContent); + return ByteUtil.byteToHexStr(encryptedBytes); + } + + public static String encryptAES(String content, String key) throws Exception { + byte[] byteContent = content.getBytes("UTF-8"); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(1, secretKeySpec, ivParameterSpec); + byte[] encryptedBytes = cipher.doFinal(byteContent); + return toHex(encryptedBytes); + } + + public static String toHex(byte[] buffer) { + StringBuffer sb = new StringBuffer(buffer.length * 2); + + for (int i = 0; i < buffer.length; ++i) { + sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16)); + sb.append(Character.forDigit(buffer[i] & 15, 16)); + } + + return sb.toString(); + } + + public static byte[] fromHex(String hex) { + byte[] res = new byte[hex.length() / 2]; + char[] chs = hex.toCharArray(); + int i = 0; + + for (int c = 0; i < chs.length; ++c) { + res[c] = (byte) Integer.parseInt(new String(chs, i, 2), 16); + i += 2; + } + + return res; + } + + public static String decryptAES(String content, String key) throws Exception { + byte[] encryptedBytes = fromHex(content); + byte[] enCodeFormat = key.getBytes(); + SecretKeySpec secretKey = new SecretKeySpec(enCodeFormat, "AES"); + byte[] initParam = "16-Bytes--String".getBytes(); + IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(2, secretKey, ivParameterSpec); + byte[] result = cipher.doFinal(encryptedBytes); + return new String(result, "UTF-8"); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java new file mode 100644 index 0000000..580b40e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AmtUtils.java @@ -0,0 +1,247 @@ +package com.lennon.cn.utill.utill; + +import java.math.BigDecimal; + +public class AmtUtils { + + /** + * 功能描述:金额字符串转换:单位分转成单元 + *

+ * 传入需要转换的金额字符串 + * + * @return 转换后的金额字符串 + */ + public static String fenToYuan(Object o) { + if (o == null) { + return "0.00"; + } + String s = o.toString(); +// if ("".equals(s)) { +// return "0.00"; +// } + if (s.contains(".")) { + s = s.substring(0, s.indexOf(".")); + } + int len = -1; + StringBuilder sb = new StringBuilder(); + if (s != null && s.trim().length() > 0 && !"null".equalsIgnoreCase(s)) { + s = removeZero(s); + if (s != null && s.trim().length() > 0 && !"null".equalsIgnoreCase(s)) { + len = s.length(); + int tmp = s.indexOf("-"); + if (tmp >= 0) { + if (len == 2) { + sb.append("-0.0").append(s.substring(1)); + } else if (len == 3) { + sb.append("-0.").append(s.substring(1)); + } else { + sb.append(s.substring(0, len - 2)).append(".").append(s.substring(len - 2)); + } + } else { + if (len == 1) { + sb.append("0.0").append(s); + } else if (len == 2) { + sb.append("0.").append(s); + } else { + sb.append(s.substring(0, len - 2)).append(".").append(s.substring(len - 2)); + } + } + } else { + sb.append("0.00"); + } + } else { + sb.append("0.00"); + } + return sb.toString(); + } + + /** + * 功能描述:金额字符串转换:单位元转成单分 + *

+ * 传入需要转换的金额字符串 + * + * @return 转换后的金额字符串 + */ + public static String yuanToFen(Object o) { + if (o == null) { + return "0"; + } + String s = o.toString(); + int posIndex = -1; + String str = ""; + StringBuilder sb = new StringBuilder(); + if (s != null && s.trim().length() > 0 && !"null".equalsIgnoreCase(s)) { + posIndex = s.indexOf("."); + if (posIndex > 0) { + int len = s.length(); + if (len == posIndex + 1) { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append("00"); + } else if (len == posIndex + 2) { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append(s.substring(posIndex + 1, posIndex + 2)).append("0"); + } else if (len == posIndex + 3) { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append(s.substring(posIndex + 1, posIndex + 3)); + } else { + str = s.substring(0, posIndex); + if (str == "0") { + str = ""; + } + sb.append(str).append(s.substring(posIndex + 1, posIndex + 3)); + } + } else { + sb.append(s).append("00"); + } + } else { + sb.append("0"); + } + str = removeZero(sb.toString()); + if (str != null && str.trim().length() > 0 && !"null".equalsIgnoreCase(str.trim())) { + return str; + } else { + return "0"; + } + } + + /** + * 功能描述:去除字符串首部为"0"字符 + * + * @param str 传入需要转换的字符串 + * @return 转换后的字符串 + */ + public static String removeZero(String str) { + char ch; + String result = ""; + if (str != null && str.trim().length() > 0 && !"null".equalsIgnoreCase(str.trim())) { + try { + for (int i = 0; i < str.length(); i++) { + ch = str.charAt(i); + if (ch != '0') { + result = str.substring(i); + break; + } + } + } catch (Exception e) { + result = ""; + } + } else { + result = ""; + } + return result; + + } + + /** + * 金额(分) + * + * @param rate 手续费v + * 小数点后位数 + * @return + */ + public static String getSettleAmt(String amountfen, double rate) { + String feeAmtStr = String.valueOf(Double.valueOf(AmtUtils.fenToYuan(amountfen)) * rate); + BigDecimal mData = new BigDecimal(feeAmtStr).setScale(2, BigDecimal.ROUND_HALF_UP); + String resultAmt = AmtUtils.yuanToFen(String.valueOf(mData)); + return resultAmt; + } + + /** + * 金额为分的格式 + */ + private static final String CURRENCY_FEN_REGEX = "\\-?[0-9]+"; + + /** + * 将分为单位的转换为元并返回金额格式的字符串 (除100) + * + * @param amount + * @return + * @throws Exception + */ + public static String changeF2Y(Long amount) throws Exception { + if (!amount.toString().matches(CURRENCY_FEN_REGEX)) { + throw new Exception("金额格式有误"); + } + int flag = 0; + String amString = amount.toString(); + if (amString.charAt(0) == '-') { + flag = 1; + amString = amString.substring(1); + } + StringBuffer result = new StringBuffer(); + if (amString.length() == 1) { + result.append("0.0").append(amString); + } else if (amString.length() == 2) { + result.append("0.").append(amString); + } else { + String intString = amString.substring(0, amString.length() - 2); + for (int i = 1; i <= intString.length(); i++) { + result.append(intString.substring(intString.length() - i, intString.length() - i + 1)); + } + result.reverse().append(".").append(amString.substring(amString.length() - 2)); + } + if (flag == 1) { + return "-" + result.toString(); + } else { + return result.toString(); + } + } + + /** + * 将分为单位的转换为元 (除100) + * + * @param amount + * @return + * @throws Exception + */ + public static String changeF2Y(String amount) throws Exception { + if (!amount.matches(CURRENCY_FEN_REGEX)) { + throw new Exception("金额格式有误"); + } + return String.format("%.2f", BigDecimal.valueOf(Long.valueOf(amount)).divide(new BigDecimal(100))); + } + + /** + * 将元为单位的转换为分 (乘100) + * + * @param amount + * @return + */ + public static String changeY2F(Long amount) { + return BigDecimal.valueOf(amount).multiply(new BigDecimal(100)).toString(); + } + + /** + * 将元为单位的转换为分 替换小数点,支持以逗号区分的金额 + * + * @param amount + * @return + */ + public static String changeY2F(String amount) { + String currency = amount.replaceAll("\\$|\\¥|\\,", ""); + //处理包含, ¥ 或者$的金额 + int index = currency.indexOf("."); + int length = currency.length(); + long amLong = 0L; + if (index == -1) { + amLong = Long.parseLong(currency + "00"); + } else if (length - index >= 3) { + amLong = Long.parseLong((currency.substring(0, index + 3)).replace(".", "")); + } else if (length - index == 2) { + amLong = Long.parseLong((currency.substring(0, index + 2)).replace(".", "") + 0); + } else { + amLong = Long.parseLong((currency.substring(0, index + 1)).replace(".", "") + "00"); + } + return Long.toString(amLong); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java new file mode 100644 index 0000000..0f071e9 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AnimationUtil.java @@ -0,0 +1,307 @@ +package com.lennon.cn.utill.utill; + +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.view.animation.RotateAnimation; +import android.view.animation.ScaleAnimation; + +public class AnimationUtil { + /** + * 默认动画持续时间 + */ + public static final long DEFAULT_ANIMATION_DURATION = 400; + + + /** + * 获取一个旋转动画 + * + * @param fromDegrees 开始角度 + * @param toDegrees 结束角度 + * @param pivotXType 旋转中心点X轴坐标相对类型 + * @param pivotXValue 旋转中心点X轴坐标 + * @param pivotYType 旋转中心点Y轴坐标相对类型 + * @param pivotYValue 旋转中心点Y轴坐标 + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个旋转动画 + */ + public static RotateAnimation getRotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue, long durationMillis, Animation.AnimationListener animationListener) { + RotateAnimation rotateAnimation = new RotateAnimation(fromDegrees, + toDegrees, pivotXType, pivotXValue, pivotYType, pivotYValue); + rotateAnimation.setDuration(durationMillis); + if (animationListener != null) { + rotateAnimation.setAnimationListener(animationListener); + } + return rotateAnimation; + } + + + /** + * 获取一个根据视图自身中心点旋转的动画 + * + * @param durationMillis 动画持续时间 + * @param animationListener 动画监听器 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(long durationMillis, Animation.AnimationListener animationListener) { + return getRotateAnimation(0f, 359f, Animation.RELATIVE_TO_SELF, 0.5f, + Animation.RELATIVE_TO_SELF, 0.5f, durationMillis, + animationListener); + } + + + /** + * 获取一个根据中心点旋转的动画 + * + * @param duration 动画持续时间 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(long duration) { + return getRotateAnimationByCenter(duration, null); + } + + + /** + * 获取一个根据视图自身中心点旋转的动画 + * + * @param animationListener 动画监听器 + * @return 一个根据中心点旋转的动画 + */ + public static RotateAnimation getRotateAnimationByCenter(Animation.AnimationListener animationListener) { + return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, + animationListener); + } + + + /** + * 获取一个根据中心点旋转的动画 + * + * @return 一个根据中心点旋转的动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static RotateAnimation getRotateAnimationByCenter() { + return getRotateAnimationByCenter(DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, long durationMillis, Animation.AnimationListener animationListener) { + AlphaAnimation alphaAnimation = new AlphaAnimation(fromAlpha, toAlpha); + alphaAnimation.setDuration(durationMillis); + if (animationListener != null) { + alphaAnimation.setAnimationListener(animationListener); + } + return alphaAnimation; + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @param durationMillis 持续时间 + * @return 一个透明度渐变动画 + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, long durationMillis) { + return getAlphaAnimation(fromAlpha, toAlpha, durationMillis, null); + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @param animationListener 动画监听器 + * @return 一个透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha, Animation.AnimationListener animationListener) { + return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个透明度渐变动画 + * + * @param fromAlpha 开始时的透明度 + * @param toAlpha 结束时的透明度都 + * @return 一个透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getAlphaAnimation(float fromAlpha, float toAlpha) { + return getAlphaAnimation(fromAlpha, toAlpha, DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(long durationMillis, Animation.AnimationListener animationListener) { + return getAlphaAnimation(1.0f, 0.0f, durationMillis, animationListener); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @return 一个由完全显示变为不可见的透明度渐变动画 + */ + public static AlphaAnimation getHiddenAlphaAnimation(long durationMillis) { + return getHiddenAlphaAnimation(durationMillis, null); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @param animationListener 动画监听器 + * @return 一个由完全显示变为不可见的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getHiddenAlphaAnimation(Animation.AnimationListener animationListener) { + return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个由完全显示变为不可见的透明度渐变动画 + * + * @return 一个由完全显示变为不可见的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getHiddenAlphaAnimation() { + return getHiddenAlphaAnimation(DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @param animationListener 动画监听器 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(long durationMillis, Animation.AnimationListener animationListener) { + return getAlphaAnimation(0.0f, 1.0f, durationMillis, animationListener); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @param durationMillis 持续时间 + * @return 一个由不可见变为完全显示的透明度渐变动画 + */ + public static AlphaAnimation getShowAlphaAnimation(long durationMillis) { + return getAlphaAnimation(0.0f, 1.0f, durationMillis, null); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @param animationListener 动画监听器 + * @return 一个由不可见变为完全显示的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getShowAlphaAnimation(Animation.AnimationListener animationListener) { + return getAlphaAnimation(0.0f, 1.0f, DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个由不可见变为完全显示的透明度渐变动画 + * + * @return 一个由不可见变为完全显示的透明度渐变动画,默认持续时间为DEFAULT_ANIMATION_DURATION + */ + public static AlphaAnimation getShowAlphaAnimation() { + return getAlphaAnimation(0.0f, 1.0f, DEFAULT_ANIMATION_DURATION, null); + } + + + /** + * 获取一个缩小动画 + * + * @param durationMillis 时间 + * @param animationListener 监听 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(long durationMillis, Animation.AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 0.0f, 1.0f, + 0.0f, ScaleAnimation.RELATIVE_TO_SELF, + ScaleAnimation.RELATIVE_TO_SELF); + scaleAnimation.setDuration(durationMillis); + scaleAnimation.setAnimationListener(animationListener); + return scaleAnimation; + } + + + /** + * 获取一个缩小动画 + * + * @param durationMillis 时间 + * @return 一个缩小动画 + */ + public static ScaleAnimation getLessenScaleAnimation(long durationMillis) { + return getLessenScaleAnimation(durationMillis, null); + } + + + /** + * 获取一个缩小动画 + * + * @param animationListener 监听 + * @return 返回一个缩小的动画 + */ + public static ScaleAnimation getLessenScaleAnimation(Animation.AnimationListener animationListener) { + return getLessenScaleAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } + + + /** + * 获取一个放大动画 + * + * @param durationMillis 时间 + * @param animationListener 监听 + * @return 返回一个放大的效果 + */ + public static ScaleAnimation getAmplificationAnimation(long durationMillis, Animation.AnimationListener animationListener) { + ScaleAnimation scaleAnimation = new ScaleAnimation(0.0f, 1.0f, 0.0f, + 1.0f, ScaleAnimation.RELATIVE_TO_SELF, + ScaleAnimation.RELATIVE_TO_SELF); + scaleAnimation.setDuration(durationMillis); + scaleAnimation.setAnimationListener(animationListener); + return scaleAnimation; + } + + + /** + * 获取一个放大动画 + * + * @param durationMillis 时间 + * @return 返回一个放大的效果 + */ + public static ScaleAnimation getAmplificationAnimation(long durationMillis) { + return getAmplificationAnimation(durationMillis, null); + } + + + /** + * 获取一个放大动画 + * + * @param animationListener 监听 + * @return 返回一个放大的效果 + */ + public static ScaleAnimation getAmplificationAnimation(Animation.AnimationListener animationListener) { + return getAmplificationAnimation(DEFAULT_ANIMATION_DURATION, animationListener); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java b/utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java new file mode 100644 index 0000000..3608900 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AppStatusManager.java @@ -0,0 +1,48 @@ +package com.lennon.cn.utill.utill; + +/** + * Created by lennon on 2018/1/23. + */ + +public class AppStatusManager { + private static AppStatusManager mInstance = null; + + private int appStatus = AppStatusConstant.APP_FORCE_KILLED; + + private AppStatusManager() { + + } + + public static AppStatusManager getInstance() { + if(mInstance==null) { + synchronized (AppStatusManager.class) { + if(mInstance==null) { + mInstance = new AppStatusManager(); + } + } + } + return mInstance; + } + + public void setAppStatus(int appStatus) { + this.appStatus = appStatus; + } + + public int getAppStatus() { + return appStatus; + } + + + public static class AppStatusConstant { + + /** + * App被回收,初始状态 + */ + public static final int APP_FORCE_KILLED = 0; + + /** + * 正常运行 + */ + public static final int APP_NORMAL = 1; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java new file mode 100644 index 0000000..4eb22ef --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AudioPlayerUtil.java @@ -0,0 +1,86 @@ +package com.lennon.cn.utill.utill; + +import android.content.Context; +import android.media.MediaMetadataRetriever; +import android.media.MediaPlayer; +import android.net.Uri; +import cn.droidlover.xdroidmvp.log.XLog; + +import java.io.IOException; +import java.util.HashMap; + +/** + * @author lennon + *

+ * 作者:lennon on 2019/2/18 16:30 + *

+ * 邮箱:1136160757@qq.com + */ +public class AudioPlayerUtil { + private static final String TAG = "AudioRecordTest"; + private MediaPlayer mPlayer; + + public AudioPlayerUtil() { + } + + public MediaPlayer start(String mFileName, MediaPlayer.OnCompletionListener listener) { + if (this.mPlayer == null) { + this.mPlayer = new MediaPlayer(); + } else { + this.mPlayer.reset(); + } + + try { + this.mPlayer.setDataSource(mFileName); + this.mPlayer.prepareAsync(); + this.mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mediaPlayer) { + mediaPlayer.start(); + } + }); + if (listener != null) { + this.mPlayer.setOnCompletionListener(listener); + } + } catch (Exception var4) { + var4.printStackTrace(); + } + return mPlayer; + + } + + public MediaPlayer start(Context context, String mFileUrl, MediaPlayer.OnCompletionListener listener) { + if (this.mPlayer == null) { + this.mPlayer = new MediaPlayer(); + } else { + this.mPlayer.reset(); + } + try { + Uri uri = Uri.parse(mFileUrl); + this.mPlayer.setDataSource(context, uri); + this.mPlayer.prepareAsync(); + this.mPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mediaPlayer) { + mediaPlayer.start(); + } + }); + if (listener != null) { + this.mPlayer.setOnCompletionListener(listener); + } + } catch (Exception var4) { + var4.printStackTrace(); + } + return mPlayer; + + } + + public void stop() { + if (this.mPlayer != null) { + this.mPlayer.stop(); + this.mPlayer.release(); + this.mPlayer = null; + } + + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java new file mode 100644 index 0000000..ca5b8d1 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/AudioRecorderUtil.java @@ -0,0 +1,196 @@ +package com.lennon.cn.utill.utill; + +import android.media.MediaRecorder; +import android.os.Handler; + +import java.io.File; +import java.io.IOException; + +/** + * @author lennon + *

+ * 作者:lennon on 2019/2/18 16:29 + *

+ * 邮箱:1136160757@qq.com + */ +public class AudioRecorderUtil { + private final String TAG = AudioRecorderUtil.class.getName(); + public static final int MAX_LENGTH = 60000; + private String filePath; + private String folderPath; + private MediaRecorder mMediaRecorder; + private int maxLength; + private long startTime; + private long endTime; + private OnAudioStatusUpdateListener audioStatusUpdateListener; + private final Handler mHandler = new Handler(); + private Runnable mUpdateMicStatusTimer = new Runnable() { + @Override + public void run() { + AudioRecorderUtil.this.updateMicStatus(); + } + }; + private int BASE = 1; + private int SPACE = 100; + + public AudioRecorderUtil(String folderPath) { + File path = new File(folderPath); + if (!path.exists()) { + path.mkdirs(); + } + + this.folderPath = folderPath; + this.maxLength = '\uea60'; + } + + public String start() { + if (this.mMediaRecorder == null) { + this.mMediaRecorder = new MediaRecorder(); + } else { + try { + this.mMediaRecorder.stop(); + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + } catch (Exception var2) { + this.mMediaRecorder.reset(); + } + } + + this.mHandler.removeCallbacks(this.mUpdateMicStatusTimer); + + try { + this.mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT); + this.mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); + this.mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + this.filePath = this.folderPath + File.separator + System.currentTimeMillis() + ".mp3"; + this.mMediaRecorder.setOutputFile(this.filePath); + this.mMediaRecorder.setMaxDuration(this.maxLength); + this.mMediaRecorder.prepare(); + this.mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() { + @Override + public void onInfo(MediaRecorder mr, int what, int extra) { + if (what == 800) { + AudioRecorderUtil.this.stop(); + } + + } + }); + this.mMediaRecorder.start(); + this.startTime = System.currentTimeMillis(); + this.updateMicStatus(); + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onStart(); + } + } catch (IllegalStateException var3) { + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onError(var3); + } + + this.cancel(); + } catch (IOException var4) { + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onError(var4); + } + + this.cancel(); + } + return filePath; + } + + public long getSumTime() { + return this.startTime == 0L ? 0L : System.currentTimeMillis() - this.startTime; + } + + public long stop() { + if (this.mMediaRecorder == null) { + return 0L; + } else { + this.endTime = System.currentTimeMillis(); + + try { + this.mMediaRecorder.stop(); + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onStop(this.filePath); + } + } catch (RuntimeException var3) { + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + File file = new File(this.filePath); + if (file.exists()) { + file.delete(); + } + + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onError(var3); + } + } + + this.filePath = ""; + return this.endTime - this.startTime; + } + } + + public void cancel() { + try { + this.mMediaRecorder.stop(); + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + } catch (RuntimeException var2) { + this.mMediaRecorder.reset(); + this.mMediaRecorder.release(); + this.mMediaRecorder = null; + } + + File file = new File(this.filePath); + if (file.exists()) { + file.delete(); + } + + this.filePath = ""; + if (this.audioStatusUpdateListener != null) { + this.audioStatusUpdateListener.onCancel(); + } + + } + + public void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } + + private void updateMicStatus() { + if (this.mMediaRecorder != null) { + double ratio = (double) this.mMediaRecorder.getMaxAmplitude() / (double) this.BASE; + double db = 0.0D; + if (ratio > 1.0D) { + db = 20.0D * Math.log10(ratio); + if (null != this.audioStatusUpdateListener) { + this.audioStatusUpdateListener.onProgress(db, System.currentTimeMillis() - this.startTime); + } + } + + this.mHandler.postDelayed(this.mUpdateMicStatusTimer, (long) this.SPACE); + } + + } + + public void setOnAudioStatusUpdateListener(OnAudioStatusUpdateListener audioStatusUpdateListener) { + this.audioStatusUpdateListener = audioStatusUpdateListener; + } + + public interface OnAudioStatusUpdateListener { + void onStart(); + + void onProgress(double var1, long var3); + + void onError(Exception var1); + + void onCancel(); + + void onStop(String var1); + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt b/utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt new file mode 100644 index 0000000..7742c50 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/BitmapOperations.kt @@ -0,0 +1,109 @@ +package com.lennon.cn.utill.utill + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Matrix +import android.graphics.Paint + +object BitmapOperations { + + private val paint: Paint + get() { + val paint = Paint() + paint.isAntiAlias = true + return paint + } + + /** + * 按比例缩放图片 + * baseBitmap: 需要缩放的原图 + * float x:横向缩放比例 + * float y:纵向缩放比例 + */ + fun bitmapScale(baseBitmap: Bitmap, x: Float, y: Float): Bitmap { + // 因为要将图片放大,所以要根据放大的尺寸重新创建Bitmap + val afterBitmap = Bitmap.createBitmap((baseBitmap.width * x).toInt(), (baseBitmap.height * y).toInt(), baseBitmap.config) + val canvas = Canvas(afterBitmap) + // 初始化Matrix对象 + val matrix = Matrix() + // 根据传入的参数设置缩放比例 + matrix.setScale(x, y) + // 根据缩放比例,把图片draw到Canvas上 + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 按给定尺寸缩放图片 + * baseBitmap: 需要缩放的原图 + * new_width:缩放后的宽度 + * new_height:缩放后的高度 + */ + fun bitmapScale(baseBitmap: Bitmap, new_width: Int, new_height: Int): Bitmap { + // 因为要将图片放大,所以要根据放大的尺寸重新创建Bitmap + val afterBitmap = Bitmap.createBitmap(new_width, new_height, baseBitmap.config) + val canvas = Canvas(afterBitmap) + // 初始化Matrix对象 + val matrix = Matrix() + // 根据传入的参数设置缩放比例 + val sx = new_width.toFloat() / baseBitmap.width.toFloat() + val sy = new_height.toFloat() / baseBitmap.height.toFloat() + matrix.setScale(sx, sy) + // 根据缩放比例,把图片draw到Canvas上 + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 倾斜图片 + */ + fun bitmapSkew(baseBitmap: Bitmap, dx: Float, dy: Float): Bitmap { + // 根据图片的倾斜比例,计算变换后图片的大小, + val afterBitmap = Bitmap.createBitmap(baseBitmap.width + (baseBitmap.width * dx).toInt(), baseBitmap.height + (baseBitmap.height * dy).toInt(), baseBitmap.config) + val canvas = Canvas(afterBitmap) + val matrix = Matrix() + // 设置图片倾斜的比例 + matrix.setSkew(dx, dy) + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 图片移动 + */ + fun bitmapTranslate(baseBitmap: Bitmap, dx: Float, dy: Float): Bitmap { + // 需要根据移动的距离来创建图片的拷贝图大小 + val afterBitmap = Bitmap.createBitmap((baseBitmap.width + dx).toInt(), (baseBitmap.height + dy).toInt(), baseBitmap.config) + val canvas = Canvas(afterBitmap) + val matrix = Matrix() + // 设置移动的距离 + matrix.setTranslate(dx, dy) + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 图片绕中心旋转 + */ + fun bitmapRotate(baseBitmap: Bitmap, degrees: Float): Bitmap { + // 创建一个和原图一样大小的图片 + val afterBitmap = Bitmap.createBitmap(baseBitmap.width, baseBitmap.height, baseBitmap.config) + val canvas = Canvas(afterBitmap) + val matrix = Matrix() + // 根据原图的中心位置旋转 + matrix.setRotate(degrees, baseBitmap.width.toFloat() / 2.0f, baseBitmap.height.toFloat() / 2.0f) + canvas.drawBitmap(baseBitmap, matrix, paint) + return afterBitmap + } + + /** + * 图片绕中心旋转 + */ + fun rotaingImage(bitmap: Bitmap, angle: Int): Bitmap { + //旋转图片 动作 + val matrix = Matrix() + matrix.postRotate(angle.toFloat()) + // 创建新的图片 + return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java new file mode 100644 index 0000000..5b60a3e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/ByteUtil.java @@ -0,0 +1,68 @@ +package com.lennon.cn.utill.utill; + +public class ByteUtil { + /** + * 将字节数组转换为十六进制字符串 + * + * @param byteArray byte[] + * @return String + */ + public static String byteToHexStr(byte[] byteArray) { + StringBuilder strDigest = new StringBuilder(); + for (byte aByteArray : byteArray) { + strDigest.append(byteToHexStr(aByteArray)); + } + return strDigest.toString(); + } + + /** + * 将字节转换为十六进制字符串 + * + * @param mByte byte + * @return String + */ + public static char[] byteToHexStr(byte mByte) { + char[] digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + char[] tempArr = new char[2]; + tempArr[0] = digit[(mByte >>> 4) & 0X0F]; + tempArr[1] = digit[mByte & 0X0F]; + return tempArr; + } + + /** + * hex字符串转byte数组 + * + * @param inHex 待转换的Hex字符串 + * @return 转换后的byte数组结果 + */ + public static byte[] hexToByteArray(String inHex) { + int hexlen = inHex.length(); + byte[] result; + if (hexlen % 2 == 1) { + // 奇数 + hexlen++; + result = new byte[(hexlen / 2)]; + inHex = "0" + inHex; + } else { + // 偶数 + result = new byte[(hexlen / 2)]; + } + int j = 0; + for (int i = 0; i < hexlen; i += 2) { + result[j] = hexToByte(inHex.substring(i, i + 2)); + j++; + } + return result; + } + + /** + * Hex字符串转byte + * + * @param inHex 待转换的Hex字符串 + * @return 转换后的byte + */ + public static byte hexToByte(String inHex) { + return (byte) Integer.parseInt(inHex, 16); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java b/utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java new file mode 100644 index 0000000..5d33a04 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/CountdownUtill.java @@ -0,0 +1,57 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.os.CountDownTimer; +import android.widget.TextView; +import lennon.com.utill.R; + +/** + * Created by lennon on 2017/9/24. + */ + +public class CountdownUtill extends CountDownTimer { + private Activity mActivity; + private TextView mBtn; + private boolean mIsEnable = false; + private Listener listener; + private String startString = "", endString = ""; + + public CountdownUtill(Activity activity, long millisInFuture, long countDownInterval, TextView button, Listener listener) { + super(millisInFuture, countDownInterval); + this.mActivity = activity; + this.mBtn = button; + this.mIsEnable = true; + this.listener = listener; + } + + @SuppressLint("SetTextI18n") + @Override + public void onFinish() { + mBtn.setText("0" + mActivity.getResources().getString(R.string.resend_sms)); + if (listener != null) { + listener.onFinish(); + } + } + + @SuppressLint("SetTextI18n") + @Override + public void onTick(long millisUntilFinished) { + if (mIsEnable) + mBtn.setEnabled(false); + mIsEnable = false; + mBtn.setText(startString + (millisUntilFinished / 1000) + mActivity.getResources().getString(R.string.resend_sms) + endString); + } + + public void setStartString(String startString) { + this.startString = startString; + } + + public void setEndString(String endString) { + this.endString = endString; + } + + public interface Listener { + void onFinish(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt new file mode 100644 index 0000000..ccb73bf --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/DensityUtils.kt @@ -0,0 +1,154 @@ +package com.lennon.cn.utill.utill + +import android.app.Activity +import android.app.Application +import android.content.ComponentCallbacks +import android.content.Context +import android.content.res.Configuration +import android.util.DisplayMetrics + +object DensityUtils { + private var appDensity: Float = 0.toFloat() + private var appScaledDensity: Float = 0.toFloat() + private var appDisplayMetrics: DisplayMetrics? = null + private var barHeight: Int = 0 +// val WIDTH = "width" +// val HEIGHT = "height" + + enum class Density { + WIDTH, HEIGHT; + } + + /** + * 在Application里初始化一下 + * @param application + */ + fun setDensity(application: Application) { + //获取application的DisplayMetrics + appDisplayMetrics = application.resources.displayMetrics + //获取状态栏高度 + barHeight = getStatusBarHeight(application) + + if (appDensity == 0f) { + //初始化的时候赋值 + appDensity = appDisplayMetrics!!.density + appScaledDensity = appDisplayMetrics!!.scaledDensity + + //添加字体变化的监听 + application.registerComponentCallbacks(object : ComponentCallbacks { + override fun onConfigurationChanged(newConfig: Configuration) { + //字体改变后,将appScaledDensity重新赋值 + if (newConfig != null && newConfig.fontScale > 0) { + appScaledDensity = application.resources.displayMetrics.scaledDensity + } + } + + override fun onLowMemory() {} + }) + } + } + /** + * 此方法在BaseActivity中做初始化(如果不封装BaseActivity的话,直接用下面那个方法就好) + * 在setContentView()之前设置 + * @param activity + */ + fun setDefault(activity: Activity) { + setAppOrientation(activity, Density.WIDTH) + } + + /** + * 此方法用于在某一个Activity里面更改适配的方向 + * 在setContentView()之前设置 + * @param activity + * @param orientation + */ + fun setOrientation(activity: Activity, orientation: Density) { + setAppOrientation(activity, orientation) + } + + /** + * 此方法用于在某一个Activity里面更改适配的方向 + * 在setContentView()之前设置 + * @param activity + * @param orientation + */ + fun setOrientation(activity: Activity, orientation: Density, f: Float) { + setAppOrientation(activity, orientation, f) + } + + /** + * targetDensity + * targetScaledDensity + * targetDensityDpi + * 这三个参数是统一修改过后的值 + * orientation:方向值,传入width或height + */ + private fun setAppOrientation(activity: Activity, orientation: Density, f: Float) { + + val targetDensity: Float + + if (orientation == Density.HEIGHT) { + targetDensity = (appDisplayMetrics!!.heightPixels - barHeight) / f//设计图的高度 单位:dp + } else { + targetDensity = appDisplayMetrics!!.widthPixels / f//设计图的宽度 单位:dp + } + + val targetScaledDensity = targetDensity * (appScaledDensity / appDensity) + val targetDensityDpi = (160 * targetDensity).toInt() + + /** + * + * 最后在这里将修改过后的值赋给系统参数 + * 只修改Activity的density值 + */ + val activityDisplayMetrics = activity.resources.displayMetrics + activityDisplayMetrics.density = targetDensity + activityDisplayMetrics.scaledDensity = targetScaledDensity + activityDisplayMetrics.densityDpi = targetDensityDpi + } + + + /** + * targetDensity + * targetScaledDensity + * targetDensityDpi + * 这三个参数是统一修改过后的值 + * orientation:方向值,传入width或height + */ + private fun setAppOrientation(activity: Activity, orientation: Density) { + val targetDensity: Float + if (orientation == Density.HEIGHT) { + targetDensity = (appDisplayMetrics!!.heightPixels - barHeight) / 1080f//设计图的高度 单位:dp + } else { + targetDensity = appDisplayMetrics!!.widthPixels / 420f//设计图的宽度 单位:dp + } + + val targetScaledDensity = targetDensity * (appScaledDensity / appDensity) + val targetDensityDpi = (160 * targetDensity).toInt() + + /** + * + * 最后在这里将修改过后的值赋给系统参数 + * 只修改Activity的density值 + */ + val activityDisplayMetrics = activity.resources.displayMetrics + activityDisplayMetrics.density = targetDensity + activityDisplayMetrics.scaledDensity = targetScaledDensity + activityDisplayMetrics.densityDpi = targetDensityDpi + } + + /** + * 获取状态栏高度 + * + * @param context + * @return + */ + fun getStatusBarHeight(context: Context): Int { + var result = 0 + val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) { + result = context.resources.getDimensionPixelSize(resourceId) + } + return result + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java new file mode 100644 index 0000000..9cebc61 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/FileUtil.java @@ -0,0 +1,859 @@ +package com.lennon.cn.utill.utill; + +import android.app.Application; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.os.Environment; +import android.os.StatFs; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +/** + * 文件操作工具包 + * + * @author liux (http://my.oschina.net/liux) + * @version 1.0 + * @created 2012-3-21 + */ +public class FileUtil { + public static AssetFileDescriptor getAssetFileDescription(Application application, String filename) throws IOException { + AssetManager manager = application.getAssets(); + return manager.openFd(filename); + } + + /** + * 根据文件绝对路径获取文件名 + * + * @param filePath + * @return + */ + public static String getFileName(String filePath) { + if (TextUtils.isEmpty(filePath)) { + return ""; + } else { + return filePath.substring(filePath.lastIndexOf(File.separator) + 1); + } + } + + //创建一个临时目录,用于复制临时文件,如assets目录下的离线资源文件 + public static String createTmpDir(Context context) { + String sampleDir = "baiduTTS"; + String tmpDir = Environment.getExternalStorageDirectory().toString() + "/" + sampleDir; + if (!FileUtil.makeDir(tmpDir)) { + tmpDir = context.getExternalFilesDir(sampleDir).getAbsolutePath(); + if (!FileUtil.makeDir(sampleDir)) { + throw new RuntimeException("create model resources dir failed :" + tmpDir); + } + } + return tmpDir; + } + + public static boolean makeDir(String dirPath) { + File file = new File(dirPath); + if (!file.exists()) { + return file.mkdirs(); + } else { + return true; + } + } + + public static void copyFromAssets(AssetManager assets, String source, String dest, boolean isCover) throws IOException { + File file = new File(dest); + if (isCover || (!isCover && !file.exists())) { + InputStream is = null; + FileOutputStream fos = null; + try { + is = assets.open(source); + String path = dest; + fos = new FileOutputStream(path); + byte[] buffer = new byte[1024]; + int size = 0; + while ((size = is.read(buffer, 0, 1024)) >= 0) { + fos.write(buffer, 0, size); + } + } finally { + if (fos != null) { + try { + fos.close(); + } finally { + if (is != null) { + is.close(); + } + } + } + } + } + } + + /** + * 写文本文件 在Android系统中,文件保存在 /data/data/PACKAGE_NAME/files 目录下 + * + * @param context + */ + public static void write(Context context, String fileName, String content) { + if (content == null) { + content = ""; + } + + try { + FileOutputStream fos = context.openFileOutput(fileName, + Context.MODE_PRIVATE); + fos.write(content.getBytes()); + + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 读取文本文件 + * + * @param context + * @param fileName + * @return + */ + public static String read(Context context, String fileName) { + try { + FileInputStream in = context.openFileInput(fileName); + return readInStream(in); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + public static String readInStream(InputStream inStream) { + try { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[512]; + int length = -1; + while ((length = inStream.read(buffer)) != -1) { + outStream.write(buffer, 0, length); + } + + outStream.close(); + inStream.close(); + return outStream.toString(); + } catch (IOException e) { + XLog.e("FileTest " + e.getMessage()); + } + return null; + } + + public static File createFile(String folderPath, String fileName) { + File destDir = new File(folderPath); + if (!destDir.exists()) { + destDir.mkdirs(); + } + return new File(folderPath, fileName + fileName); + } + + /** + * 向手机写图片 + * + * @param buffer + * @param folder + * @param fileName + * @return + */ + public static boolean writeFile(byte[] buffer, String folder, + String fileName) { + boolean writeSucc = false; + + boolean sdCardExist = Environment.getExternalStorageState().equals( + Environment.MEDIA_MOUNTED); + + String folderPath = ""; + if (sdCardExist) { + folderPath = Environment.getExternalStorageDirectory() + + File.separator + folder + File.separator; + } else { + writeSucc = false; + } + + File fileDir = new File(folderPath); + if (!fileDir.exists()) { + fileDir.mkdirs(); + } + + File file = new File(folderPath + fileName); + FileOutputStream out = null; + try { + out = new FileOutputStream(file); + out.write(buffer); + writeSucc = true; + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + return writeSucc; + } + + /** + * 根据文件的绝对路径获取文件名但不包含扩展名 + * + * @param filePath + * @return + */ + public static String getFileNameNoFormat(String filePath) { + if (StringUtils.isEmpty(filePath)) { + return ""; + } + int point = filePath.lastIndexOf('.'); + return filePath.substring(filePath.lastIndexOf(File.separator) + 1, + point); + } + + /** + * 获取文件扩展名 + * + * @param fileName + * @return + */ + public static String getFileFormat(String fileName) { + if (StringUtils.isEmpty(fileName)) { + return ""; + } + + int point = fileName.lastIndexOf('.'); + return fileName.substring(point + 1); + } + + /** + * 获取文件大小 + * + * @param filePath + * @return + */ + public static long getFileSize(String filePath) { + long size = 0; + + File file = new File(filePath); + if (file != null && file.exists()) { + size = file.length(); + } + return size; + } + + /** + * 获取文件大小 + * + * @param size 字节 + * @return + */ + public static String getFileSize(long size) { + if (size <= 0) { + return "0"; + } + java.text.DecimalFormat df = new java.text.DecimalFormat("##.##"); + float temp = (float) size / 1024; + if (temp >= 1024) { + return df.format(temp / 1024) + "M"; + } else { + return df.format(temp) + "K"; + } + } + + /** + * 转换文件大小 + * + * @param fileS + * @return B/KB/MB/GB + */ + public static String formatFileSize(long fileS) { + java.text.DecimalFormat df = new java.text.DecimalFormat("#.00"); + String fileSizeString = ""; + if (fileS < 1024) { + fileSizeString = df.format((double) fileS) + "B"; + } else if (fileS < 1048576) { + fileSizeString = df.format((double) fileS / 1024) + "KB"; + } else if (fileS < 1073741824) { + fileSizeString = df.format((double) fileS / 1048576) + "MB"; + } else { + fileSizeString = df.format((double) fileS / 1073741824) + "G"; + } + return fileSizeString; + } + + /** + * 获取目录文件大小 + * + * @param dir + * @return + */ + public static long getDirSize(File dir) { + if (dir == null) { + return 0; + } + if (!dir.isDirectory()) { + return 0; + } + long dirSize = 0; + File[] files = dir.listFiles(); + if (files != null) { + + for (File file : files) { + if (file.isFile()) { + dirSize += file.length(); + } else if (file.isDirectory()) { + dirSize += file.length(); + dirSize += getDirSize(file); // 递归调用继续统计 + } + } + } + return dirSize; + } + + public static AssetFileDescriptor getAssetFileDescription(String path) throws IOException { + return BaseApplication.Companion.getAppliction().getAssets().openFd(path); + } + + /** + * 获取目录文件个数 + * + * @return + */ + public long getFileList(File dir) { + long count = 0; + File[] files = dir.listFiles(); + count = files.length; + for (File file : files) { + if (file.isDirectory()) { + count = count + getFileList(file);// 递归 + count--; + } + } + return count; + } + + public static byte[] toBytes(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int ch; + while ((ch = in.read()) != -1) { + out.write(ch); + } + byte buffer[] = out.toByteArray(); + out.close(); + return buffer; + } + + /** + * 检查文件是否存在 + * + * @param name + * @return + */ + public static boolean checkFileExists(String name) { + boolean status; + if (!"".equals(name)) { + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + name); + status = newPath.exists(); + } else { + status = false; + } + return status; + } + + /** + * 检查路径是否存在 + * + * @param path + * @return + */ + public static boolean checkFilePathExists(String path) { + return new File(path).exists(); + } + + /** + * 计算SD卡的剩余空间 + * + * @return 返回-1,说明没有安装sd卡 + */ + public static long getFreeDiskSpace() { + String status = Environment.getExternalStorageState(); + long freeSpace = 0; + if (status.equals(Environment.MEDIA_MOUNTED)) { + try { + File path = Environment.getExternalStorageDirectory(); + StatFs stat = new StatFs(path.getPath()); + long blockSize = stat.getBlockSize(); + long availableBlocks = stat.getAvailableBlocks(); + freeSpace = availableBlocks * blockSize / 1024; + } catch (Exception e) { + e.printStackTrace(); + } + } else { + return -1; + } + return (freeSpace); + } + + /** + * 新建目录 + * + * @param directoryName + * @return + */ + public static boolean createDirectory(String directoryName) { + boolean status; + if (!"".equals(directoryName)) { + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + directoryName); + status = newPath.mkdir(); + status = true; + } else { + status = false; + } + return status; + } + + /** + * 检查是否安装SD卡 + * + * @return + */ + public static boolean checkSaveLocationExists() { + String sDCardStatus = Environment.getExternalStorageState(); + boolean status; + if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) { + status = true; + } else { + status = false; + } + return status; + } + + /** + * 检查是否安装外置的SD卡 + * + * @return + */ + public static boolean checkExternalSDExists() { + + Map evn = System.getenv(); + return evn.containsKey("SECONDARY_STORAGE"); + } + + /** + * 复制单个文件 + * + * @param oldPath String 原文件路径 如:c:/fqf.txt + * @param newPath String 复制后路径 如:f:/fqf.txt + * @return boolean + */ + public void copyFile(String oldPath, String newPath) { + try { + int bytesum = 0; + int byteread = 0; + File oldfile = new File(oldPath); + if (oldfile.exists()) { //文件存在时 + InputStream inStream = new FileInputStream(oldPath); //读入原文件 + FileOutputStream fs = new FileOutputStream(newPath); + byte[] buffer = new byte[1444]; + int length; + while ((byteread = inStream.read(buffer)) != -1) { + bytesum += byteread; //字节数 文件大小 + System.out.println(bytesum); + fs.write(buffer, 0, byteread); + } + inStream.close(); + } + } catch (Exception e) { + System.out.println("复制单个文件操作出错"); + e.printStackTrace(); + + } + } + + public static void copyFileforAsses(String assetName, String newPath) { + try { + int bytesum = 0; + int byteread = 0; + InputStream inStream = BaseApplication.Companion.context().getAssets().open(assetName); + FileOutputStream fs = new FileOutputStream(newPath); + byte[] buffer = new byte[1444]; + int length; + while ((byteread = inStream.read(buffer)) != -1) { + bytesum += byteread; //字节数 文件大小 + System.out.println(bytesum); + fs.write(buffer, 0, byteread); + } + inStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 删除目录(包括:目录里的所有文件) + * + * @param fileName + * @return + */ + public static boolean deleteDirectory(String fileName) { + boolean status; + SecurityManager checker = new SecurityManager(); + + if (!"".equals(fileName)) { + + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + fileName); + checker.checkDelete(newPath.toString()); + if (newPath.isDirectory()) { + String[] listfile = newPath.list(); + try { + for (int i = 0; i < listfile.length; i++) { + File deletedFile = new File(newPath.toString() + "/" + + listfile[i].toString()); + deletedFile.delete(); + } + newPath.delete(); + status = true; + } catch (Exception e) { + e.printStackTrace(); + status = false; + } + + } else { + status = false; + } + } else { + status = false; + } + return status; + } + + /** + * 删除文件 + * + * @param fileName + * @return + */ + public static boolean deleteFile(String fileName) { + boolean status; + SecurityManager checker = new SecurityManager(); + + if (!"".equals(fileName)) { + + File path = Environment.getExternalStorageDirectory(); + File newPath = new File(path.toString() + fileName); + checker.checkDelete(newPath.toString()); + if (newPath.isFile()) { + try { + newPath.delete(); + status = true; + } catch (SecurityException se) { + se.printStackTrace(); + status = false; + } + } else { + status = false; + } + } else { + status = false; + } + return status; + } + + /** + * 删除空目录 + *

+ * 返回 0代表成功 ,1 代表没有删除权限, 2代表不是空目录,3 代表未知错误 + * + * @return + */ + public static int deleteBlankPath(String path) { + File f = new File(path); + if (!f.canWrite()) { + return 1; + } + if (f.list() != null && f.list().length > 0) { + return 2; + } + if (f.delete()) { + return 0; + } + return 3; + } + + /** + * 重命名 + * + * @param oldName + * @param newName + * @return + */ + public static boolean reNamePath(String oldName, String newName) { + File f = new File(oldName); + return f.renameTo(new File(newName)); + } + + /** + * 删除文件 + * + * @param filePath + */ + public static boolean deleteFileWithPath(String filePath) { + SecurityManager checker = new SecurityManager(); + File f = new File(filePath); + checker.checkDelete(filePath); + if (f.isFile()) { + f.delete(); + return true; + } + return false; + } + + /** + * 清空一个文件夹 + */ + public static void clearFileWithPath(String filePath) { + List files = FileUtil.listPathFiles(filePath); + if (files.isEmpty()) { + return; + } + for (File f : files) { + if (f.isDirectory()) { + clearFileWithPath(f.getAbsolutePath()); + } else { + f.delete(); + } + } + } + + /** + * 获取SD卡的根目录 + * + * @return + */ + public static String getSDRoot() { + + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + /** + * 获取手机外置SD卡的根目录 + * + * @return + */ + public static String getExternalSDRoot() { + + Map evn = System.getenv(); + + return evn.get("SECONDARY_STORAGE"); + } + + /** + * 列出root目录下所有子目录 + * + * @return 绝对路径 + */ + public static List listPath(String root) { + List allDir = new ArrayList(); + SecurityManager checker = new SecurityManager(); + File path = new File(root); + checker.checkRead(root); + // 过滤掉以.开始的文件夹 + if (path.isDirectory()) { + for (File f : path.listFiles()) { + if (f.isDirectory() && !f.getName().startsWith(".")) { + allDir.add(f.getAbsolutePath()); + } + } + } + return allDir; + } + + /** + * 获取一个文件夹下的所有文件 + * + * @param root + * @return + */ + public static List listPathFiles(String root) { + List allDir = new ArrayList(); + SecurityManager checker = new SecurityManager(); + File path = new File(root); + checker.checkRead(root); + File[] files = path.listFiles(); + for (File f : files) { + if (f.isFile()) { + allDir.add(f); + } else { + listPath(f.getAbsolutePath()); + } + } + return allDir; + } + + public enum PathStatus { + SUCCESS, EXITS, ERROR + } + + /** + * 创建目录 + */ + public static PathStatus createPath(String newPath) { + File path = new File(newPath); + if (path.exists()) { + return PathStatus.EXITS; + } + if (path.mkdir()) { + return PathStatus.SUCCESS; + } else { + return PathStatus.ERROR; + } + } + + /** + * 截取路径名 + * + * @return + */ + public static String getPathName(String absolutePath) { + int start = absolutePath.lastIndexOf(File.separator) + 1; + int end = absolutePath.length(); + return absolutePath.substring(start, end); + } + + /** + * 获取应用程序缓存文件夹下的指定目录 + * + * @param context + * @param dir + * @return + */ + public static String getAppCache(Context context, String dir) { + String savePath = context.getCacheDir().getAbsolutePath() + "/" + dir + "/"; + File savedir = new File(savePath); + if (!savedir.exists()) { + savedir.mkdirs(); + } + savedir = null; + return savePath; + } + + /** + * 关闭流 + * + * @param closeables + */ + public static void closeIO(Closeable... closeables) { + if (null == closeables || closeables.length <= 0) { + return; + } + for (Closeable cb : closeables) { + try { + if (null == cb) { + continue; + } + cb.close(); + } catch (IOException e) { + throw new RuntimeException( + FileUtil.class.getClass().getName(), e); + } + } + } + + /** + * 图片写入文件 + * + * @param bitmap 图片 + * @param filePath 文件路径 + * @return 是否写入成功 + */ + public static boolean bitmapToFile(Bitmap bitmap, int quality, Bitmap.CompressFormat format, String filePath) { + boolean isSuccess = false; + if (bitmap == null) { + return isSuccess; + } + File file = new File(filePath.substring(0, + filePath.lastIndexOf(File.separator))); + if (!file.exists()) { + file.mkdirs(); + } + + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(filePath), + 8 * 1024); + isSuccess = bitmap.compress(format, quality, out); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } finally { + closeIO(out); + } + return isSuccess; + } + + /** + * 获取SD卡下指定文件夹的绝对路径 + * + * @return 返回SD卡下的指定文件夹的绝对路径 + */ + public static String getSavePath(String folderName) { + return getSaveFolder(folderName).getAbsolutePath(); + } + + /** + * 获取文件夹对象 + * + * @return 返回SD卡下的指定文件夹对象,若文件夹不存在则创建 + */ + public static File getSaveFolder(String folderName) { + File file = new File(getSDCardPath() + File.separator + folderName + + File.separator); + file.mkdirs(); + return file; + } + + public static String getSDCardPath() { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + /** + * 写入文件 + * + * @param in + * @param file + */ + public static void writeFile(InputStream in, File file) throws IOException { + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + + if (file != null && file.exists()) { + file.delete(); + } + + FileOutputStream out = new FileOutputStream(file); + byte[] buffer = new byte[1024 * 128]; + int len = -1; + while ((len = in.read(buffer)) != -1) { + out.write(buffer, 0, len); + } + out.flush(); + out.close(); + in.close(); + + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java new file mode 100644 index 0000000..ab5e1f8 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/GetDeviceId.java @@ -0,0 +1,189 @@ +package com.lennon.cn.utill.utill; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +public class GetDeviceId { + + /** + * 获取设备唯一标识符 + * + * @param context + * @return + */ + public static String getDeviceId(Context context) { + //读取保存的在sd卡中的唯一标识符 + List deviceIds = readDeviceID(context); + if (deviceIds != null) { + XLog.e("readDeviceID-------------------------" + deviceIds.toString()); + } + if (deviceIds != null && deviceIds.size() > 0) { + if (deviceIds.size() == 1) { + String deviceId = deviceIds.get(0); + //判断是否已经生成过, + if (deviceId != null && !"".equals(deviceId)) { + if (deviceId.contains("-") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return VersionUtill.getUUID(); + } + return deviceId.replace("deviceId:", "").trim(); + } + } else { + if (!TextUtils.isEmpty(deviceIds.get(0))) { + String deviceId = deviceIds.get(0).replace("deviceId:", "").trim(); + //判断是否已经生成过, + if (!"".equals(deviceId)) { + return deviceId; + } + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return VersionUtill.getUUID(); + } else { + return VersionUtill.getAndroidId(context); + } + } + + /** + * 读取固定的文件中的内容,这里就是读取sd卡中保存的设备唯一标识符 + * + * @param context + * @return + */ + public static List readDeviceID(Context context) { + File newFile = getNewDevicesDir(context); + List strings = getFileContent(new File(newFile.getAbsolutePath() + "/", "devices.txt")); + if (strings.size() > 0) { + return strings; + } + return new ArrayList<>(); + } + + /** + * 保存 内容到 SD卡中, 这里保存的就是 设备唯一标识符 + * + * @param str + * @param context + */ + public static void saveDeviceID(String str, Context context) { + File newFile = getNewDevicesDir(context); + File f = new File(newFile.getPath(), newFile.getName() + ".txt"); + try { + if (f.exists()) { + f.delete(); + } + } catch (Exception e) { + e.printStackTrace(); + XLog.e(e.getMessage()); + } + + writeTxtToFile("deviceId:" + str, newFile.getAbsolutePath() + "/", "devices.txt"); + + writeTxtToFile("deviceId-version:2", newFile.getAbsolutePath() + "/", "devices.txt"); + + + } + + + // 将字符串写入到文本文件中 + private static void writeTxtToFile(String strcontent, String filePath, String fileName) { + //生成文件夹之后,再生成文件,不然会出错 + makeFilePath(filePath, fileName); + + String strFilePath = filePath + fileName; + // 每次写入时,都换行写 + String strContent = strcontent + "\r\n"; + try { + File file = new File(strFilePath); + if (!file.exists()) { + XLog.d("TestFile", "Create the file:" + strFilePath); + file.getParentFile().mkdirs(); + file.createNewFile(); + } + RandomAccessFile raf = new RandomAccessFile(file, "rwd"); + raf.seek(file.length()); + raf.write(strContent.getBytes()); + raf.close(); + } catch (Exception e) { + XLog.e("TestFile", "Error on write File:" + e); + } + } + +//生成文件 + + private static File makeFilePath(String filePath, String fileName) { + File file = null; + makeRootDirectory(filePath); + try { + file = new File(filePath + fileName); + if (!file.exists()) { + file.createNewFile(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return file; + } + +//生成文件夹 + + private static void makeRootDirectory(String filePath) { + File file = null; + try { + file = new File(filePath); + if (!file.exists()) { + file.mkdir(); + } + } catch (Exception e) { + XLog.e("error:", e + ""); + } + } + + //读取指定目录下的所有TXT文件的文件内容 + private static List getFileContent(File file) { + List content = new ArrayList<>(); + if (!file.isDirectory()) { //检查此路径名的文件是否是一个目录(文件夹) +// if (file.getName().endsWith("txt")) {//文件格式为""文件 + try { + InputStream instream = new FileInputStream(file); + if (instream != null) { + InputStreamReader inputreader + = new InputStreamReader(instream, "UTF-8"); + BufferedReader buffreader = new BufferedReader(inputreader); + String line = ""; + //分行读取 + while ((line = buffreader.readLine()) != null) { + String s = "" + line; + content.add(s); + } + instream.close();//关闭输入流 + } + } catch (java.io.FileNotFoundException e) { + XLog.e("TestFile", "The File doesn't not exist."); + } catch (IOException e) { + XLog.e("TestFile", e.getMessage()); + } +// } + } + return content; + } + + private static File getNewDevicesDir(Context context) { + File mCropFile = null; + mCropFile = new File(context.getFilesDir(), "devices"); + if (!mCropFile.getParentFile().exists()) { + mCropFile.getParentFile().mkdirs(); + } + XLog.e("getDevicesDir-----------------" + context.getFilesDir() + "-------------" + mCropFile.getParent() + "-----------------" + mCropFile.getName() + "-------------" + mCropFile.getPath()); + return mCropFile; + } + +} + diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java b/utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java new file mode 100644 index 0000000..ff89d4e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/IDCard.java @@ -0,0 +1,196 @@ +package com.lennon.cn.utill.utill; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Hashtable; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by lennon on 2017/6/2. + */ + +public class IDCard { + /*********************************** 身份证验证开始 ****************************************/ + /** + * 身份证号码验证 1、号码的结构 公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码, + * 八位数字出生日期码,三位数字顺序码和一位数字校验码。 2、地址码(前六位数) + * 表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。 3、出生日期码(第七位至十四位) + * 表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。 4、顺序码(第十五位至十七位) + * 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号, 顺序码的奇数分配给男性,偶数分配给女性。 5、校验码(第十八位数) + * (1)十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ,先对前17位数字的权求和 + * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 + * (2)计算模 Y = mod(S, 11) (3)通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0 X 9 8 7 6 5 4 3 2 + */ + + /** + * 功能:身份证的有效验证 + * + * @param IDStr 身份证号 + * @return 有效:返回"" 无效:返回String信息 + * @throws ParseException + */ + public static String IDCardValidate(String IDStr) { + String errorInfo = "";// 记录错误信息 + String[] ValCodeArr = {"1", "0", "x", "9", "8", "7", "6", "5", "4", + "3", "2"}; + String[] Wi = {"7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", + "9", "10", "5", "8", "4", "2"}; + String Ai = ""; + // ================ 号码的长度 15位或18位 ================ + if (IDStr.length() != 15 && IDStr.length() != 18) { + errorInfo = "身份证号码长度应该为15位或18位。"; + return errorInfo; + } + // =======================(end)======================== + + // ================ 数字 除最后以为都为数字 ================ + if (IDStr.length() == 18) { + Ai = IDStr.substring(0, 17); + } else { + Ai = IDStr.substring(0, 6) + "19" + IDStr.substring(6, 15); + } + if (!isNumeric(Ai)) { + errorInfo = "身份证15位号码都应为数字 ; 18位号码除最后一位外,都应为数字。"; + return errorInfo; + } + // =======================(end)======================== + + // ================ 出生年月是否有效 ================ + String strYear = Ai.substring(6, 10);// 年份 + String strMonth = Ai.substring(10, 12);// 月份 + String strDay = Ai.substring(12, 14);// 月份 + if (!isDate(strYear + "-" + strMonth + "-" + strDay)) { + errorInfo = "身份证生日无效。"; + return errorInfo; + } + GregorianCalendar gc = new GregorianCalendar(); + SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd"); + try { + if ((gc.get(Calendar.YEAR) - Integer.parseInt(strYear)) > 150 + || (gc.getTime().getTime() - s.parse( + strYear + "-" + strMonth + "-" + strDay).getTime()) < 0) { + errorInfo = "身份证生日不在有效范围。"; + return errorInfo; + } + } catch (NumberFormatException e) { + e.printStackTrace(); + return "识别出错"; + } catch (ParseException e) { + e.printStackTrace(); + return "识别出错"; + } + if (Integer.parseInt(strMonth) > 12 || Integer.parseInt(strMonth) == 0) { + errorInfo = "身份证月份无效"; + return errorInfo; + } + if (Integer.parseInt(strDay) > 31 || Integer.parseInt(strDay) == 0) { + errorInfo = "身份证日期无效"; + return errorInfo; + } + // =====================(end)===================== + + // ================ 地区码时候有效 ================ + Hashtable h = GetAreaCode(); + if (h.get(Ai.substring(0, 2)) == null) { + errorInfo = "身份证地区编码错误。"; + return errorInfo; + } + // ============================================== + + // ================ 判断最后一位的值 ================ + int TotalmulAiWi = 0; + for (int i = 0; i < 17; i++) { + TotalmulAiWi = TotalmulAiWi + + Integer.parseInt(String.valueOf(Ai.charAt(i))) + * Integer.parseInt(Wi[i]); + } + int modValue = TotalmulAiWi % 11; + String strVerifyCode = ValCodeArr[modValue]; + Ai = Ai + strVerifyCode; + + if (IDStr.length() == 18) { + if (!Ai.equals(IDStr)) { + errorInfo = "身份证无效,不是合法的身份证号码"; + return errorInfo; + } + } else { + return ""; + } + // =====================(end)===================== + return ""; + } + + /** + * 功能:设置地区编码 + * + * @return Hashtable 对象 + */ + @SuppressWarnings("unchecked") + private static Hashtable GetAreaCode() { + Hashtable hashtable = new Hashtable(); + hashtable.put("11", "北京"); + hashtable.put("12", "天津"); + hashtable.put("13", "河北"); + hashtable.put("14", "山西"); + hashtable.put("15", "内蒙古"); + hashtable.put("21", "辽宁"); + hashtable.put("22", "吉林"); + hashtable.put("23", "黑龙江"); + hashtable.put("31", "上海"); + hashtable.put("32", "江苏"); + hashtable.put("33", "浙江"); + hashtable.put("34", "安徽"); + hashtable.put("35", "福建"); + hashtable.put("36", "江西"); + hashtable.put("37", "山东"); + hashtable.put("41", "河南"); + hashtable.put("42", "湖北"); + hashtable.put("43", "湖南"); + hashtable.put("44", "广东"); + hashtable.put("45", "广西"); + hashtable.put("46", "海南"); + hashtable.put("50", "重庆"); + hashtable.put("51", "四川"); + hashtable.put("52", "贵州"); + hashtable.put("53", "云南"); + hashtable.put("54", "西藏"); + hashtable.put("61", "陕西"); + hashtable.put("62", "甘肃"); + hashtable.put("63", "青海"); + hashtable.put("64", "宁夏"); + hashtable.put("65", "新疆"); + hashtable.put("71", "台湾"); + hashtable.put("81", "香港"); + hashtable.put("82", "澳门"); + hashtable.put("91", "国外"); + return hashtable; + } + + /** + * 功能:判断字符串是否为数字 + * + * @param str + * @return + */ + private static boolean isNumeric(String str) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern pattern = Pattern.compile("[0-9]*"); + Matcher isNum = pattern.matcher(str); + return isNum.matches(); + } + + /** + * 功能:判断字符串是否为日期格式 + * + * @param + * @return + */ + public static boolean isDate(String strDate) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern pattern = Pattern + .compile("^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\\s(((0?[0-9])|([1-2][0-3]))\\:([0-5]?[0-9])((\\s)|(\\:([0-5]?[0-9])))))?$"); + Matcher m = pattern.matcher(strDate); + return m.matches(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java new file mode 100644 index 0000000..599883e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/JsonUtils.java @@ -0,0 +1,136 @@ +package com.lennon.cn.utill.utill; + + +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; +import com.google.gson.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class JsonUtils { + private static Gson mGson; + + static { + if (mGson == null) { + mGson = new Gson(); + } + } + + public static String toJson(Object object) { + return mGson.toJson(object); + } + + @SuppressWarnings("unchecked") + public static List> str2ListMap(String jsonArrStr) { + System.out.println(jsonArrStr); + List> jsonObjList = new ArrayList>(); + List jsonList = jsonToList(jsonArrStr); + for (Object object : jsonList) { + String jsonStr = mGson.toJson(object); + Map json = jsonToMap(jsonStr); + System.out.println(json.toString()); + jsonObjList.add((Map) json); + } + return jsonObjList; + } + + /** + * 将传入的json字符串解析为List集合 + * + * @param jsonStr + * @return + */ + public static List jsonToList(String jsonStr) { + List ObjectList = null; + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>() { + }.getType(); + ObjectList = mGson.fromJson(jsonStr, type); + return ObjectList; + } + + /** + * 将传入的json字符串解析为Map集合 + * + * @param jsonStr + * @return + */ + public static Map jsonToMap(String jsonStr) { + Map ObjectMap = null; + java.lang.reflect.Type type = new com.google.gson.reflect.TypeToken>() { + }.getType(); + ObjectMap = mGson.fromJson(jsonStr, type); + return ObjectMap; + } + + /** + * map转json字符串 + * + * @param m + * @return + */ + public static String map2json(Map m) { + mGson.toJson(m).toString(); + return mGson.toJson(m).toString(); + } + + /** + * JsonObject 转换为对象 + */ + public static Object jsonToObject(final Class cls, final JsonDeserializer deserializer, final JsonObject json) { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(cls, deserializer); + Gson gson = builder.create(); + gson.fromJson(json, cls); + return gson.fromJson(json, cls); + } + + public static T jsonToObject(String json, Class tClass) { + if (TextUtils.isEmpty(json)) { + return null; + } + T t; + try { + Gson gson = new Gson(); + t = gson.fromJson(json, tClass); + } catch (Exception e) { + XLog.e(e.getMessage()); + return null; + } + return t; + } + + /** + * JsonObject 转换为对象数组 + */ + public static List jsonToList(final Class cls, final JsonDeserializer deserializer, final JsonArray json) { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(cls, deserializer); + Gson gson = builder.create(); + List items = new ArrayList<>(); + for (JsonElement element : json) { + items.add(gson.fromJson(element, cls)); + } + return items; + } + + /** + * string to json array + * + * @param datas + * @param + * @return + */ + public static JsonArray listToJson(List datas) { + JsonParser parser = new JsonParser(); + Gson gson = new Gson(); + String str = gson.toJson(datas); + JsonArray aJson = parser.parse(str).getAsJsonArray(); + return aJson; + } + + public static String listToJsonString(List list) { + return new Gson().toJson(list); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt b/utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt new file mode 100644 index 0000000..3ebbe18 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/NetUtil.kt @@ -0,0 +1,43 @@ +package com.lennon.cn.utill.utill + +import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkInfo + +/** + * Created by lennon on 2017/12/25. + */ + +object NetUtil { + /** + * 没有连接网络 + */ + val NETWORK_NONE = -1 + /** + * 移动网络 + */ + val NETWORK_MOBILE = 0 + /** + * 无线网络 + */ + val NETWORK_WIFI = 1 + + fun getNetWorkState(context: Context): Int { + // 得到连接管理器对象 + val connectivityManager = context + .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + val activeNetworkInfo = connectivityManager.activeNetworkInfo + if (activeNetworkInfo != null && activeNetworkInfo.isConnected) { + + if (activeNetworkInfo.type == ConnectivityManager.TYPE_WIFI) { + return NETWORK_WIFI + } else if (activeNetworkInfo.type == ConnectivityManager.TYPE_MOBILE) { + return NETWORK_MOBILE + } + } else { + return NETWORK_NONE + } + return NETWORK_NONE + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java new file mode 100644 index 0000000..3ec862c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/OSUtils.java @@ -0,0 +1,352 @@ +package com.lennon.cn.utill.utill; + +import android.os.Environment; +import android.text.TextUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OSUtils { + private OSUtils() { + } + + /** + * ROM 类型 + */ + private static final ROM ROM_TYPE = initRomType(); + + private static final String KEY_DISPLAY_ID = "ro.build.display.id"; + private static final String KEY_BASE_OS_VERSION = "ro.build.version.base_os"; + private static final String KEY_CLIENT_ID_BASE = "ro.com.google.clientidbase"; + + // 小米 : MIUI + private static final String KEY_MIUI_VERSION = "ro.build.version.incremental"; // "7.6.15" + private static final String KEY_MIUI_VERSION_NANE = "ro.miui.ui.version.name"; // "V8" + private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code"; // "6" + + private static final String VALUE_MIUI_CLIENT_ID_BASE = "android-xiaomi"; + // 华为 : EMUI + private static final String KEY_EMUI_VERSION = "ro.build.version.emui"; // "EmotionUI_3.0" + private static final String KEY_EMUI_API_LEVEL = "ro.build.hw_emui_api_level"; // + private static final String KEY_EMUI_SYSTEM_VERSION = "ro.confg.hw_systemversion"; // "T1-A21wV100R001C233B008_SYSIMG" + // 魅族 : Flyme + private static final String KEY_FLYME_PUBLISHED = "ro.flyme.published"; // "true" + private static final String KEY_FLYME_SETUP = "ro.meizu.setupwizard.flyme"; // "true" + + private static final String VALUE_FLYME_DISPLAY_ID_CONTAIN = "Flyme"; // "Flyme OS 4.5.4.2U" + // OPPO : ColorOS + private static final String KEY_COLOROS_VERSION = "ro.oppo.theme.version"; // "703" + private static final String KEY_COLOROS_THEME_VERSION = "ro.oppo.version"; // "" + private static final String KEY_COLOROS_ROM_VERSION = "ro.rom.different.version"; // "ColorOS2.1" + + private static final String VALUE_COLOROS_BASE_OS_VERSION_CONTAIN = "OPPO"; // "OPPO/R7sm/R7sm:5.1.1/LMY47V/1440928800:user/release-keys" + private static final String VALUE_COLOROS_CLIENT_ID_BASE = "android-oppo"; + // vivo : FuntouchOS + private static final String KEY_FUNTOUCHOS_BOARD_VERSION = "ro.vivo.board.version"; // "MD" + private static final String KEY_FUNTOUCHOS_OS_NAME = "ro.vivo.os.name"; // "Funtouch" + private static final String KEY_FUNTOUCHOS_OS_VERSION = "ro.vivo.os.version"; // "3.0" + private static final String KEY_FUNTOUCHOS_DISPLAY_ID = "ro.vivo.os.build.display.id"; // "FuntouchOS_3.0" + private static final String KEY_FUNTOUCHOS_ROM_VERSION = "ro.vivo.rom.version"; // "rom_3.1" + + private static final String VALUE_FUNTOUCHOS_CLIENT_ID_BASE = "android-vivo"; + // Samsung + private static final String VALUE_SAMSUNG_BASE_OS_VERSION_CONTAIN = "samsung"; // "samsung/zeroltezc/zeroltechn:6.0.1/MMB29K/G9250ZCU2DQD1:user/release-keys" + private static final String VALUE_SAMSUNG_CLIENT_ID_BASE = "android-samsung"; + // Sony + private static final String KEY_SONY_PROTOCOL_TYPE = "ro.sony.irremote.protocol_type"; // "2" + private static final String KEY_SONY_ENCRYPTED_DATA = "ro.sony.fota.encrypteddata"; // "supported" + + private static final String VALUE_SONY_CLIENT_ID_BASE = "android-sonyericsson"; + // 乐视 : eui + private static final String KEY_EUI_VERSION = "ro.letv.release.version"; // "5.9.023S" + private static final String KEY_EUI_VERSION_DATE = "ro.letv.release.version_date"; // "5.9.023S_03111" + private static final String KEY_EUI_NAME = "ro.product.letv_name"; // "乐1s" + private static final String KEY_EUI_MODEL = "ro.product.letv_model"; // "Letv X500" + // 金立 : amigo + private static final String KEY_AMIGO_ROM_VERSION = "ro.gn.gnromvernumber"; // "GIONEE ROM5.0.16" + private static final String KEY_AMIGO_SYSTEM_UI_SUPPORT = "ro.gn.amigo.systemui.support"; // "yes" + + private static final String VALUE_AMIGO_DISPLAY_ID_CONTAIN = "amigo"; // "amigo3.5.1" + private static final String VALUE_AMIGO_CLIENT_ID_BASE = "android-gionee"; + // 酷派 : yulong + private static final String KEY_YULONG_VERSION_RELEASE = "ro.yulong.version.release"; // "5.1.046.P1.150921.8676_M01" + private static final String KEY_YULONG_VERSION_TAG = "ro.yulong.version.tag"; // "LC" + + private static final String VALUE_YULONG_CLIENT_ID_BASE = "android-coolpad"; + // HTC : Sense + private static final String KEY_SENSE_BUILD_STAGE = "htc.build.stage"; // "2" + private static final String KEY_SENSE_BLUETOOTH_SAP = "ro.htc.bluetooth.sap"; // "true" + + private static final String VALUE_SENSE_CLIENT_ID_BASE = "android-htc-rev"; + // LG : LG + private static final String KEY_LG_SW_VERSION = "ro.lge.swversion"; // "D85720b" + private static final String KEY_LG_SW_VERSION_SHORT = "ro.lge.swversion_short"; // "V20b" + private static final String KEY_LG_FACTORY_VERSION = "ro.lge.factoryversion"; // "LGD857AT-00-V20b-CUO-CN-FEB-17-2015+0" + // 联想 + private static final String KEY_LENOVO_DEVICE = "ro.lenovo.device"; // "phone" + private static final String KEY_LENOVO_PLATFORM = "ro.lenovo.platform"; // "qualcomm" + private static final String KEY_LENOVO_ADB = "ro.lenovo.adb"; // "apkctl,speedup" + + private static final String VALUE_LENOVO_CLIENT_ID_BASE = "android-lenovo"; + + /** + * 获取 ROM 类型 + * + * @return ROM + */ + public static ROM getRomType() { + return ROM_TYPE; + } + + /** + * 初始化 ROM 类型 + */ + private static ROM initRomType() { + ROM rom = ROM.Other; + FileInputStream is = null; + try { + Properties buildProperties = new Properties(); + is = new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")); + buildProperties.load(is); + + if (buildProperties.containsKey(KEY_MIUI_VERSION_NANE) || buildProperties.containsKey(KEY_MIUI_VERSION_CODE)) { + // MIUI + rom = ROM.MIUI; + if (buildProperties.containsKey(KEY_MIUI_VERSION_NANE)) { + String versionName = buildProperties.getProperty(KEY_MIUI_VERSION_NANE); + if (!TextUtils.isEmpty(versionName) && versionName.matches("[Vv]\\d+")) { // V8 + try { + rom.setBaseVersion(Integer.parseInt(versionName.split("[Vv]")[1])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if (buildProperties.containsKey(KEY_MIUI_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_MIUI_VERSION); + if (!TextUtils.isEmpty(versionStr) && versionStr.matches("[\\d.]+")) { + rom.setVersion(versionStr); + } + } + } else if (buildProperties.containsKey(KEY_EMUI_VERSION) || buildProperties.containsKey(KEY_EMUI_API_LEVEL) + || buildProperties.containsKey(KEY_EMUI_SYSTEM_VERSION)) { + // EMUI + rom = ROM.EMUI; + if (buildProperties.containsKey(KEY_EMUI_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_EMUI_VERSION); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("EmotionUI_([\\d.]+)").matcher(versionStr); // EmotionUI_3.0 + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_FLYME_SETUP) || buildProperties.containsKey(KEY_FLYME_PUBLISHED)) { + // Flyme + rom = ROM.Flyme; + if (buildProperties.containsKey(KEY_DISPLAY_ID)) { + String versionStr = buildProperties.getProperty(KEY_DISPLAY_ID); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("Flyme[^\\d]*([\\d.]+)[^\\d]*").matcher(versionStr); // Flyme OS 4.5.4.2U + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_COLOROS_VERSION) || buildProperties.containsKey(KEY_COLOROS_THEME_VERSION) + || buildProperties.containsKey(KEY_COLOROS_ROM_VERSION)) { + // ColorOS + rom = ROM.ColorOS; + if (buildProperties.containsKey(KEY_COLOROS_ROM_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_COLOROS_ROM_VERSION); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("ColorOS([\\d.]+)").matcher(versionStr); // ColorOS2.1 + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_FUNTOUCHOS_OS_NAME) || buildProperties.containsKey(KEY_FUNTOUCHOS_OS_VERSION) + || buildProperties.containsKey(KEY_FUNTOUCHOS_DISPLAY_ID)) { + // FuntouchOS + rom = ROM.FuntouchOS; + if (buildProperties.containsKey(KEY_FUNTOUCHOS_OS_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_FUNTOUCHOS_OS_VERSION); + if (!TextUtils.isEmpty(versionStr) && versionStr.matches("[\\d.]+")) { // 3.0 + try { + rom.setVersion(versionStr); + rom.setBaseVersion(Integer.parseInt(versionStr.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_EUI_VERSION) || buildProperties.containsKey(KEY_EUI_NAME) + || buildProperties.containsKey(KEY_EUI_MODEL)) { + // EUI + rom = ROM.EUI; + if (buildProperties.containsKey(KEY_EUI_VERSION)) { + String versionStr = buildProperties.getProperty(KEY_EUI_VERSION); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("([\\d.]+)[^\\d]*").matcher(versionStr); // 5.9.023S + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_AMIGO_ROM_VERSION) || buildProperties.containsKey(KEY_AMIGO_SYSTEM_UI_SUPPORT)) { + // amigo + rom = ROM.AmigoOS; + if (buildProperties.containsKey(KEY_DISPLAY_ID)) { + String versionStr = buildProperties.getProperty(KEY_DISPLAY_ID); + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Matcher matcher = Pattern.compile("amigo([\\d.]+)[a-zA-Z]*").matcher(versionStr); // "amigo3.5.1" + if (!TextUtils.isEmpty(versionStr) && matcher.find()) { + try { + String version = matcher.group(1); + rom.setVersion(version); + rom.setBaseVersion(Integer.parseInt(version.split("\\.")[0])); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } else if (buildProperties.containsKey(KEY_SONY_PROTOCOL_TYPE) || buildProperties.containsKey(KEY_SONY_ENCRYPTED_DATA)) { + // Sony + rom = ROM.Sony; + } else if (buildProperties.containsKey(KEY_YULONG_VERSION_RELEASE) || buildProperties.containsKey(KEY_YULONG_VERSION_TAG)) { + // YuLong + rom = ROM.YuLong; + } else if (buildProperties.containsKey(KEY_SENSE_BUILD_STAGE) || buildProperties.containsKey(KEY_SENSE_BLUETOOTH_SAP)) { + // Sense + rom = ROM.Sense; + } else if (buildProperties.containsKey(KEY_LG_SW_VERSION) || buildProperties.containsKey(KEY_LG_SW_VERSION_SHORT) + || buildProperties.containsKey(KEY_LG_FACTORY_VERSION)) { + // LG + rom = ROM.LG; + } else if (buildProperties.containsKey(KEY_LENOVO_DEVICE) || buildProperties.containsKey(KEY_LENOVO_PLATFORM) + || buildProperties.containsKey(KEY_LENOVO_ADB)) { + // Lenovo + rom = ROM.Lenovo; + } else if (buildProperties.containsKey(KEY_DISPLAY_ID)) { + String displayId = buildProperties.getProperty(KEY_DISPLAY_ID); + if (!TextUtils.isEmpty(displayId)) { + if (displayId.contains(VALUE_FLYME_DISPLAY_ID_CONTAIN)) { + return ROM.Flyme; + } else if (displayId.contains(VALUE_AMIGO_DISPLAY_ID_CONTAIN)) { + return ROM.AmigoOS; + } + } + } else if (buildProperties.containsKey(KEY_BASE_OS_VERSION)) { + String baseOsVersion = buildProperties.getProperty(KEY_BASE_OS_VERSION); + if (!TextUtils.isEmpty(baseOsVersion)) { + if (baseOsVersion.contains(VALUE_COLOROS_BASE_OS_VERSION_CONTAIN)) { + return ROM.ColorOS; + } else if (baseOsVersion.contains(VALUE_SAMSUNG_BASE_OS_VERSION_CONTAIN)) { + return ROM.SamSung; + } + } + } else if (buildProperties.containsKey(KEY_CLIENT_ID_BASE)) { + String clientIdBase = buildProperties.getProperty(KEY_CLIENT_ID_BASE); + switch (clientIdBase) { + case VALUE_MIUI_CLIENT_ID_BASE: + return ROM.MIUI; + case VALUE_COLOROS_CLIENT_ID_BASE: + return ROM.ColorOS; + case VALUE_FUNTOUCHOS_CLIENT_ID_BASE: + return ROM.FuntouchOS; + case VALUE_SAMSUNG_CLIENT_ID_BASE: + return ROM.SamSung; + case VALUE_SONY_CLIENT_ID_BASE: + return ROM.Sony; + case VALUE_YULONG_CLIENT_ID_BASE: + return ROM.YuLong; + case VALUE_SENSE_CLIENT_ID_BASE: + return ROM.Sense; + case VALUE_LENOVO_CLIENT_ID_BASE: + return ROM.Lenovo; + case VALUE_AMIGO_CLIENT_ID_BASE: + return ROM.AmigoOS; + default: + break; + } + } + + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return rom; + } + + public enum ROM { + MIUI, // 小米 + Flyme, // 魅族 + EMUI, // 华为 + ColorOS, // OPPO + FuntouchOS, // vivo + SmartisanOS, // 锤子 + EUI, // 乐视 + Sense, // HTC + AmigoOS, // 金立 + _360OS, // 奇酷360 + NubiaUI, // 努比亚 + H2OS, // 一加 + YunOS, // 阿里巴巴 + YuLong, // 酷派 + + SamSung, // 三星 + Sony, // 索尼 + Lenovo, // 联想 + LG, // LG + + Google, // 原生 + + Other; // CyanogenMod, Lewa OS, 百度云OS, Tencent OS, 深度OS, IUNI OS, Tapas OS, Mokee + + private int baseVersion = -1; + private String version; + + void setVersion(String version) { + this.version = version; + } + + void setBaseVersion(int baseVersion) { + this.baseVersion = baseVersion; + } + + public int getBaseVersion() { + return baseVersion; + } + + public String getVersion() { + return version; + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java new file mode 100644 index 0000000..da53e2c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/PictureUtil.java @@ -0,0 +1,189 @@ +package com.lennon.cn.utill.utill; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import androidx.exifinterface.media.ExifInterface; +import android.net.Uri; +import android.os.Environment; +import android.util.Base64; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + + +/** + * + * @author lennon + * @date 2017/6/7 + */ + +public class PictureUtil { + + public static String compressImage(String filePath, int quality) { + //获取一定尺寸的图片 + Bitmap bm = getSmallBitmap(filePath); + //获取相片拍摄角度 + int degree = readPictureDegree(filePath); + //旋转照片角度,防止头像横着显示 + if (degree != 0) { + bm = rotateBitmap(bm, degree); + } + File outputFile = new File(BaseApplication.Companion.context().getCacheDir() + "/image/" + FileUtil.getFileName(filePath)); + try { + if (!outputFile.exists()) { + outputFile.getParentFile().mkdirs(); + //outputFile.createNewFile(); + } else { + outputFile.delete(); + } + FileOutputStream out = new FileOutputStream(outputFile); + bm.compress(Bitmap.CompressFormat.JPEG, quality, out); + } catch (Exception e) { + } + return outputFile.getPath(); + } + + /** + * 根据路径获得图片信息并按比例压缩,返回bitmap + */ + public static Bitmap getSmallBitmap(String filePath) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true;//只解析图片边沿,获取宽高 + BitmapFactory.decodeFile(filePath, options); + // 计算缩放比 + options.inSampleSize = calculateInSampleSize(options, 480, 800); + // 完整解析图片返回bitmap + options.inJustDecodeBounds = false; + return BitmapFactory.decodeFile(filePath, options); + } + + + /** + * 获取照片角度 + * + * @param path + * @return + */ + public static int readPictureDegree(String path) { + int degree = 0; + try { + ExifInterface exifInterface = new ExifInterface(path); + int orientation = exifInterface.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL); + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + degree = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + degree = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + degree = 270; + break; + default: + break; + } + } catch (IOException e) { + e.printStackTrace(); + } + return degree; + } + + /** + * 旋转照片 + * + * @param bitmap + * @param degress + * @return + */ + public static Bitmap rotateBitmap(Bitmap bitmap, int degress) { + if (bitmap != null) { + Matrix m = new Matrix(); + m.postRotate(degress); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), + bitmap.getHeight(), m, true); + return bitmap; + } + return bitmap; + } + + public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + if (height > reqHeight || width > reqWidth) { + final int heightRatio = Math.round((float) height / (float) reqHeight); + final int widthRatio = Math.round((float) width / (float) reqWidth); + inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; + } + return inSampleSize; + } + + public static String bitmapToString(String filePath) { + Bitmap bm = getSmallBitmap(filePath, 480, 800); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bm.compress(Bitmap.CompressFormat.JPEG, 40, baos); + byte[] b = baos.toByteArray(); + return Base64.encodeToString(b, Base64.DEFAULT); + } + + + /** + * 根据路径获得突破并压缩返回bitmap用于显示 + * + * @param filePath 图片的路径 + * @param reqWidth 要求的图片的像素 + * @param reqHeight 要求的图片的像素 + * @return + */ + public static Bitmap getSmallBitmap(String filePath, int reqWidth, int reqHeight) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(filePath, options); + + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); + + options.inJustDecodeBounds = false; + + return BitmapFactory.decodeFile(filePath, options); + } + + public static void deleteTempFile(String path) { + File file = new File(path); + if (file.exists()) { + file.delete(); + } + } + + public static void galleryAddPic(Context context, String path) { + Intent mediaScanIntent = new Intent( + Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + File f = new File(path); + Uri contentUri = Uri.fromFile(f); + mediaScanIntent.setData(contentUri); + context.sendBroadcast(mediaScanIntent); + } + + public static File getAlbumDir() { + File dir = new File( + Environment + .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), + getAlbumName()); + if (!dir.exists()) { + dir.mkdirs(); + } + return dir; + } + + public static String getAlbumName() { + return UtillString.SAMPLE_DIR_NAME; + } +} + diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java b/utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java new file mode 100644 index 0000000..331f800 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/RandomUntil.java @@ -0,0 +1,149 @@ +package com.lennon.cn.utill.utill; + +import java.util.Random; + +/** + * 随机数、字母 工具类 + * Created by admin on 2017/2/20. + */ +public class RandomUntil { + + /** + * 生成一个0 到 count 之间的随机数 + * + * @param endNum + * @return + */ + public static int getNum(int endNum) { + if (endNum > 0) { + Random random = new Random(); + return random.nextInt(endNum); + } + return 0; + } + + /** + * 生成一个startNum 到 endNum之间的随机数(不包含endNum的随机数) + * + * @param startNum + * @param endNum + * @return + */ + public static int getNum(int startNum, int endNum) { + if (endNum > startNum) { + Random random = new Random(); + return random.nextInt(endNum - startNum) + startNum; + } + return 0; + } + + /** + * 生成随机大写字母 + * + * @return + */ + public static String getLargeLetter() { + Random random = new Random(); + return String.valueOf((char) (random.nextInt(27) + 'A')); + } + + /** + * 生成随机大写字母字符串 + * + * @return + */ + public static String getLargeLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + buffer.append((char) (random.nextInt(27) + 'A')); + } + return buffer.toString(); + } + + /** + * 生成随机小写字母 + * + * @return + */ + public static String getSmallLetter() { + Random random = new Random(); + return String.valueOf((char) (random.nextInt(27) + 'a')); + } + + /** + * 生成随机小写字母字符串 + * + * @return + */ + public static String getSmallLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + buffer.append((char) (random.nextInt(27) + 'a')); + } + return buffer.toString(); + } + + /** + * 数字与小写字母混编字符串 + * + * @param size + * @return + */ + public static String getNumSmallLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + if (random.nextInt(2) % 2 == 0) {//字母 + buffer.append((char) (random.nextInt(27) + 'a')); + } else {//数字 + buffer.append(random.nextInt(10)); + } + } + return buffer.toString(); + } + + /** + * 数字与大写字母混编字符串 + * + * @param size + * @return + */ + public static String getNumLargeLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + if (random.nextInt(2) % 2 == 0) {//字母 + buffer.append((char) (random.nextInt(27) + 'A')); + } else {//数字 + buffer.append(random.nextInt(10)); + } + } + return buffer.toString(); + } + + /** + * 数字与大小写字母混编字符串 + * + * @param size + * @return + */ + public static String getNumLargeSmallLetter(int size) { + StringBuffer buffer = new StringBuffer(); + Random random = new Random(); + for (int i = 0; i < size; i++) { + if (random.nextInt(2) % 2 == 0) {//字母 + if (random.nextInt(2) % 2 == 0) { + buffer.append((char) (random.nextInt(27) + 'A')); + } else { + buffer.append((char) (random.nextInt(27) + 'a')); + } + } else {//数字 + buffer.append(random.nextInt(10)); + } + } + return buffer.toString(); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java new file mode 100644 index 0000000..1f0efbf --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/StatusBarUtil.java @@ -0,0 +1,864 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.graphics.Color; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.LinearLayout; +import androidx.annotation.ColorInt; +import androidx.annotation.IntDef; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.drawerlayout.widget.DrawerLayout; +import lennon.com.utill.R; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class StatusBarUtil { + public final static int TYPE_MIUI = 0; + public final static int TYPE_FLYME = 1; + public final static int TYPE_M = 3;//6.0 + + @IntDef({TYPE_MIUI, + TYPE_FLYME, + TYPE_M}) + @Retention(RetentionPolicy.SOURCE) + @interface ViewType { + } + public static final int DEFAULT_STATUS_BAR_ALPHA = 112; + private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view; + private static final int FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view; + private static final int TAG_KEY_HAVE_SET_OFFSET = -123; + + /** + * 设置状态栏颜色 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + public static void setColor(Activity activity, @ColorInt int color) { + setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 设置状态栏颜色 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + * @param statusBarAlpha 状态栏透明度 + */ + + public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + } else { + decorView.addView(createStatusBarView(activity, color, statusBarAlpha)); + } + setRootView(activity); + } + } + + /** + * 为滑动返回界面设置状态栏颜色 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + */ + public static void setColorForSwipeBack(Activity activity, int color) { + setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 为滑动返回界面设置状态栏颜色 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + * @param statusBarAlpha 状态栏透明度 + */ + public static void setColorForSwipeBack(Activity activity, @ColorInt int color, + @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + + ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content)); + View rootView = contentView.getChildAt(0); + int statusBarHeight = getStatusBarHeight(activity); + if (rootView != null && rootView instanceof CoordinatorLayout) { + final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + coordinatorLayout.setFitsSystemWindows(false); + contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight; + if (isNeedRequestLayout) { + contentView.setPadding(0, statusBarHeight, 0, 0); + coordinatorLayout.post(new Runnable() { + @Override + public void run() { + coordinatorLayout.requestLayout(); + } + }); + } + } else { + coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + } + } else { + contentView.setPadding(0, statusBarHeight, 0, 0); + contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); + } + setTransparentForWindow(activity); + } + } + + /** + * 设置状态栏纯色 不加半透明效果 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + public static void setColorNoTranslucent(Activity activity, @ColorInt int color) { + setColor(activity, color, 0); + } + + /** + * 设置状态栏颜色(5.0以下无半透明效果,不建议使用) + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + */ + @Deprecated + public static void setColorDiff(Activity activity, @ColorInt int color) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + transparentStatusBar(activity); + ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); + // 移除半透明矩形,以免叠加 + View fakeStatusBarView = contentView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(color); + } else { + contentView.addView(createStatusBarView(activity, color)); + } + setRootView(activity); + } + + /** + * 使状态栏半透明 + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + */ + public static void setTranslucent(Activity activity) { + setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 使状态栏半透明 + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + * @param statusBarAlpha 状态栏透明度 + */ + public static void setTranslucent(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setTransparent(activity); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 针对根布局是 CoordinatorLayout, 使状态栏半透明 + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + * @param statusBarAlpha 状态栏透明度 + */ + public static void setTranslucentForCoordinatorLayout(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + transparentStatusBar(activity); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 设置状态栏全透明 + * + * @param activity 需要设置的activity + */ + public static void setTransparent(Activity activity) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + transparentStatusBar(activity); + setRootView(activity); + } + + /** + * 使状态栏透明(5.0以上半透明效果,不建议使用) + *

+ * 适用于图片作为背景的界面,此时需要图片填充到状态栏 + * + * @param activity 需要设置的activity + */ + @Deprecated + public static void setTranslucentDiff(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // 设置状态栏透明 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + setRootView(activity); + } + } + + /** + * 为DrawerLayout 布局设置状态栏变色 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + */ + public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { + setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 为DrawerLayout 布局设置状态栏颜色,纯色 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + */ + public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { + setColorForDrawerLayout(activity, drawerLayout, color, 0); + } + + /** + * 为DrawerLayout 布局设置状态栏变色 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + * @param statusBarAlpha 状态栏透明度 + */ + public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color, + @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + // 生成一个状态栏大小的矩形 + // 添加 statusBarView 到布局中 + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(color); + } else { + contentLayout.addView(createStatusBarView(activity, color), 0); + } + // 内容布局不是 LinearLayout 时,设置padding top + if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { + contentLayout.getChildAt(1) + .setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(), + contentLayout.getPaddingRight(), contentLayout.getPaddingBottom()); + } + // 设置属性 + setDrawerLayoutProperty(drawerLayout, contentLayout); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 设置 DrawerLayout 属性 + * + * @param drawerLayout DrawerLayout + * @param drawerLayoutContentLayout DrawerLayout 的内容布局 + */ + private static void setDrawerLayoutProperty(DrawerLayout drawerLayout, ViewGroup drawerLayoutContentLayout) { + ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); + drawerLayout.setFitsSystemWindows(false); + drawerLayoutContentLayout.setFitsSystemWindows(false); + drawerLayoutContentLayout.setClipToPadding(true); + drawer.setFitsSystemWindows(false); + } + + /** + * 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用) + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + * @param color 状态栏颜色值 + */ + @Deprecated + public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // 生成一个状态栏大小的矩形 + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + if (fakeStatusBarView.getVisibility() == View.GONE) { + fakeStatusBarView.setVisibility(View.VISIBLE); + } + fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA)); + } else { + // 添加 statusBarView 到布局中 + contentLayout.addView(createStatusBarView(activity, color), 0); + } + // 内容布局不是 LinearLayout 时,设置padding top + if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { + contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); + } + // 设置属性 + setDrawerLayoutProperty(drawerLayout, contentLayout); + } + } + + /** + * 为 DrawerLayout 布局设置状态栏透明 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { + setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA); + } + + /** + * 为 DrawerLayout 布局设置状态栏透明 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, + @IntRange(from = 0, to = 255) int statusBarAlpha) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setTransparentForDrawerLayout(activity, drawerLayout); + addTranslucentView(activity, statusBarAlpha); + } + + /** + * 为 DrawerLayout 布局设置状态栏透明 + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + // 内容布局不是 LinearLayout 时,设置padding top + if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { + contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); + } + + // 设置属性 + setDrawerLayoutProperty(drawerLayout, contentLayout); + } + + /** + * 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用) + * + * @param activity 需要设置的activity + * @param drawerLayout DrawerLayout + */ + @Deprecated + public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // 设置状态栏透明 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // 设置内容布局属性 + ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); + contentLayout.setFitsSystemWindows(true); + contentLayout.setClipToPadding(true); + // 设置抽屉布局属性 + ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1); + vg.setFitsSystemWindows(false); + // 设置 DrawerLayout 属性 + drawerLayout.setFitsSystemWindows(false); + } + } + + /** + * 为头部是 ImageView 的界面设置状态栏全透明 + * + * @param activity 需要设置的activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTransparentForImageView(Activity activity, View needOffsetView) { + setTranslucentForImageView(activity, 0, needOffsetView); + } + + /** + * 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度) + * + * @param activity 需要设置的activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageView(Activity activity, View needOffsetView) { + setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); + } + + /** + * 为头部是 ImageView 的界面设置状态栏透明 + * + * @param activity 需要设置的activity + * @param statusBarAlpha 状态栏透明度 + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha, + View needOffsetView) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + return; + } + setTransparentForWindow(activity); + addTranslucentView(activity, statusBarAlpha); + if (needOffsetView != null) { + Object haveSetOffset = needOffsetView.getTag(TAG_KEY_HAVE_SET_OFFSET); + if (haveSetOffset != null && (Boolean) haveSetOffset) { + return; + } + ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams(); + layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity), + layoutParams.rightMargin, layoutParams.bottomMargin); + needOffsetView.setTag(TAG_KEY_HAVE_SET_OFFSET, true); + } + } + + /** + * 为 fragment 头部是 ImageView 的设置状态栏透明 + * + * @param activity fragment 对应的 activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) { + setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView); + } + + /** + * 为 fragment 头部是 ImageView 的设置状态栏透明 + * + * @param activity fragment 对应的 activity + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) { + setTranslucentForImageViewInFragment(activity, 0, needOffsetView); + } + + /** + * 为 fragment 头部是 ImageView 的设置状态栏透明 + * + * @param activity fragment 对应的 activity + * @param statusBarAlpha 状态栏透明度 + * @param needOffsetView 需要向下偏移的 View + */ + public static void setTranslucentForImageViewInFragment(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha, + View needOffsetView) { + setTranslucentForImageView(activity, statusBarAlpha, needOffsetView); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + clearPreviousSetting(activity); + } + } + + /** + * 隐藏伪状态栏 View + * + * @param activity 调用的 Activity + */ + public static void hideFakeStatusBarView(Activity activity) { + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + fakeStatusBarView.setVisibility(View.GONE); + } + View fakeTranslucentView = decorView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); + if (fakeTranslucentView != null) { + fakeTranslucentView.setVisibility(View.GONE); + } + } + + @TargetApi(Build.VERSION_CODES.M) + public static void setLightMode(Activity activity) { + setMIUIStatusBarDarkIcon(activity, true); + setMeizuStatusBarDarkIcon(activity, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + + @TargetApi(Build.VERSION_CODES.M) + public static void setDarkMode(Activity activity) { + setMIUIStatusBarDarkIcon(activity, false); + setMeizuStatusBarDarkIcon(activity, false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + + /** + * 修改 MIUI V6 以上状态栏颜色 + */ + private static void setMIUIStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) { + Class clazz = activity.getWindow().getClass(); + try { + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + int darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + extraFlagField.invoke(activity.getWindow(), darkIcon ? darkModeFlag : 0, darkModeFlag); + } catch (Exception e) { + //e.printStackTrace(); + } + } + + /** + * 修改魅族状态栏字体颜色 Flyme 4.0 + */ + private static void setMeizuStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) { + try { + WindowManager.LayoutParams lp = activity.getWindow().getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (darkIcon) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + activity.getWindow().setAttributes(lp); + } catch (Exception e) { + //e.printStackTrace(); + } + } + + /////////////////////////////////////////////////////////////////////////////////// + + @TargetApi(Build.VERSION_CODES.KITKAT) + private static void clearPreviousSetting(Activity activity) { + ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); + View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID); + if (fakeStatusBarView != null) { + decorView.removeView(fakeStatusBarView); + ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); + rootView.setPadding(0, 0, 0, 0); + } + } + + /** + * 添加半透明矩形条 + * + * @param activity 需要设置的 activity + * @param statusBarAlpha 透明值 + */ + private static void addTranslucentView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) { + ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content); + View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID); + if (fakeTranslucentView != null) { + if (fakeTranslucentView.getVisibility() == View.GONE) { + fakeTranslucentView.setVisibility(View.VISIBLE); + } + fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0)); + } else { + contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha)); + } + } + + /** + * 生成一个和状态栏大小相同的彩色矩形条 + * + * @param activity 需要设置的 activity + * @param color 状态栏颜色值 + * @return 状态栏矩形条 + */ + private static View createStatusBarView(Activity activity, @ColorInt int color) { + return createStatusBarView(activity, color, 0); + } + + /** + * 生成一个和状态栏大小相同的半透明矩形条 + * + * @param activity 需要设置的activity + * @param color 状态栏颜色值 + * @param alpha 透明值 + * @return 状态栏矩形条 + */ + private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) { + // 绘制一个和状态栏一样高的矩形 + View statusBarView = new View(activity); + LinearLayout.LayoutParams params = + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); + statusBarView.setLayoutParams(params); + statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); + statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID); + return statusBarView; + } + + /** + * 设置根布局参数 + */ + private static void setRootView(Activity activity) { + ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content); + for (int i = 0, count = parent.getChildCount(); i < count; i++) { + View childView = parent.getChildAt(i); + if (childView instanceof ViewGroup) { + childView.setFitsSystemWindows(true); + ((ViewGroup) childView).setClipToPadding(true); + } + } + } + + /** + * 设置透明 + */ + private static void setTransparentForWindow(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + activity.getWindow() + .getDecorView() + .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + activity.getWindow() + .setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + /** + * 使状态栏透明 + */ + @TargetApi(Build.VERSION_CODES.KITKAT) + private static void transparentStatusBar(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + activity.getWindow().setStatusBarColor(Color.TRANSPARENT); + } else { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + /** + * 创建半透明矩形 View + * + * @param alpha 透明值 + * @return 半透明 View + */ + private static View createTranslucentStatusBarView(Activity activity, int alpha) { + // 绘制一个和状态栏一样高的矩形 + View statusBarView = new View(activity); + LinearLayout.LayoutParams params = + new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); + statusBarView.setLayoutParams(params); + statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0)); + statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID); + return statusBarView; + } + + /** + * 获取状态栏高度 + * + * @param context context + * @return 状态栏高度 + */ + private static int getStatusBarHeight(Context context) { + // 获得状态栏高度 + int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + return context.getResources().getDimensionPixelSize(resourceId); + } + + /** + * 计算状态栏颜色 + * + * @param color color值 + * @param alpha alpha值 + * @return 最终的状态栏颜色 + */ + private static int calculateStatusColor(@ColorInt int color, int alpha) { + if (alpha == 0) { + return color; + } + float a = 1 - alpha / 255f; + int red = color >> 16 & 0xff; + int green = color >> 8 & 0xff; + int blue = color & 0xff; + red = (int) (red * a + 0.5); + green = (int) (green * a + 0.5); + blue = (int) (blue * a + 0.5); + return 0xff << 24 | red << 16 | green << 8 | blue; + } + /** + * 修改状态栏颜色,支持4.4以上版本 + * @param colorId 颜色 + */ + public static void setStatusBarColor(Activity activity, int colorId) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = activity.getWindow(); + window.setStatusBarColor(colorId); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + //使用SystemBarTintManager,需要先将状态栏设置为透明 + setTranslucentStatus(activity); + SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity); + systemBarTintManager.setStatusBarTintEnabled(true);//显示状态栏 + systemBarTintManager.setStatusBarTintColor(colorId);//设置状态栏颜色 + } + } + + /** + * 设置状态栏透明 + */ + @TargetApi(19) + public static void setTranslucentStatus(Activity activity) { + // 5.0以上系统状态栏透明 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Window window = activity.getWindow(); + //清除透明状态栏 + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + //设置状态栏颜色必须添加 + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT);//设置透明 + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //19 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + } + } + + /** + * 设置沉浸式状态栏 + * + * @param fontIconDark 状态栏字体和图标颜色是否为深色 + */ + public static void setImmersiveStatusBar(Activity activity, boolean fontIconDark) { + setTranslucentStatus(activity); + if (fontIconDark) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + setStatusBarFontIconDark(activity,TYPE_M); + } else if (OSUtils.ROM.MIUI.equals(OSUtils.getRomType())) { + setStatusBarFontIconDark(activity,TYPE_MIUI); + } else if (OSUtils.ROM.Flyme.equals(OSUtils.getRomType())) { + setStatusBarFontIconDark(activity,TYPE_FLYME); + } else {//其他情况下我们将状态栏设置为灰色,就不会看不见字体 + setStatusBarColor(activity, Color.LTGRAY);//灰色 + } + } + } + + /** + * 设置文字颜色 + */ + public static void setStatusBarFontIconDark(Activity activity, @ViewType int type) { + switch (type) { + case TYPE_MIUI: + setMiuiUI(activity,true); + break; + case TYPE_M: + setCommonUI(activity); + break; + case TYPE_FLYME: + setFlymeUI(activity,true); + break; + default: + break; + } + } + + //设置6.0的字体 + public static void setCommonUI(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + activity.getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } + } + + //设置Flyme的字体 + public static void setFlymeUI(Activity activity, boolean dark) { + try { + Window window = activity.getWindow(); + WindowManager.LayoutParams lp = window.getAttributes(); + Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON"); + Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags"); + darkFlag.setAccessible(true); + meizuFlags.setAccessible(true); + int bit = darkFlag.getInt(null); + int value = meizuFlags.getInt(lp); + if (dark) { + value |= bit; + } else { + value &= ~bit; + } + meizuFlags.setInt(lp, value); + window.setAttributes(lp); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //设置MIUI字体 + public static void setMiuiUI(Activity activity, boolean dark) { + try { + Window window = activity.getWindow(); + Class clazz = activity.getWindow().getClass(); + Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams"); + Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE"); + int darkModeFlag = field.getInt(layoutParams); + Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class); + if (dark) { //状态栏亮色且黑色字体 + extraFlagField.invoke(window, darkModeFlag, darkModeFlag); + } else { + extraFlagField.invoke(window, 0, darkModeFlag); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java new file mode 100644 index 0000000..89eadb5 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/StringUtils.java @@ -0,0 +1,904 @@ +package com.lennon.cn.utill.utill; + +import android.app.AlarmManager; +import android.text.TextUtils; +import cn.droidlover.xdroidmvp.log.XLog; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 字符串操作工具包 + * + * @author liux (http://my.oschina.net/liux) + * @author thanatosx + * @version 2.0 + * Updated 2016-08-11 + */ +public class StringUtils { + public static String getHostName(String urlString) { + String head = ""; + int index = urlString.indexOf("://"); + if (index != -1) { + head = urlString.substring(0, index + 3); + urlString = urlString.substring(index + 3); + } + index = urlString.indexOf("/"); + if (index != -1) { + urlString = urlString.substring(0, index + 1); + } + return head + urlString; + } + + public static String getDataSize(long var0) { + DecimalFormat var2 = new DecimalFormat("###.00"); + return var0 < 1024L ? var0 + "bytes" : (var0 < 1048576L ? var2.format((double) ((float) var0 / 1024.0F)) + + "KB" : (var0 < 1073741824L ? var2.format((double) ((float) var0 / 1024.0F / 1024.0F)) + + "MB" : (var0 < 0L ? var2.format((double) ((float) var0 / 1024.0F / 1024.0F / 1024.0F)) + + "GB" : "error"))); + } + private final static Pattern emailer = Pattern + .compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"); + + private final static Pattern IMG_URL = Pattern + .compile(".*?(gif|jpeg|png|jpg|bmp)"); + + private final static Pattern URL = Pattern + .compile("^(https|http)://.*?$(net|com|.com.cn|org|me|)"); + + private final static ThreadLocal YYYYMMDDHHMMSS = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); + } + }; + + private final static ThreadLocal YYYYMMDD = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + } + }; + + private final static ThreadLocal YYYYMMDDHHMM = new ThreadLocal() { + @Override + protected SimpleDateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()); + } + }; + + private static final Pattern UniversalDatePattern = Pattern.compile( + "([0-9]{4})-([0-9]{2})-([0-9]{2})[\\s]+([0-9]{2}):([0-9]{2}):([0-9]{2})" + ); + + /** + * 将字符串转位日期类型 + * + * @param sdate string date that's type like YYYY-MM-DD HH:mm:ss + * @return {@link Date} + */ + public static Date toDate(String sdate) { + return toDate(sdate, YYYYMMDDHHMMSS.get()); + } + + public static Date toDate(String sdate, SimpleDateFormat formatter) { + try { + return formatter.parse(sdate); + } catch (Exception e) { + return null; + } + } + + /** + * YYYY-MM-DD HH:mm:ss格式的时间字符串转换为{@link Calendar}类型 + * + * @param str YYYY-MM-DD HH:mm:ss格式字符串 + * @return {@link Calendar} + */ + public static Calendar parseCalendar(String str) { + Matcher matcher = UniversalDatePattern.matcher(str); + Calendar calendar = Calendar.getInstance(); + if (!matcher.find()) { + return null; + } + calendar.set( + matcher.group(1) == null ? 0 : toInt(matcher.group(1)), + matcher.group(2) == null ? 0 : toInt(matcher.group(2)) - 1, + matcher.group(3) == null ? 0 : toInt(matcher.group(3)), + matcher.group(4) == null ? 0 : toInt(matcher.group(4)), + matcher.group(5) == null ? 0 : toInt(matcher.group(5)), + matcher.group(6) == null ? 0 : toInt(matcher.group(6)) + ); + return calendar; + } + + /** + * transform date to string that's type like YYYY-MM-DD HH:mm:ss + * + * @param date {@link Date} + * @return + */ + public static String getDateString(Date date) { + return YYYYMMDDHHMMSS.get().format(date); + } + + /** + * transform date to string that's type like YYYY-MM-DD HH:mm + * + * @param sdate + * @return + */ + public static String getDateString(String sdate) { + if (TextUtils.isEmpty(sdate)) { + return ""; + } + return YYYYMMDDHHMM.get().format(toDate(sdate)); + } + + + public static String formatYearMonthDay(String st) { + if (TextUtils.isEmpty(st)) { + return ""; + } + Matcher matcher = UniversalDatePattern.matcher(st); + if (!matcher.find()) { + return st; + } + return String.format("%s年%s月%s日", + matcher.group(1) == null ? 0 : matcher.group(1), + matcher.group(2) == null ? 0 : matcher.group(2), + matcher.group(3) == null ? 0 : matcher.group(3)); + } + + public static String formatYearMonthDayNew(String st) { + if (TextUtils.isEmpty(st)) { + return ""; + } + Matcher matcher = UniversalDatePattern.matcher(st); + if (!matcher.find()) { + return st; + } + return String.format("%s/%s/%s", + matcher.group(1) == null ? 0 : matcher.group(1), + matcher.group(2) == null ? 0 : matcher.group(2), + matcher.group(3) == null ? 0 : matcher.group(3)); + } + + /** + * format time friendly + * + * @param sdate YYYY-MM-DD HH:mm:ss + * @return n分钟前, n小时前, 昨天, 前天, n天前, n个月前 + */ + public static String formatSomeAgo(String sdate) { + if (sdate == null) { + return ""; + } + Calendar calendar = parseCalendar(sdate); + if (calendar == null) { + return sdate; + } + + Calendar mCurrentDate = Calendar.getInstance(); + long crim = mCurrentDate.getTimeInMillis(); // current + long trim = calendar.getTimeInMillis(); // target + long diff = crim - trim; + + int year = mCurrentDate.get(Calendar.YEAR); + int month = mCurrentDate.get(Calendar.MONTH); + int day = mCurrentDate.get(Calendar.DATE); + + if (diff < 60 * 1000) { + return "刚刚"; + } + if (diff >= 60 * 1000 && diff < AlarmManager.INTERVAL_HOUR) { + return String.format("%s分钟前", diff / 60 / 1000); + } + mCurrentDate.set(year, month, day, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return String.format("%s小时前", diff / AlarmManager.INTERVAL_HOUR); + } + mCurrentDate.set(year, month, day - 1, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "昨天"; + } + mCurrentDate.set(year, month, day - 2, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "前天"; + } + if (diff < AlarmManager.INTERVAL_DAY * 30) { + return String.format("%s天前", diff / AlarmManager.INTERVAL_DAY); + } + if (diff < AlarmManager.INTERVAL_DAY * 30 * 12) { + return String.format("%s月前", diff / (AlarmManager.INTERVAL_DAY * 30)); + } + return String.format("%s年前", mCurrentDate.get(Calendar.YEAR) - calendar.get(Calendar.YEAR)); + } + + /** + * @param str YYYY-MM-DD HH:mm:ss string + * @return 今天, 昨天, 前天, n天前 + */ + public static String formatSomeDay(String str) { + return formatSomeDay(parseCalendar(str)); + } + + /** + * @param calendar {@link Calendar} + * @return 今天, 昨天, 前天, n天前 + */ + public static String formatSomeDay(Calendar calendar) { + if (calendar == null) { + return "?天前"; + } + Calendar mCurrentDate = Calendar.getInstance(); + long crim = mCurrentDate.getTimeInMillis(); // current + long trim = calendar.getTimeInMillis(); // target + long diff = crim - trim; + + int year = mCurrentDate.get(Calendar.YEAR); + int month = mCurrentDate.get(Calendar.MONTH); + int day = mCurrentDate.get(Calendar.DATE); + + mCurrentDate.set(year, month, day, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "今天"; + } + mCurrentDate.set(year, month, day - 1, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "昨天"; + } + mCurrentDate.set(year, month, day - 2, 0, 0, 0); + if (trim >= mCurrentDate.getTimeInMillis()) { + return "前天"; + } + return String.format("%s天前", diff / AlarmManager.INTERVAL_DAY); + } + + /** + * @param calendar {@link Calendar} + * @return 星期n + */ + public static String formatWeek(Calendar calendar) { + if (calendar == null) { + return "星期?"; + } + return new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"}[calendar.get(Calendar.DAY_OF_WEEK) - 1]; + } + + /** + * @param str YYYY-MM-DD HH:mm:ss string + * @return 星期n + */ + public static String formatWeek(String str) { + return formatWeek(parseCalendar(str)); + } + + /** + * @param sdate YYYY-MM-DD HH:mm:ss string + * @return + */ + public static String formatDayWeek(String sdate) { + Calendar calendar = parseCalendar(sdate); + if (calendar == null) { + return "??/?? 星期?"; + } + Calendar mCurrentDate = Calendar.getInstance(); + String ws = formatWeek(calendar); + int diff = mCurrentDate.get(Calendar.DATE) - calendar.get(Calendar.DATE); + if (diff == 0) { + return "今天 / " + ws; + } + if (diff == 1) { + return "昨天 / " + ws; + } + int m = calendar.get(Calendar.MONTH); + int d = calendar.get(Calendar.DATE); + return String.format("%s/%s / %s", formatInt(m), formatInt(d), ws); + } + + /** + * format to HH + * + * @param i integer + * @return HH + */ + public static String formatInt(int i) { + return (i < 10 ? "0" : "") + i; + } + + + /** + * 智能格式化 + */ + public static String friendly_time3(String sdate) { + Calendar calendar = parseCalendar(sdate); + if (calendar == null) { + return sdate; + } + + Calendar mCurrentDate = Calendar.getInstance(); + SimpleDateFormat formatter = YYYYMMDDHHMMSS.get(); + + int hour = calendar.get(Calendar.HOUR_OF_DAY); + String s = hour >= 0 && hour < 12 ? "上午" : "下午"; + s += " HH:mm"; + + if (mCurrentDate.get(Calendar.DATE) == calendar.get(Calendar.DATE)) { + formatter.applyPattern(s); + } else if (mCurrentDate.get(Calendar.DATE) - calendar.get(Calendar.DATE) == 1) { + formatter.applyPattern("昨天 " + s); + } else if (mCurrentDate.get(Calendar.YEAR) == calendar.get(Calendar.YEAR)) { + formatter.applyPattern("MM-dd " + s); + } else { + formatter.applyPattern("YYYY-MM-dd " + s); + } + return formatter.format(calendar.getTime()); + } + + + /** + * 判断给定字符串时间是否为今日 + * + * @param sdate + * @return boolean + */ + public static boolean isToday(String sdate) { + Date time = toDate(sdate); + Date today = new Date(); + if (time != null) { + String nowDate = YYYYMMDD.get().format(today); + String timeDate = YYYYMMDD.get().format(time); + if (nowDate.equals(timeDate)) { + return true; + } + } + return false; + } + + /** + * 是否是相同的一天 + * + * @param sdate1 sdate1 + * @param sdate2 sdate2 + * @return + */ + public static boolean isSameDay(String sdate1, String sdate2) { + if (TextUtils.isEmpty(sdate1) || TextUtils.isEmpty(sdate2)) { + return false; + } + Date date1 = toDate(sdate1); + Date date2 = toDate(sdate2); + if (date1 != null && date2 != null) { + String d1 = YYYYMMDD.get().format(date1); + String d2 = YYYYMMDD.get().format(date2); + if (d1.equals(d2)) { + return true; + } + } + return false; + } + + public static String getCurrentTimeStr() { + return YYYYMMDDHHMMSS.get().format(new Date()); + } + + /*** + * 计算两个时间差,返回的是的秒s + * + * @param date1 + * @param date2 + * @return + * @author 火蚁 2015-2-9 下午4:50:06 + */ + public static long calDateDifferent(String date1, String date2) { + try { + Date d1 = YYYYMMDDHHMMSS.get().parse(date1); + Date d2 = YYYYMMDDHHMMSS.get().parse(date2); + // 毫秒ms + long diff = d2.getTime() - d1.getTime(); + return diff / 1000; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 判断给定字符串是否空白串。 空白串是指由空格、制表符、回车符、换行符组成的字符串 若输入字符串为null或空字符串,返回true + * + * @param input + * @return boolean + */ + @Deprecated + public static boolean isEmpty(String input) { + if (input == null || "".equals(input)) { + return true; + } + + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { + return false; + } + } + return true; + } + + /** + * 判断一个url是否为图片url + * + * @param url + * @return + */ + public static boolean isImgUrl(String url) { + if (url == null || url.trim().length() == 0) { + return false; + } + return IMG_URL.matcher(url).matches(); + } + + /** + * 判断是否为一个合法的url地址 + * + * @param str + * @return + */ + public static boolean isUrl(String str) { + if (str == null || str.trim().length() == 0) { + return false; + } + return URL.matcher(str).matches(); + } + + /** + * 字符串转整数 + * + * @param str + * @param defValue + * @return + */ + public static int toInt(String str, int defValue) { + try { + return Integer.parseInt(str); + } catch (Exception e) { + XLog.d("oschina "+ e.getMessage()); + } + return defValue; + } + + /** + * 对象转整数 + * + * @param obj + * @return 转换异常返回 0 + */ + public static int toInt(Object obj) { + if (obj == null) { + return 0; + } + return toInt(obj.toString(), 0); + } + + /** + * 对象转整数 + * + * @param obj + * @return 转换异常返回 0 + */ + public static long toLong(String obj) { + try { + return Long.parseLong(obj); + } catch (Exception e) { + } + return 0; + } + + /** + * 字符串转布尔值 + * + * @param b + * @return 转换异常返回 false + */ + public static boolean toBool(String b) { + try { + return Boolean.parseBoolean(b); + } catch (Exception e) { + } + return false; + } + + public static String getString(String s) { + return s == null ? "" : s; + } + + /** + * 将一个InputStream流转换成字符串 + * + * @param is + * @return + */ + public static String toConvertString(InputStream is) { + StringBuilder res = new StringBuilder(); + BufferedReader read = new BufferedReader(new InputStreamReader(is)); + try { + String line; + while ((line = read.readLine()) != null) { + res.append(line).append("
"); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + read.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return res.toString(); + } + + /*** + * 截取字符串 + * + * @param start 从那里开始,0算起 + * @param num 截取多少个 + * @param str 截取的字符串 + * @return + */ + public static String getSubString(int start, int num, String str) { + if (str == null) { + return ""; + } + int length = str.length(); + if (start < 0) { + start = 0; + } + if (start > length) { + start = length; + } + if (num < 0) { + num = 1; + } + int end = start + num; + if (end > length) { + end = length; + } + return str.substring(start, end); + } + + /** + * 获取当前时间为每年第几周 + * + * @return + */ + public static int getWeekOfYear() { + return getWeekOfYear(new Date()); + } + + /** + * 获取当前时间为每年第几周 + * + * @param date + * @return + */ + public static int getWeekOfYear(Date date) { + Calendar c = Calendar.getInstance(); + c.setFirstDayOfWeek(Calendar.MONDAY); + c.setTime(date); + int week = c.get(Calendar.WEEK_OF_YEAR) - 1; + week = week == 0 ? 52 : week; + return week > 0 ? week : 1; + } + + public static int[] getCurrentDate() { + int[] dateBundle = new int[3]; + String[] temp = getDataTime("yyyy-MM-dd").split("-"); + + for (int i = 0; i < 3; i++) { + try { + dateBundle[i] = Integer.parseInt(temp[i]); + } catch (Exception e) { + dateBundle[i] = 0; + } + } + return dateBundle; + } + + /** + * 返回当前系统时间 + */ + public static String getDataTime(String format) { + return new SimpleDateFormat(format, Locale.getDefault()).format(new Date()); + } + + public static boolean isNotEmpty(String value) { + return !isEmpty(value); + } + + /** + * 检查对象是否为数字型字符串,包含负数开头的。 + * + * @param target + * @return + */ + public static boolean isNumeric(String target) { + if (target == null) { + return false; + } + char[] chars = target.toString().toCharArray(); + int length = chars.length; + if (length < 1) { + return false; + } + + int i = 0; + if (length > 1 && chars[0] == '-') { + i = 1; + } + + for (; i < length; i++) { + if (!Character.isDigit(chars[i])) { + return false; + } + } + return true; + } + + /** + * 判断传入字符串是否为数字类型 + * + * @param target + * @param isRadix + * 是否小数 + * @return + */ + public static boolean isNumeric(String target, boolean isRadix) { + if (null == target || "".equals(target.trim())) { + return false; + } + if (isRadix) { + if (target.matches("[0-9]+.[0-9]+")) { + return true; + } + } + else { + if (target.matches("[0-9]+")) { + return true; + } + } + + return false; + } + + /** + * 把通用字符编码的字符串转化为汉字编码。 + */ + public static String unicodeToChinese(String unicode) { + StringBuilder out = new StringBuilder(); + if (!isEmpty(unicode)) { + for (int i = 0; i < unicode.length(); i++) { + out.append(unicode.charAt(i)); + } + } + return out.toString(); + } + + public static String toUnderlineStyle(String name) { + StringBuilder newName = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isUpperCase(c)) { + if (i > 0) { + newName.append("_"); + } + newName.append(Character.toLowerCase(c)); + } + else { + newName.append(c); + } + } + return newName.toString(); + } + + public static String convertString(byte[] data, int offset, int length) { + if (data == null) { + return null; + } + else { + try { + return new String(data, offset, length, CHARSET_UTF8); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public static byte[] convertBytes(String data) { + if (data == null) { + return null; + } + else { + try { + return data.getBytes(CHARSET_UTF8); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public static String numberFormater(int number, int length) { + NumberFormat formatter = NumberFormat.getNumberInstance(); + formatter.setMinimumIntegerDigits(length); + formatter.setGroupingUsed(false); + return formatter.format(number); + } + + static final Pattern reUnicode = Pattern.compile("\\\\u([0-9a-zA-Z]{4})"); + + public static String decode1(String s) { + Matcher m = reUnicode.matcher(s); + StringBuffer sb = new StringBuffer(s.length()); + while (m.find()) { + m.appendReplacement(sb, Character.toString((char) Integer.parseInt(m.group(1), 16))); + } + m.appendTail(sb); + return sb.toString(); + } + + public static String paddingLeft(String src, int len, String value) { + + while (src.length() < len) { + src = value + src; + } + return src; + } + + public static String paddingRight(String src, int len, String value) { + StringBuilder srcBuilder = new StringBuilder(src); + while (srcBuilder.length() < len) { + srcBuilder.append(value); + } + src = srcBuilder.toString(); + return src; + } + + /** + * + * @param value + * @param regex1 + * @param regex2 + * @return + */ + public static Map split(String value, String regex1, String regex2) { + + Map map = new HashMap(); + if (isEmpty(value)) { + return map; + } + + String[] tmp = value.split(regex1); + + for (String v : tmp) { + String[] tmp1 = v.split(regex2); + if (tmp1.length == 2) { + map.put(tmp1[0], tmp1[1]); + } + else if (tmp1.length > 2) { + map.put(tmp1[0], v.substring(v.indexOf(regex2))); + } + else { + System.out.println("数据格式错误:" + v); + } + } + + return map; + } + + public static boolean isEmail(String value) { + if (value == null || (value.length() == 0)) { + return false; + } + + return value.indexOf("@") > -1; + } + + public static boolean isCell(String value) { + + if (value == null || (value.length() == 0)) { + return false; + } + // 1开头、数字、11位 + return value.startsWith("1") && isNumeric(value) && (value.length() == 11); + } + + public static boolean isXml(String value) { + + return value.startsWith(" String2Map(String target, final String split) { + if (target == null || target == "") { + return null; + } + + Map map = new HashMap(); + String[] res = target.split(split); + String[] kv; + for (int i = 0; i < res.length; i++) { + if (res[i] == null || res[i] == "") { + continue; + } + kv = res[i].split("="); + if (kv.length == 2) { + if (!map.containsKey(kv[0])) { + map.put(kv[0], kv[1]); + } + } + else if (kv.length > 2) { + if (!map.containsKey(kv[0])) { + map.put(kv[0], res[i].substring(res[i].indexOf("="))); + } + } + else { + + } + } + return map; + } + + /** + * 将16位byte[] 转换为32位String + * + * @param buffer + * @return String + */ + public static String toHex(byte buffer[]) { + StringBuffer sb = new StringBuffer(buffer.length * 2); + for (int i = 0; i < buffer.length; i++) { + sb.append(Character.forDigit((buffer[i] & 240) >> 4, 16)); + sb.append(Character.forDigit(buffer[i] & 15, 16)); + } + + return sb.toString(); + } + + public static byte[] fromHex(String hex) { + byte[] res = new byte[hex.length() / 2]; + char[] chs = hex.toCharArray(); + for (int i = 0, c = 0; i < chs.length; i += 2, c++) { + res[c] = (byte) (Integer.parseInt(new String(chs, i, 2), 16)); + } + + return res; + } + + private static Pattern ChinesePattern = Pattern.compile("[\u4e00-\u9fa5]"); + + /** + * 判断字符串是否包含中文字符 + * + * @param target + * @return + */ + public static boolean hasChinese(String target) { + return ChinesePattern.matcher(target).find(); + } + + public static String substring(String src, int start, int length) { + + return src.substring(start, start + length); + } + + static String CHARSET_UTF8 = "UTF-8"; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java b/utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java new file mode 100644 index 0000000..52e6f8c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/SystemBarTintManager.java @@ -0,0 +1,97 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.res.TypedArray; +import android.os.Build; +import android.view.*; +import android.widget.FrameLayout; + +public class SystemBarTintManager { + public static final int DEFAULT_TINT_COLOR = 0x99000000; + private boolean mStatusBarAvailable; + private boolean mStatusBarTintEnabled; + private View mStatusBarTintView; + + @TargetApi(19) + public SystemBarTintManager(Activity activity) { + + Window win = activity.getWindow(); + //获取DecorView + ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + // 检查主题中是否有透明的状态栏 + int[] attrs = {android.R.attr.windowTranslucentStatus}; + TypedArray a = activity.obtainStyledAttributes(attrs); + try { + mStatusBarAvailable = a.getBoolean(0, false); + } finally { + a.recycle(); + } + WindowManager.LayoutParams winParams = win.getAttributes(); + int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;//状态栏透明 + if ((winParams.flags & bits) != 0) { + mStatusBarAvailable = true; + } + } + if (mStatusBarAvailable) { + setupStatusBarView(activity, decorViewGroup); + } + } + + /** + * 初始化状态栏 + * + * @param context + * @param decorViewGroup + */ + private void setupStatusBarView(Activity context, ViewGroup decorViewGroup) { + mStatusBarTintView = new View(context); + //设置高度为Statusbar的高度 + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, getStatusBarHeight(context)); + params.gravity = Gravity.TOP; + mStatusBarTintView.setLayoutParams(params); + mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR); + //默认不显示 + mStatusBarTintView.setVisibility(View.GONE); + //decorView添加状态栏高度的View + decorViewGroup.addView(mStatusBarTintView); + } + + /** + * 获取状态栏高度 + * + * @param activity + * @return + */ + private int getStatusBarHeight(Activity activity) { + int statusBarHeight = 0; + int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId); + } + return statusBarHeight; + } + + /** + * 显示状态栏 + */ + public void setStatusBarTintEnabled(boolean enabled) { + mStatusBarTintEnabled = enabled; + if (mStatusBarAvailable) { + mStatusBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + } + + /** + * 设置状态栏颜色 + * + * @param color + */ + public void setStatusBarTintColor(int color) { + if (mStatusBarAvailable) { + mStatusBarTintView.setBackgroundColor(color); + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java b/utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java new file mode 100644 index 0000000..0579e47 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TeminalDevice.java @@ -0,0 +1,346 @@ +package com.lennon.cn.utill.utill; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Build; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.Window; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import com.lennon.cn.utill.base.BaseApplication; + +import java.io.File; +import java.util.List; + +/** + * @author dingyi + */ +@SuppressLint("MissingPermission") +public class TeminalDevice { + /** + * 将dip或dp值转换为px值,保证尺寸大小不变 + * + * @param dipValue + * @return + */ + public static int dip2px(Context context, float dipValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dipValue * scale + 0.5f); + } + + public static float dp2px(float dp) { + return dp * getDisplayMetrics().density; + } + + public static float px2dp(float px) { + return px / getDisplayMetrics().density; + } + + public static DisplayMetrics getDisplayMetrics() { + return BaseApplication.Companion.context().getResources().getDisplayMetrics(); + } + + public static float getScreenHeight() { + return getDisplayMetrics().heightPixels; + } + + public static float getScreenWidth() { + return getDisplayMetrics().widthPixels; + } + + public static int getStatusBarHeight(Activity context) { + Rect rectangle = new Rect(); + Window window = context.getWindow(); + window.getDecorView().getWindowVisibleDisplayFrame(rectangle); + return rectangle.top; + } + + public static boolean hasInternet() { + ConnectivityManager cm = (ConnectivityManager) BaseApplication.Companion.context() + .getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + return info != null && info.isAvailable() && info.isConnected(); + } + + public static boolean isPortrait() { + return BaseApplication.Companion.context().getResources().getConfiguration() + .orientation == Configuration.ORIENTATION_PORTRAIT; + } + + /** + * 打开或关闭键盘 + */ + public static void startOrCloseKeyboard(View view) { + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + // 得到InputMethodManager的实例 + if (imm.isActive()) { + // 如果开启 + imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, + InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + public static void closeKeyboard(EditText view) { + view.clearFocus(); + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN); + } + + public static void openKeyboard(View view) { + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 平板电脑? + * + * @return ?? + */ + public static boolean isTablet() { + int s = BaseApplication.Companion.context().getResources().getConfiguration().screenLayout; + s &= Configuration.SCREENLAYOUT_SIZE_MASK; + return s >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + public static void hideSoftKeyboard(View view) { + if (view == null) { + return; + } + View mFocusView = view; + + Context context = view.getContext(); + if (context != null && context instanceof Activity) { + Activity activity = ((Activity) context); + mFocusView = activity.getCurrentFocus(); + } + if (mFocusView == null) { + return; + } + mFocusView.clearFocus(); + InputMethodManager manager = (InputMethodManager) mFocusView.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + manager.hideSoftInputFromWindow(mFocusView.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + + public static void showSoftKeyboard(View view) { + if (view == null) { + return; + } + view.setFocusable(true); + view.setFocusableInTouchMode(true); + if (!view.isFocused()) { + view.requestFocus(); + } + + InputMethodManager inputMethodManager = (InputMethodManager) view.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + inputMethodManager.showSoftInput(view, 0); + } + + public static void gotoMarket(Context context, String pck) { + if (!isHaveMarket(context)) { + //AppContext.showToast("你手机中没有安装应用市场!"); + return; + } + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("market://details?id=" + pck)); + if (intent.resolveActivity(context.getPackageManager()) != null) { + context.startActivity(intent); + } + } + + public static boolean isHaveMarket(Context context) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_APP_MARKET); + PackageManager pm = context.getPackageManager(); + List infos = pm.queryIntentActivities(intent, 0); + return infos.size() > 0; + } + + public static void openAppInMarket(Context context) { + if (context == null) { + return; + } + String pckName = context.getPackageName(); + gotoMarket(context, pckName); + } + + public static int getVersionCode() { + return getVersionCode(BaseApplication.Companion.context().getPackageName()); + } + + public static int getVersionCode(String packageName) { + try { + return BaseApplication.Companion.context() + .getPackageManager() + .getPackageInfo(packageName, 0) + .versionCode; + } catch (PackageManager.NameNotFoundException ex) { + return 0; + } + } + + public static String getVersionName() { + try { + return BaseApplication + .Companion + .context() + .getPackageManager() + .getPackageInfo(BaseApplication.Companion.context().getPackageName(), 0) + .versionName; + } catch (PackageManager.NameNotFoundException ex) { + return "undefined version name"; + } + } + + //版本名 + public static String getVersionName(Context context) { + return getPackageInfo(context).versionName; + } + + //版本号 + public static int getVersionCode(Context context) { + return getPackageInfo(context).versionCode; + } + + private static PackageInfo getPackageInfo(Context context) { + PackageInfo pi = null; + + try { + PackageManager pm = context.getPackageManager(); + pi = pm.getPackageInfo(context.getPackageName(), + PackageManager.GET_CONFIGURATIONS); + + return pi; + } catch (Exception e) { + e.printStackTrace(); + } + + return pi; + } + + public static void installAPK(Context context, File file) { + if (file == null || !file.exists()) { + return; + } + Intent intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); + context.startActivity(intent); + } + + public static boolean openAppActivity(Context context, + String packageName, + String activityName) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ComponentName cn = new ComponentName(packageName, activityName); + intent.setComponent(cn); + try { + context.startActivity(intent); + return true; + } catch (ActivityNotFoundException e) { + return false; + } + } + + public static boolean isWifiOpen() { + ConnectivityManager cm = (ConnectivityManager) BaseApplication + .Companion.context().getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + if (info == null) { + return false; + } + if (!info.isAvailable() || !info.isConnected()) { + return false; + } + if (info.getType() != ConnectivityManager.TYPE_WIFI) { + return false; + } + return true; + } + + @SuppressWarnings("deprecation") + public static void copyTextToBoard(String string) { + if (TextUtils.isEmpty(string)) { + return; + } + ClipboardManager clip = (ClipboardManager) BaseApplication.Companion.context() + .getSystemService(Context.CLIPBOARD_SERVICE); + clip.setText(string); + //AppContext.showToast(R.string.copy_success); + } + + /** + * 调用系统安装了的应用分享 + * + * @param context + * @param title + * @param url + */ + public static void showSystemShareOption(Activity context, + final String title, final String url) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_SUBJECT, "分享:" + title); + intent.putExtra(Intent.EXTRA_TEXT, title + " " + url); + context.startActivity(Intent.createChooser(intent, "选择分享")); + } + + /** + * 获取设备ID + * + * @return + */ + @SuppressLint("MissingPermission") + public static String getDeviceId() { + TelephonyManager tm = (TelephonyManager) BaseApplication.Companion.context().getSystemService(Context.TELEPHONY_SERVICE); + return tm.getDeviceId(); + } + + public static String getHandSetInfo() { + String handSetInfo = "手机型号:" + Build.MODEL + + "\n系统版本:" + Build.VERSION.RELEASE + + "\n产品型号:" + Build.PRODUCT + + "\n版本显示:" + Build.DISPLAY + + "\n系统定制商:" + Build.BRAND + + "\n设备参数:" + Build.DEVICE + + "\n开发代号:" + Build.VERSION.CODENAME + + "\nSDK版本号:" + Build.VERSION.SDK_INT + + "\nCPU类型:" + Build.CPU_ABI + + "\n硬件类型:" + Build.HARDWARE + + "\n主机:" + Build.HOST + + "\n生产ID:" + Build.ID + + "\nROM制造商:" + Build.MANUFACTURER // 这行返回的是rom定制商的名称 + ; + return handSetInfo; + } + + public static String getPhoneInfo() { + String os = "android," + Build.VERSION.RELEASE; + String model = Build.MODEL.toLowerCase(); + String brand = Build.BRAND; + if (null == brand) { + brand = ""; + } + if (null == model) { + model = "undefined"; + } + String phoneinfo = os + "," + brand + "," + model.toLowerCase(); + return phoneinfo; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java new file mode 100644 index 0000000..45621da --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TimeCountUtil.java @@ -0,0 +1,45 @@ +package com.lennon.cn.utill.utill; + + +import android.app.Activity; +import android.os.CountDownTimer; +import android.widget.Button; +import lennon.com.utill.R; + +/** + * Created by Administrator on 2015/8/25. + */ +public class TimeCountUtil extends CountDownTimer { + + private Activity mActivity; + private Button mBtn; + private boolean mIsEnable = false; + + public TimeCountUtil(Activity activity, long millisInFuture, long countDownInterval, Button button) { + super(millisInFuture, countDownInterval); + this.mActivity = activity; + this.mBtn = button; + this.mIsEnable = true; + } + + @Override + public void onFinish() { + mBtn.setText(mActivity.getResources().getString(R.string.reget_sms)); + mBtn.setEnabled(true); + mBtn.setClickable(true); + mBtn.setTextColor(mActivity.getResources().getColor(R.color.color_ffffff)); + mBtn.setBackground(mBtn.getResources().getDrawable(R.drawable.common_button)); + mIsEnable = true; + } + + @Override + public void onTick(long millisUntilFinished) { + if (mIsEnable) { + mBtn.setEnabled(false); + } + mIsEnable = false; + mBtn.setText(millisUntilFinished / 1000 + mActivity.getResources().getString(R.string.resend_sms)); + mBtn.setTextColor(mActivity.getResources().getColor(R.color.color_333333)); + mBtn.setBackground(mBtn.getResources().getDrawable(R.drawable.common_button_fffff)); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt b/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt new file mode 100644 index 0000000..0100ed8 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/TimeUtill.kt @@ -0,0 +1,34 @@ +package com.lennon.cn.utill.utill + +object TimeUtill { + /** + * @param mss 要转换的毫秒数 + * @return 该毫秒数转换为 * days * hours * minutes * seconds 后的格式 + * @author fy.zhang + */ + fun formatDuring(mss: Long): String { + val days = mss / (1000 * 60 * 60 * 24) + val hours = mss % (1000 * 60 * 60 * 24) / (1000 * 60 * 60) + val minutes = mss % (1000 * 60 * 60) / (1000 * 60) + val seconds = mss % (1000 * 60) / 1000 + val millis = mss % 1000 + var s = "" + if (days > 0) { + s += days.toString() + "天" + } + if (hours > 0) { + s += hours.toString() + "小时" + } + if (minutes > 0) { + s += minutes.toString() + "分钟" + } + if (seconds > 0) { + s += seconds.toString() + "秒" + } + if (millis > 0) { + s += millis.toString() + "毫秒" + } + return s + } + +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt b/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt new file mode 100644 index 0000000..dbd5f54 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/Utill.kt @@ -0,0 +1,311 @@ +package com.lennon.cn.utill.utill + +import android.annotation.SuppressLint +import android.app.ActivityManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.res.Resources +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Build +import android.text.TextUtils +import android.util.DisplayMetrics +import android.view.WindowManager +import com.lennon.cn.utill.dialog.CommonAlertDialog +import com.lennon.cn.utill.dialog.OnAlertDialogListener +import java.io.File +import java.io.UnsupportedEncodingException +import java.util.ArrayList +import com.lennon.cn.utill.conf.Lennon +import android.provider.MediaStore +import android.content.ContentValues +import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.View +import kotlin.math.pow +import kotlin.math.sqrt + + +object Utill { + /** + * 产生图片的路径,带文件夹和文件名,文件名为当前毫秒数 + */ + fun generateImgePath(): String { + val f = File(Lennon.getFilePathName() + File.separator + "images") + if (!f.exists()) { + f.mkdirs() + } + return f.absolutePath + File.separator + System.currentTimeMillis().toString() + ".jpg" + } + /** + * view转bitmap + */ + fun viewConversionBitmap(v: View): Bitmap { + val w = v.width + val h = v.height + + val bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) + val c = Canvas(bmp) + /** 如果不设置canvas画布为白色,则生成透明 */ + + v.layout(0, 0, w, h) + v.draw(c) + + return bmp + } + + fun getImageContentUri(context: Context, imageFile: File): Uri? { + val filePath = imageFile.absolutePath + val cursor = context.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + arrayOf(MediaStore.Images.Media._ID), MediaStore.Images.Media.DATA + "=? ", + arrayOf(filePath), null + ) + if (cursor != null && cursor.moveToFirst()) { + val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)) + val baseUri = Uri.parse("content://media/external/images/media") + return Uri.withAppendedPath(baseUri, "" + id) + } else { + return if (imageFile.exists()) { + val values = ContentValues() + values.put(MediaStore.Images.Media.DATA, filePath) + context.contentResolver.insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + values + ) + } else { + null + } + } + } + + /** + * 获取当前应用程序的包名 + * @param context 上下文对象 + * @return 返回包名 + */ + fun getAppProcessName(context: Context): String { + //当前应用pid + val pid = android.os.Process.myPid() + //任务管理类 + val manager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + //遍历所有应用 + val infos = manager.runningAppProcesses + for (info in infos) { + if (info.pid == pid)//得到当前应用 + return info.processName//返回包名 + } + return "" + } + + + /** + * 获取程序 图标 + * @param context + * @param packname 应用包名 + * @return + */ + fun getAppIcon(context: Context, packname: String): Drawable? { + try { + //包管理操作管理类 + val pm = context.getPackageManager() + //获取到应用信息 + val info = pm.getApplicationInfo(packname, 0) + return info.loadIcon(pm) + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + return null + } + + /** + * 获取程序的版本号 + * @param context + * @param packname + * @return + */ + fun getAppVersion(context: Context, packname: String): String { + //包管理操作管理类 + val pm = context.getPackageManager() + try { + val packinfo = pm.getPackageInfo(packname, 0) + return packinfo.versionName + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return packname + } + + + /** + * 获取程序的名字 + * @param context + * @param packname + * @return + */ + fun getAppName(context: Context, packname: String): String { + //包管理操作管理类 + val pm = context.getPackageManager() + try { + val info = pm.getApplicationInfo(packname, 0) + return info.loadLabel(pm).toString() + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return packname + } + + /* + * 获取程序的权限 + */ + fun getAllPermissions(context: Context, packname: String): Array? { + try { + //包管理操作管理类 + val pm = context.getPackageManager() + val packinfo = pm.getPackageInfo(packname, PackageManager.GET_PERMISSIONS) + //获取到所有的权限 + return packinfo.requestedPermissions + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return null + } + + + /** + * 获取程序的签名 + * @param context + * @param packname + * @return + */ + fun getAppSignature(context: Context, packname: String): String { + try { + //包管理操作管理类 + val pm = context.getPackageManager() + val packinfo = pm.getPackageInfo(packname, PackageManager.GET_SIGNATURES) + //获取当前应用签名 + return packinfo.signatures[0].toCharsString() + + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + + } + return packname + } + + /** + * 获取当前展示 的Activity名称 + * @return + */ +// fun getCurrentActivityName(context: Context): String? { +// val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager +// val runningActivity = activityManager.getRunningTasks(1).get(0).topActivity?.getClassName() +// return runningActivity +// } + + fun makeDir(dirPath: String) { + val file = File(dirPath) + if (!file.exists()) { + file.mkdirs() + } + } + + /** + * 判断是否为平板 + * + * @return + */ + fun isPad(context: Context): Boolean { + val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val display = wm.defaultDisplay + val dm = DisplayMetrics() + display.getMetrics(dm) + val x = (dm.widthPixels / dm.xdpi).toDouble().pow(2.0) + val y = (dm.heightPixels / dm.ydpi).toDouble().pow(2.0) + // 屏幕尺寸 + val screenInches = sqrt(x + y) + // 大于6尺寸则为Pad + return screenInches >= 8.0 + } + + fun toUtf8(str: String): String? { + var result: String? = null + try { + result = String(str.toByteArray(charset("UTF-8")), charset("UTF-8")) + } catch (e: UnsupportedEncodingException) { + e.printStackTrace() + } + + return result + } + + fun getFileName(url: String): String { + val s = url.split("/".toRegex()) + .dropLastWhile { it.isEmpty() } + .toTypedArray() + return s[s.size - 1] + } + + fun StringToStringArray(random: String): List { + val l = ArrayList() + val chars = random.toCharArray() + for (i in chars.indices) { + l.add(chars[i] + "") + } + return l + } + + @Suppress("DEPRECATION") + fun getColor(res: Resources, color: Int): Int { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + return res.getColor(color, res.newTheme()) + } + return res.getColor(color) + } + + fun showTel(context: Context, t: String) { + showTel(context, t, "4001006501") + } + + fun showTel(context: Context, title: String, cell: String) { + val mAlertDialog = CommonAlertDialog(context) + mAlertDialog.setDialogListener(object : OnAlertDialogListener() { + override fun onSure() { + super.onSure() + mAlertDialog.dismiss() + val intent = Intent() + intent.action = Intent.ACTION_CALL + intent.data = Uri.parse("tel:$cell") + context.startActivity(intent) + } + + override fun onCancle() { + super.onCancle() + mAlertDialog.dismiss() + } + }) + mAlertDialog.setTitle( + if (TextUtils.isEmpty(title)) { + "联系客服" + } else { + title + } + ) + mAlertDialog.setSureMsg("立即拨号") + mAlertDialog.setMsg("电话:$cell") + mAlertDialog.show() + } + + + fun isMobileNO(mobiles: String): Boolean { + // mobiles = mobiles.replace(" ", "") + val telRegex = "[1]\\d{10}" //"[1]"代表第1位为数字1,"\\d{9}"代表后面是可以是0~9的数字,有10位。 + return if (TextUtils.isEmpty(mobiles)) false + else mobiles.matches(telRegex.toRegex()) + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java b/utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java new file mode 100644 index 0000000..e5285fd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/UtillString.java @@ -0,0 +1,9 @@ +package com.lennon.cn.utill.utill; + +/** + * Created by lennon on 2017/5/5. + */ + +public class UtillString { + public static final String SAMPLE_DIR_NAME = "hmgj"; +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java b/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java new file mode 100644 index 0000000..466a1a8 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/VersionUtill.java @@ -0,0 +1,141 @@ +package com.lennon.cn.utill.utill; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.provider.Settings; +import android.telephony.TelephonyManager; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; + +import androidx.core.app.ActivityCompat; + +import com.lennon.cn.utill.base.BaseApplication; + +/** + * 作者:11361 on 2019/1/24 10:07 + *

+ * 邮箱:1136160757@qq.com + */ +public class VersionUtill { + @SuppressLint({"WifiManagerLeak", "MissingPermission", "HardwareIds"}) + public static String getAndroidId(Context context) { + int REQUEST_EXTERNAL_STORAGE = 1; + String[] PERMISSIONS_STORAGE = { + Manifest.permission.BLUETOOTH, + Manifest.permission.ACCESS_WIFI_STATE, + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }; + int permission = ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE); + + if (permission != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.ACCESS_WIFI_STATE) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(context, + Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { + // We don't have permission so prompt the user + if (context instanceof Activity) { + ActivityCompat.requestPermissions( + (Activity) context, + PERMISSIONS_STORAGE, + REQUEST_EXTERNAL_STORAGE + ); + }else if (BaseApplication.Companion.getCuttureActivity()!=null){ + ActivityCompat.requestPermissions( + BaseApplication.Companion.getCuttureActivity(), + PERMISSIONS_STORAGE, + REQUEST_EXTERNAL_STORAGE + ); + } + } + BluetoothAdapter m_BluetoothAdapter = null; // Local Bluetooth adapter + m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + String m_szBTMAC = ""; + if (m_BluetoothAdapter != null) { + m_szBTMAC = m_BluetoothAdapter.getAddress();//蓝牙MAC地址 + } + WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + String m_szWLANMAC = wm.getConnectionInfo().getMacAddress();//wifi MAC地址 + String m_szDevIDShort = "35" + //we make this look like a valid IMEI + Build.BOARD.length() % 10 +//主板编号 + Build.BRAND.length() % 10 +//系统定制商 + Build.CPU_ABI.length() % 10 +//cpu指令集 + Build.DEVICE.length() % 10 +//设备参数 + Build.DISPLAY.length() % 10 +//显示屏参数 + Build.HOST.length() % 10 + + Build.ID.length() % 10 +//修订版本列表 + Build.MANUFACTURER.length() % 10 +//硬件制造商 + Build.MODEL.length() % 10 +//版本即最终用户可见的名称 + Build.PRODUCT.length() % 10 +//整个产品的名称 + Build.TAGS.length() % 10 +//描述build的标签,如未签名,debug等等 + Build.TYPE.length() % 10 +//build的类型 + Build.USER.length() % 10; //13 digits + String m_szAndroidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); + TelephonyManager TelephonyMgr = (TelephonyManager) context.getSystemService(Activity.TELEPHONY_SERVICE); + String m_szImei = TelephonyMgr.getDeviceId(); + String m_szLongID = m_szImei + m_szDevIDShort + + m_szAndroidID + m_szWLANMAC + m_szBTMAC; +// compute md5 + MessageDigest m = null; + try { + m = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + m.update(m_szLongID.getBytes(), 0, m_szLongID.length()); +// get md5 bytes + byte p_md5Data[] = m.digest(); +// create a hex string + StringBuilder m_szUniqueID = new StringBuilder(); + for (int i = 0; i < p_md5Data.length; i++) { + int b = (0xFF & p_md5Data[i]); +// if it is a single digit, make sure it have 0 in front (proper padding) + if (b <= 0xF) { + m_szUniqueID.append("0"); + } +// add number to string + m_szUniqueID.append(Integer.toHexString(b)); + } // hex string to uppercase + m_szUniqueID = new StringBuilder(m_szUniqueID.toString().toUpperCase()); + return m_szUniqueID.toString(); + } + + @SuppressLint("MissingPermission") + public static String getUUID() { + return UUID.randomUUID().toString().replace("-", ""); + } + + + //版本名 + public static String getVersionName(Context context) { + return getPackageInfo(context).versionName; + } + + //版本号 + public static int getVersionCode(Context context) { + return getPackageInfo(context).versionCode; + } + + private static PackageInfo getPackageInfo(Context context) { + PackageInfo pi = null; + try { + PackageManager pm = context.getPackageManager(); + pi = pm.getPackageInfo(context.getPackageName(), + PackageManager.GET_CONFIGURATIONS); + return pi; + } catch (Exception e) { + e.printStackTrace(); + } + return pi; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java new file mode 100644 index 0000000..e16c7bb --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtil.java @@ -0,0 +1,166 @@ +package com.lennon.cn.utill.utill; + +import java.io.*; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +public class ZipUtil { + public static File zip(String src, String dest) throws IOException { + //提供了一个数据项压缩成一个ZIP归档输出流 + ZipOutputStream out = null; + try { + + File outFile = new File(dest);//源文件或者目录 + File fileOrDirectory = new File(src);//压缩文件路径 + out = new ZipOutputStream(new FileOutputStream(outFile)); + //如果此文件是一个文件,否则为false。 + if (fileOrDirectory.isFile()) { + zipFileOrDirectory(out, fileOrDirectory, ""); + } else { + //返回一个文件或空阵列。 + File[] entries = fileOrDirectory.listFiles(); + for (int i = 0; i < entries.length; i++) { + // 递归压缩,更新curPaths + zipFileOrDirectory(out, entries[i], ""); + } + } + return outFile; + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + //关闭输出流 + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + return null; + } + + private static void zipFileOrDirectory(ZipOutputStream out, + File fileOrDirectory, String curPath) throws IOException { + //从文件中读取字节的输入流 + FileInputStream in = null; + try { + //如果此文件是一个目录,否则返回false。 + if (!fileOrDirectory.isDirectory()) { + // 压缩文件 + byte[] buffer = new byte[4096]; + int bytes_read; + in = new FileInputStream(fileOrDirectory); + //实例代表一个条目内的ZIP归档 + ZipEntry entry = new ZipEntry(curPath + + fileOrDirectory.getName()); + //条目的信息写入底层流 + out.putNextEntry(entry); + while ((bytes_read = in.read(buffer)) != -1) { + out.write(buffer, 0, bytes_read); + } + out.closeEntry(); + } else { + // 压缩目录 + File[] entries = fileOrDirectory.listFiles(); + for (int i = 0; i < entries.length; i++) { + // 递归压缩,更新curPaths + zipFileOrDirectory(out, entries[i], curPath + + fileOrDirectory.getName() + "/"); + } + } + } catch (IOException ex) { + ex.printStackTrace(); + // throw ex; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + } + + @SuppressWarnings("unchecked") + public static void unzip(String zipFileName, String outputDirectory) + throws IOException { + ZipFile zipFile = null; + try { + zipFile = new ZipFile(zipFileName); + Enumeration e = zipFile.entries(); + ZipEntry zipEntry = null; + File dest = new File(outputDirectory); + dest.mkdirs(); + while (e.hasMoreElements()) { + zipEntry = (ZipEntry) e.nextElement(); + String entryName = zipEntry.getName(); + InputStream in = null; + FileOutputStream out = null; + try { + if (zipEntry.isDirectory()) { + String name = zipEntry.getName(); + name = name.substring(0, name.length() - 1); + File f = new File(outputDirectory + File.separator + + name); + f.mkdirs(); + } else { + int index = entryName.lastIndexOf("\\"); + if (index != -1) { + File df = new File(outputDirectory + File.separator + + entryName.substring(0, index)); + df.mkdirs(); + } + index = entryName.lastIndexOf("/"); + if (index != -1) { + File df = new File(outputDirectory + File.separator + + entryName.substring(0, index)); + df.mkdirs(); + } + File f = new File(outputDirectory + File.separator + + zipEntry.getName()); + // f.createNewFile(); + in = zipFile.getInputStream(zipEntry); + out = new FileOutputStream(f); + int c; + byte[] by = new byte[1024]; + while ((c = in.read(by)) != -1) { + out.write(by, 0, c); + } + out.flush(); + } + } catch (IOException ex) { + ex.printStackTrace(); + throw new IOException("解压失败:" + ex.toString()); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + } + } + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + } + } + } + } + } catch (IOException ex) { + ex.printStackTrace(); + throw new IOException("解压失败:" + ex.toString()); + } finally { + if (zipFile != null) { + try { + zipFile.close(); + } catch (IOException ex) { + } + } + } + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java new file mode 100644 index 0000000..59ff70a --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/ZipUtils.java @@ -0,0 +1,163 @@ +package com.lennon.cn.utill.utill; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class ZipUtils { + private static final int BUFFER_SIZE = 2 * 1024; + + + + /** + + * 压缩成ZIP 方法1 + + * @param srcDir 压缩文件夹路径 + + * @param out 压缩文件输出流 + + * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构; + + * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败) + + * @throws RuntimeException 压缩失败会抛出运行时异常 + + */ + + public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure) + + throws RuntimeException{ + + long start = System.currentTimeMillis(); + + ZipOutputStream zos = null ; + + try { + + zos = new ZipOutputStream(out); + + File sourceFile = new File(srcDir); + + compress(sourceFile,zos,sourceFile.getName(),KeepDirStructure); + long end = System.currentTimeMillis(); + System.out.println("压缩完成,耗时:" + (end - start) +" ms"); + } catch (Exception e) { + throw new RuntimeException("zip error from ZipUtils",e); + }finally{ + if(zos != null){ + try { + zos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + /** + * 压缩成ZIP 方法2 + * @param srcFiles 需要压缩的文件列表 + * @param out 压缩文件输出流 + * @throws RuntimeException 压缩失败会抛出运行时异常 + */ + public static void toZip(List srcFiles , OutputStream out)throws RuntimeException { + long start = System.currentTimeMillis(); + ZipOutputStream zos = null ; + try { + zos = new ZipOutputStream(out); + for (File srcFile : srcFiles) { + byte[] buf = new byte[BUFFER_SIZE]; + zos.putNextEntry(new ZipEntry(srcFile.getName())); + int len; + FileInputStream in = new FileInputStream(srcFile); + while ((len = in.read(buf)) != -1){ + zos.write(buf, 0, len); + } + zos.closeEntry(); + in.close(); + } + long end = System.currentTimeMillis(); + System.out.println("压缩完成,耗时:" + (end - start) +" ms"); + } catch (Exception e) { + throw new RuntimeException("zip error from ZipUtils",e); + }finally{ + if(zos != null){ + try { + zos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * 递归压缩方法 + * @param sourceFile 源文件 + * @param zos zip输出流 + * @param name 压缩后的名称 + * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构; + * false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败) + * @throws Exception + */ + private static void compress(File sourceFile, ZipOutputStream zos, String name, + boolean KeepDirStructure) throws Exception{ + byte[] buf = new byte[BUFFER_SIZE]; + if(sourceFile.isFile()){ + // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字 + zos.putNextEntry(new ZipEntry(name)); + // copy文件到zip输出流中 + int len; + FileInputStream in = new FileInputStream(sourceFile); + while ((len = in.read(buf)) != -1){ + zos.write(buf, 0, len); + } + // Complete the entry + zos.closeEntry(); + in.close(); + } else { + File[] listFiles = sourceFile.listFiles(); + if(listFiles == null || listFiles.length == 0){ + // 需要保留原来的文件结构时,需要对空文件夹进行处理 + if(KeepDirStructure){ + // 空文件夹的处理 + zos.putNextEntry(new ZipEntry(name + "/")); + // 没有文件,不需要文件的copy + zos.closeEntry(); + } + + }else { + for (File file : listFiles) { + // 判断是否需要保留原来的文件结构 + if (KeepDirStructure) { + // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠, + // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了 + compress(file, zos, name + "/" + file.getName(),KeepDirStructure); + } else { + compress(file, zos, file.getName(),KeepDirStructure); + } + + } + } + } + } + +// public static void main(String[] args) throws Exception { +// /** 测试压缩方法1 */ +// FileOutputStream fos1 = new FileOutputStream(new File("c:/mytest01.zip")); +// ZipUtils.toZip("D:/log", fos1,true); +// +// /** 测试压缩方法2 */ +// List fileList = new ArrayList<>(); +// fileList.add(new File("D:/Java/jdk1.7.0_45_64bit/bin/jar.exe")); +// fileList.add(new File("D:/Java/jdk1.7.0_45_64bit/bin/java.exe")); +// FileOutputStream fos2 = new FileOutputStream(new File("c:/mytest02.zip")); +// ZipUtils.toZip(fileList, fos2); +// } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java b/utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java new file mode 100644 index 0000000..605464d --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/photo/PhotoString.java @@ -0,0 +1,20 @@ +package com.lennon.cn.utill.utill.photo; + +import com.lennon.cn.utill.bean.StringBean; + +public class PhotoString implements StringBean { + private String name; + + @Override + public String getItemString() { + return name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java new file mode 100644 index 0000000..f33b98b --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/photo/SelectPicUtil.java @@ -0,0 +1,254 @@ +package com.lennon.cn.utill.utill.photo; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.MediaStore; +import android.text.TextUtils; +import android.widget.Toast; + + +import androidx.core.content.FileProvider; + +import com.lennon.cn.utill.conf.Lennon; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class SelectPicUtil { + + private static final String temp = Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.jpg"; + + public static final int GET_BY_ALBUM = 801;// 打开相册 + public static final int GET_BY_CAMERA = 802;// 打开相机 + public static final int CROP = 803;// 裁剪图片 + + public static void getByAlbum(Activity act) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + Intent intent = new Intent(Intent.ACTION_PICK, + android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); + act.startActivityForResult(intent, GET_BY_ALBUM); + } else { + Intent intentFromGallery = new Intent(); + intentFromGallery.setType("image/*"); + intentFromGallery.setAction(Intent.ACTION_GET_CONTENT); + act.startActivityForResult(intentFromGallery, GET_BY_ALBUM); + } + } + + public static void getByCamera(Activity act) { + getByCamera(act, temp, GET_BY_CAMERA); + } + + // 给其他地方用 + public static void getByCamera(Activity act, String path, int requestCode) { + String state = Environment.getExternalStorageState(); + if (state.equals(Environment.MEDIA_MOUNTED)) { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + Uri uri = getUri(act, new File(path)); + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri); + takePictureIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); + act.startActivityForResult(takePictureIntent, requestCode); + } else { + Toast.makeText(act, "请安装sd卡", Toast.LENGTH_SHORT).show(); + } + } + + public static Uri onActivityResult(Activity act, int requestCode, + int resultCode, Intent data) { + return onActivityResult(act, requestCode, resultCode, data, 0, 0, 0, 0, false); + } + + public static Uri onActivityResult(Activity act, int requestCode, + int resultCode, Intent data, int w, int h, int aspectX, int aspectY) { + return onActivityResult(act, requestCode, resultCode, data, w, h, aspectX, aspectY, true); + } + + public static Uri onActivityResult(Activity act, int requestCode, + int resultCode, Intent data, int outputX, int outputY, int aspectX, int aspectY, boolean isCut) { + if (resultCode == Activity.RESULT_OK) { + Uri uri = null; + switch (requestCode) { + case GET_BY_ALBUM: + if (isCut) { + if (null == data) return null; + String path = getImageAbsolutePath(act, data.getData()); + if (TextUtils.isEmpty(path)) return null; + uri = getUri(act, new File(path)); + } else { + return data.getData(); + } + break; + case GET_BY_CAMERA: + if (isCut) { + uri = getUri(act, new File(temp)); + } else { + return Uri.parse(temp); + } + break; + case CROP: +// return getUri(act, new File(temp)); + return Uri.fromFile(new File(temp)); + } + if (isCut && null != uri) { + crop(act, uri, outputX, outputY, aspectX, aspectY); + return null; + } + } + return null; + } + + public static void crop(Activity act, Uri uri, int outputX, int outputY, int aspectX, int aspectY) { + if (outputX == 0 && outputY == 0) { + outputX = outputY = 480; + } + if (aspectX == 0 && aspectY == 0) { + aspectX = aspectY = 1; + } + Intent intent = new Intent("com.android.camera.action.CROP"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + //对目标应用临时授权该Uri所代表的文件 + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + intent.setDataAndType(uri, "image/*"); + intent.putExtra("crop", "true"); + intent.putExtra("aspectX", aspectX); + intent.putExtra("aspectY", aspectY); + intent.putExtra("outputX", outputX); + intent.putExtra("outputY", outputY); + +// intent.putExtra(MediaStore.EXTRA_OUTPUT, getUri(act, new File(temp))); + intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(temp))); + + intent.putExtra("outputFormat", "JPEG"); + + intent.putExtra("noFaceDetection", true); + intent.putExtra("return-data", false); + act.startActivityForResult(intent, CROP); + } + + public static String getImageAbsolutePath(Activity context, Uri uri) { + if (null == uri) + return null; + final String scheme = uri.getScheme(); + String data = null; + if (scheme == null) + data = uri.getPath(); + else if (ContentResolver.SCHEME_FILE.equals(scheme)) { + data = uri.getPath(); + } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { + Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, + null, null); + if (null != cursor) { + if (cursor.moveToFirst()) { + int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); + if (index > -1) { + data = cursor.getString(index); + } + } + cursor.close(); + } + } + return data; + } + + + public static Uri getUri(Context context, File file) { + Uri fileUri; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // BuildConfig 是工程包路径下的:如:com.yingyou.demo.BuildConfig + fileUri = FileProvider.getUriForFile(context, Lennon.Companion.getFileProvide(), file); + } else { + fileUri = Uri.fromFile(file); + } + + return fileUri; + } + + public static Bitmap getImageThumbnail(String imagePath, int width, + int height) { + Bitmap bitmap = null; + BitmapFactory.Options options = new BitmapFactory.Options(); + // Options中有个属性inJustDecodeBounds。我们可以充分利用它,来避免大图片的溢出问题 + options.inJustDecodeBounds = true;// 设置为true可以不加载到内存,直接获取Bitmap宽高 + // 获取这个图片的宽和高,注意此处的bitmap为null + bitmap = BitmapFactory.decodeFile(imagePath, options); + if (bitmap == null) { + // 计算缩放比 + int h = options.outHeight;// 获取Bitmap的实际高度 + int w = options.outWidth;// 获取Bitmap的实际宽度 + + int beWidth = w / width; + int beHeight = h / height; + int rate = 1; + if (beWidth < beHeight) { + rate = beWidth; + } else { + rate = beHeight; + } + if (rate <= 0) {// 图片实际大小小于缩略图,不缩放 + rate = 1; + } + options.inSampleSize = rate;// rate就是压缩的比例 + options.inJustDecodeBounds = false; + // 重新读入图片,读取缩放后的bitmap,注意这次要把options.inJustDecodeBounds 设为 false + bitmap = BitmapFactory.decodeFile(imagePath, options);// 获取压缩后的图片 + } + return bitmap; + } + + public static Bitmap getBitmapFormUri(Activity ac, Uri uri) { + InputStream input; + try { + input = ac.getContentResolver().openInputStream(uri); + BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); + onlyBoundsOptions.inJustDecodeBounds = true; + onlyBoundsOptions.inDither = true;// optional + onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// optional + BitmapFactory.decodeStream(input, null, onlyBoundsOptions); + input.close(); + int originalWidth = onlyBoundsOptions.outWidth; + int originalHeight = onlyBoundsOptions.outHeight; + if ((originalWidth == -1) || (originalHeight == -1)) + return null; + // 图片分辨率以480x800为标准 + float hh = 800f;// 这里设置高度为800f + float ww = 480f;// 这里设置宽度为480f + // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 + int be = 1;// be=1表示不缩放 + if (originalWidth > originalHeight && originalWidth > ww) {// 如果宽度大的话根据宽度固定大小缩放 + be = (int) (originalWidth / ww); + } else if (originalWidth < originalHeight && originalHeight > hh) {// 如果高度高的话根据宽度固定大小缩放 + be = (int) (originalHeight / hh); + } + if (be <= 0) + be = 1; + // 比例压缩 + BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); + bitmapOptions.inSampleSize = be;// 设置缩放比例 + bitmapOptions.inDither = true;// optional + bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;// optional + input = ac.getContentResolver().openInputStream(uri); + Bitmap bitmap = BitmapFactory.decodeStream(input, null, + bitmapOptions); + input.close(); + return bitmap;// 再进行质量压缩 + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java new file mode 100644 index 0000000..9d2a4cd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSACoder.java @@ -0,0 +1,258 @@ +package com.lennon.cn.utill.utill.rsa; + +import android.util.Base64; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashMap; +import java.util.Map; + + +/** + * Created by humf.需要依赖 commons-codec 包 + */ +public class RSACoder { + public static final String KEY_ALGORITHM = "RSA"; + public static final String SIGNATURE_ALGORITHM = "MD5withRSA"; + + private static final String PUBLIC_KEY = "RSAPublicKey"; + private static final String PRIVATE_KEY = "RSAPrivateKey"; + + public static byte[] decryptBASE64(String key) { + return Base64.decode(key, Base64.DEFAULT); + } + + public static String encryptBASE64(byte[] bytes) { + return Base64.encodeToString(bytes, Base64.DEFAULT); + } + + /** + * 用私钥对信息生成数字签名 + * + * @param data 加密数据 + * @param privateKey 私钥 + * @return + * @throws Exception + */ + public static String sign(byte[] data, String privateKey) throws Exception { + // 解密由base64编码的私钥 + byte[] keyBytes = decryptBASE64(privateKey); + // 构造PKCS8EncodedKeySpec对象 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + // KEY_ALGORITHM 指定的加密算法 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + // 取私钥匙对象 + PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec); + // 用私钥对信息生成数字签名 + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initSign(priKey); + signature.update(data); + return encryptBASE64(signature.sign()); + } + + /** + * 校验数字签名 + * + * @param data 加密数据 + * @param publicKey 公钥 + * @param sign 数字签名 + * @return 校验成功返回true 失败返回false + * @throws Exception + */ + public static boolean verify(byte[] data, String publicKey, String sign) + throws Exception { + // 解密由base64编码的公钥 + byte[] keyBytes = decryptBASE64(publicKey); + // 构造X509EncodedKeySpec对象 + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + // KEY_ALGORITHM 指定的加密算法 + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + // 取公钥匙对象 + PublicKey pubKey = keyFactory.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM); + signature.initVerify(pubKey); + signature.update(data); + // 验证签名是否正常 + return signature.verify(decryptBASE64(sign)); + } + + /** + * 私钥解密密文 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + // 对数据解密 + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 解密
+ * 用私钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPrivateKey(String data, String key) + throws Exception { + return decryptByPrivateKey(decryptBASE64(data), key); + } + + /** + * 解密
+ * 用公钥解密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] decryptByPublicKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得公钥 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicKey = keyFactory.generatePublic(x509KeySpec); + // 对数据解密 + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + return cipher.doFinal(data); + } + + /** + * 加密
+ * 用公钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encryptByPublicKey(String data, String key) + throws Exception { + // 对公钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得公钥 + X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key publicKey = keyFactory.generatePublic(x509KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(data.getBytes()); + } + + /** + * 加密
+ * 用私钥加密 + * + * @param data + * @param key + * @return + * @throws Exception + */ + public static byte[] encryptByPrivateKey(byte[] data, String key) + throws Exception { + // 对密钥解密 + byte[] keyBytes = decryptBASE64(key); + // 取得私钥 + PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM); + Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec); + // 对数据加密 + Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 取得私钥 + * + * @param keyMap + * @return + * @throws Exception + */ + public static String getPrivateKey(Map keyMap) + throws Exception { + Key key = (Key) keyMap.get(PRIVATE_KEY); + return encryptBASE64(key.getEncoded()); + } + + /** + * 取得公钥 + * + * @param keyMap + * @return + * @throws Exception + */ + public static String getPublicKey(Map keyMap) + throws Exception { + Key key = keyMap.get(PUBLIC_KEY); + return encryptBASE64(key.getEncoded()); + } + + /** + * 初始化密钥 + * + * @return + * @throws Exception + */ + public static Map initKey() throws Exception { + KeyPairGenerator keyPairGen = KeyPairGenerator + .getInstance(KEY_ALGORITHM); + keyPairGen.initialize(1024); + KeyPair keyPair = keyPairGen.generateKeyPair(); + Map keyMap = new HashMap(2); + keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥 + keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥 + return keyMap; + } + + public static void main(String[] args) throws Exception { + Map keyMap = initKey(); + String publicKey = getPublicKey(keyMap); + String privateKey = getPrivateKey(keyMap); + + System.out.println(keyMap); + System.out.println("-----------------------------------"); + System.out.println(publicKey); + System.out.println("-----------------------------------"); + System.out.println(privateKey); + System.out.println("-----------------------------------"); + byte[] encryptByPrivateKey = encryptByPrivateKey("123456".getBytes(), privateKey); + byte[] encryptByPublicKey = encryptByPublicKey("123456", publicKey); + System.out.println(new String(encryptByPrivateKey)); + System.out.println("-----------------------------------"); + System.out.println(new String(encryptByPublicKey)); + System.out.println("-----------------------------------"); + String sign = sign(encryptByPrivateKey, privateKey); + System.out.println(sign); + System.out.println("-----------------------------------"); + boolean verify = verify(encryptByPrivateKey, publicKey, sign); + System.out.println(verify); + System.out.println("-----------------------------------"); + byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey); + byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey); + System.out.println(new String(decryptByPublicKey)); + System.out.println("-----------------------------------"); + System.out.println(new String(decryptByPrivateKey)); + + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java new file mode 100644 index 0000000..d4a9199 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAKeyUtil.java @@ -0,0 +1,92 @@ +package com.lennon.cn.utill.utill.rsa; + +import android.os.Build; +import android.util.Base64; + +import java.security.*; +import java.util.Arrays; + +import androidx.annotation.RequiresApi; + +public class RSAKeyUtil { + + @RequiresApi(api = Build.VERSION_CODES.FROYO) + public static RSAKey createKeyPairsStr() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(512, new SecureRandom()); + KeyPair pair = generator.generateKeyPair(); + PublicKey pubKey = pair.getPublic(); + PrivateKey privKey = pair.getPrivate(); + byte[] publicKeyByte = pubKey.getEncoded(); + byte[] privateKeyByte = privKey.getEncoded(); + String publicKeyStr = Base64.encodeToString(publicKeyByte, Base64.DEFAULT); + String privateKeyStr = Base64.encodeToString(privateKeyByte, Base64.DEFAULT); + + System.out.println("公钥:" + Arrays.toString(publicKeyByte)); + System.out.println("私钥:" + Arrays.toString(privateKeyByte)); + System.out.println("公钥Base64编码:" + publicKeyStr); + System.out.println("私钥Base64编码:" + privateKeyStr); + + return RSAKey.builder().addPublicKeyStr(publicKeyStr).addPrivateKeyStr(privateKeyStr).build(); + } + + public static class RSAKey { + private String publicKeyStr; + private String privateKeyStr; + + private RSAKey(RSAKeyBuilder rSAKeyBuilder) { + this.publicKeyStr = rSAKeyBuilder.publicKeyStr; + this.privateKeyStr = rSAKeyBuilder.privateKeyStr; + } + + public String getPublicKeyStr() { + return publicKeyStr; + } + + public void setPublicKeyStr(String publicKeyStr) { + this.publicKeyStr = publicKeyStr; + } + + public String getPrivateKeyStr() { + return privateKeyStr; + } + + public void setPrivateKeyStr(String privateKeyStr) { + this.privateKeyStr = privateKeyStr; + } + + /** + * Creates builder to build {@link RSAKey}. + * + * @return created builder + */ + public static RSAKeyBuilder builder() { + return new RSAKeyBuilder(); + } + + /** + * Builder to build {@link RSAKey}. + */ + public static final class RSAKeyBuilder { + private String publicKeyStr; + private String privateKeyStr; + + private RSAKeyBuilder() { + } + + public RSAKeyBuilder addPublicKeyStr(String publicKeyStr) { + this.publicKeyStr = publicKeyStr; + return this; + } + + public RSAKeyBuilder addPrivateKeyStr(String privateKeyStr) { + this.privateKeyStr = privateKeyStr; + return this; + } + + public RSAKey build() { + return new RSAKey(this); + } + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java new file mode 100644 index 0000000..0c5fc2e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rsa/RSAUtils.java @@ -0,0 +1,373 @@ +package com.lennon.cn.utill.utill.rsa; + +import android.util.Base64; + +import javax.crypto.Cipher; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + + +public class RSAUtils { + private static final String ALGORITHM = "RSA"; + private static final String TRANSFORMATION = "RSA"; + + /** + * 从文件中输入流中加载公钥 + * + * @param in 公钥输入流 + * @throws Exception 加载公钥时产生的异常 + */ + public static RSAPublicKey loadPublicKey(InputStream in) throws Exception { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String readLine = null; + StringBuilder sb = new StringBuilder(); + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } else { + sb.append(readLine); + sb.append('\r'); + } + } + return loadPublicKey(sb.toString()); + } catch (IOException e) { + throw new Exception("公钥数据流读取错误"); + } catch (NullPointerException e) { + throw new Exception("公钥输入流为空"); + } + } + + /** + * 从字符串中加载公钥 + * + * @param publicKeyStr 公钥数据字符串 + * @return + * @throws Exception 加载公钥时产生的异常 + */ + public static RSAPublicKey loadPublicKey(String publicKeyStr) + throws Exception { + try { + byte[] buffer = Base64.decode(publicKeyStr, Base64.DEFAULT); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("公钥非法"); + } catch (NullPointerException e) { + throw new Exception("公钥数据为空"); + } + } + + /** + * 从文件中加载私钥 + * + * @param in 私钥输入流 + * @return + * @throws Exception + */ + public static RSAPrivateKey loadPrivateKey(InputStream in) throws Exception { + try { + BufferedReader br = new BufferedReader(new InputStreamReader(in)); + String readLine = null; + StringBuilder sb = new StringBuilder(); + while ((readLine = br.readLine()) != null) { + if (readLine.charAt(0) == '-') { + continue; + } else { + sb.append(readLine); + sb.append('\r'); + } + } + return loadPrivateKey(sb.toString()); + } catch (IOException e) { + throw new Exception("私钥数据读取错误"); + } catch (NullPointerException e) { + throw new Exception("私钥输入流为空"); + } + } + + /** + * 从字符串中加载私钥 + * + * @param privateKeyStr 私钥字符串 + * @return + * @throws Exception + * @desc + */ + public static RSAPrivateKey loadPrivateKey(String privateKeyStr) + throws Exception { + try { + byte[] buffer = Base64.decode(privateKeyStr, Base64.DEFAULT); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer); + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new Exception("无此算法"); + } catch (InvalidKeySpecException e) { + throw new Exception("私钥非法"); + } catch (NullPointerException e) { + throw new Exception("私钥数据为空"); + } + } + + /** + * 公钥加密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + */ + public static String encryptByPublicKey(String data, RSAPublicKey publicKey) + throws Exception { + // 模长 + int key_len = publicKey.getModulus().bitLength() / 8; + // 加密数据长度 <= 模长-11 + String[] datas = splitString(data, key_len - 11); + String mi = ""; + // 如果明文长度大于模长-11则要分组加密 + for (String s : datas) { + mi += bcd2Str(encryptByPublicKey(s.getBytes(), publicKey)); + } + return mi; + } + + /** + * 公钥加密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + * @desc + */ + public static byte[] encryptByPublicKey(byte[] data, RSAPublicKey publicKey) + throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return cipher.doFinal(data); + } + + /** + * 私钥加密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + * @desc + */ + public static byte[] encryptByPrivateKey(byte[] data, + RSAPrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + + /** + * 私钥加密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + * @desc + */ + public static String encryptByPrivateKey(String data, + RSAPrivateKey privateKey) throws Exception { + // 模长 + int key_len = privateKey.getModulus().bitLength() / 8; + // 加密数据长度 <= 模长-11 + String[] datas = splitString(data, key_len - 11); + String mi = ""; + // 如果明文长度大于模长-11则要分组加密 + for (String s : datas) { + mi += bcd2Str(encryptByPrivateKey(s.getBytes(), privateKey)); + } + return mi; + } + + /** + * 私钥解密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + */ + public static String decryptByPrivateKey(String data, + RSAPrivateKey privateKey) throws Exception { + // 模长 + int key_len = privateKey.getModulus().bitLength() / 8; + byte[] bytes = data.getBytes(); + byte[] bcd = ASCII_To_BCD(bytes, bytes.length); + // 如果密文长度大于模长则要分组解密 + String ming = ""; + byte[][] arrays = splitArray(bcd, key_len); + for (byte[] arr : arrays) { + ming += new String(decryptByPrivateKey(arr, privateKey)); + } + return ming; + } + + /** + * 私钥解密 + * + * @param data + * @param privateKey + * @return + * @throws Exception + * @desc + */ + public static byte[] decryptByPrivateKey(byte[] data, + RSAPrivateKey privateKey) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + return cipher.doFinal(data); + } + + /** + * 公钥解密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + * @desc + */ + public static String decryptByPublicKey(String data, + RSAPublicKey publicKey) throws Exception { + // 模长 + int key_len = publicKey.getModulus().bitLength() / 8; + byte[] bytes = data.getBytes(); + byte[] bcd = ASCII_To_BCD(bytes, bytes.length); + // 如果密文长度大于模长则要分组解密 + String ming = ""; + byte[][] arrays = splitArray(bcd, key_len); + for (byte[] arr : arrays) { + ming += new String(decryptByPublicKey(arr, publicKey)); + } + return ming; + } + + /** + * 公钥解密 + * + * @param data + * @param publicKey + * @return + * @throws Exception + * @desc + */ + public static byte[] decryptByPublicKey(byte[] data, + RSAPublicKey publicKey) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + return cipher.doFinal(data); + } + + /** + * ASCII码转BCD码 + */ + private static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) { + byte[] bcd = new byte[asc_len / 2]; + int j = 0; + for (int i = 0; i < (asc_len + 1) / 2; i++) { + bcd[i] = asc_to_bcd(ascii[j++]); + bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4)); + } + return bcd; + } + + private static byte asc_to_bcd(byte asc) { + byte bcd; + + if ((asc >= '0') && (asc <= '9')) { + bcd = (byte) (asc - '0'); + } else if ((asc >= 'A') && (asc <= 'F')) { + bcd = (byte) (asc - 'A' + 10); + } else if ((asc >= 'a') && (asc <= 'f')) { + bcd = (byte) (asc - 'a' + 10); + } else { + bcd = (byte) (asc - 48); + } + return bcd; + } + + /** + * BCD转字符串 + */ + private static String bcd2Str(byte[] bytes) { + char temp[] = new char[bytes.length * 2], val; + + for (int i = 0; i < bytes.length; i++) { + val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f); + temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); + + val = (char) (bytes[i] & 0x0f); + temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0'); + } + return new String(temp); + } + + /** + * 拆分字符串 + */ + private static String[] splitString(String string, int len) { + int x = string.length() / len; + int y = string.length() % len; + int z = 0; + if (y != 0) { + z = 1; + } + String[] strings = new String[x + z]; + String str = ""; + for (int i = 0; i < x + z; i++) { + if (i == x + z - 1 && y != 0) { + str = string.substring(i * len, i * len + y); + } else { + str = string.substring(i * len, i * len + len); + } + strings[i] = str; + } + return strings; + } + + /** + * 拆分数组 + */ + private static byte[][] splitArray(byte[] data, int len) { + int x = data.length / len; + int y = data.length % len; + int z = 0; + if (y != 0) { + z = 1; + } + byte[][] arrays = new byte[x + z][]; + byte[] arr; + for (int i = 0; i < x + z; i++) { + arr = new byte[len]; + if (i == x + z - 1 && y != 0) { + System.arraycopy(data, i * len, arr, 0, y); + } else { + System.arraycopy(data, i * len, arr, 0, len); + } + arrays[i] = arr; + } + return arrays; + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java b/utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java new file mode 100644 index 0000000..63a108e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/rx/RxTool.java @@ -0,0 +1,68 @@ +package com.lennon.cn.utill.utill.rx; + +import cn.droidlover.xdroidmvp.net.NetError; +import org.reactivestreams.Publisher; + +import java.util.concurrent.Callable; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; +import io.reactivex.FlowableEmitter; +import io.reactivex.FlowableOnSubscribe; +import io.reactivex.FlowableTransformer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; + +public class RxTool { + /** + * 线程切换 + * + * @return + */ + public static FlowableTransformer getScheduler() { + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .unsubscribeOn(AndroidSchedulers.mainThread()); + } + }; + } + + /** + * 异常处理变换 + * + * @return + */ + public static FlowableTransformer getApiTransformer() { + + return new FlowableTransformer() { + @Override + public Publisher apply(Flowable upstream) { + return upstream.flatMap(new Function>() { + @Override + public Publisher apply(T model) throws Exception { + if (model == null ) { + return Flowable.error(new NetError("无数据", NetError.NoDataError)); + } else { + return Flowable.just(model); + } + } + }); + } + }; + } + public static Flowable makeFlowable(final Callable func) { + return Flowable.create(new FlowableOnSubscribe() { + @Override + public void subscribe(FlowableEmitter e) throws Exception { + try { + e.onNext(func.call()); + } catch (Exception e1) { + } + } + }, BackpressureStrategy.ERROR); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java new file mode 100644 index 0000000..233a75e --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateStyle.java @@ -0,0 +1,57 @@ +package com.lennon.cn.utill.utill.time; + +/** + * Created by lennon on 2017/8/29. + */ + +public enum DateStyle { + MMDD("MMdd"), + YYYYMM("yyyyMM"), + YYYYMMDD("yyyyMMdd"), + + MM_DD("MM-dd"), + YYYY_MM("yyyy-MM"), + YYYY_MM_DD("yyyy-MM-dd"), + MM_DD_HH_MM("MM-dd HH:mm"), + MM_DD_HH_MM_SS("MM-dd HH:mm:ss"), + YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"), + YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"), + + MM_DD_EN("MM/dd"), + YYYY_MM_EN("yyyy/MM"), + YYYY_MM_DD_EN("yyyy/MM/dd"), + MM_DD_HH_MM_EN("MM/dd HH:mm"), + MM_DD_HH_MM_SS_EN("MM/dd HH:mm:ss"), + YYYY_MM_DD_HH_MM_EN("yyyy/MM/dd HH:mm"), + YYYY_MM_DD_HH_MM_SS_EN("yyyy/MM/dd HH:mm:ss"), + + MM_DD_CN("MM月dd日"), + YYYY_MM_CN("yyyy年MM月"), + YYYY_MM_DD_CN("yyyy年MM月dd日"), + MM_DD_HH_MM_CN("MM月dd日 HH:mm"), + MM_DD_HH_MM_SS_CN("MM月dd日 HH:mm:ss"), + YYYY_MM_DD_HH_MM_CN("yyyy年MM月dd日 HH:mm"), + YYYY_MM_DD_HH_MM_SS_CN("yyyy年MM月dd日 HH:mm:ss"), + + HH_MM("HH:mm"), + HH_MM_SS("HH:mm:ss"), + + MM_DD_P("MM.dd"), + YYYY_MM_P("yyyy.MM"), + YYYY_MM_DD_P("yyyy.MM.dd"), + MM_DD_HH_MM_P("MM.dd HH:mm"), + MM_DD_HH_MM_SS_P("MM.dd HH:mm:ss"), + YYYY_MM_DD_HH_MM_P("yyyy.MM.dd HH:mm"), + YYYY_MM_DD_HH_MM_SS_P("yyyy.MM.dd HH:mm:ss"); + + + private String value; + + DateStyle(String value) { + this.value = value; + } + + public String getValue() { + return value; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java new file mode 100644 index 0000000..6bd09b2 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/time/DateUtil.java @@ -0,0 +1,771 @@ +package com.lennon.cn.utill.utill.time; + +import org.jetbrains.annotations.NotNull; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by lennon on 2017/8/29. + */ + +public class DateUtil { + /** + * 获取SimpleDateFormat + * + * @param parttern 日期格式 + * @return SimpleDateFormat对象 + * @throws RuntimeException 异常:非法日期格式 + */ + private static SimpleDateFormat getDateFormat(String parttern) throws RuntimeException { + return new SimpleDateFormat(parttern); + } + + /** + * 获取日期中的某数值。如获取月份 + * + * @param date 日期 + * @param dateType 日期格式 + * @return 数值 + */ + private static int getInteger(Date date, int dateType) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(dateType); + } + + /** + * 增加日期中某类型的某数值。如增加日期 + * + * @param date 日期字符串 + * @param dateType 类型 + * @param amount 数值 + * @return 计算后日期字符串 + */ + private static String addInteger(String date, int dateType, int amount) { + String dateString = null; + DateStyle dateStyle = getDateStyle(date); + if (dateStyle != null) { + Date myDate = StringToDate(date, dateStyle); + myDate = addInteger(myDate, dateType, amount); + dateString = DateToString(myDate, dateStyle); + } + return dateString; + } + + /** + * 增加日期中某类型的某数值。如增加日期 + * + * @param date 日期 + * @param dateType 类型 + * @param amount 数值 + * @return 计算后日期 + */ + private static Date addInteger(Date date, int dateType, int amount) { + Date myDate = null; + if (date != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(dateType, amount); + myDate = calendar.getTime(); + } + return myDate; + } + + /** + * 获取精确的日期 + * + * @param timestamps 时间long集合 + * @return 日期 + */ + private static Date getAccurateDate(List timestamps) { + Date date = null; + long timestamp = 0; + Map map = new HashMap(); + List absoluteValues = new ArrayList(); + + if (timestamps != null && timestamps.size() > 0) { + if (timestamps.size() > 1) { + for (int i = 0; i < timestamps.size(); i++) { + for (int j = i + 1; j < timestamps.size(); j++) { + long absoluteValue = Math.abs(timestamps.get(i) - timestamps.get(j)); + absoluteValues.add(absoluteValue); + long[] timestampTmp = {timestamps.get(i), timestamps.get(j)}; + map.put(absoluteValue, timestampTmp); + } + } + + // 有可能有相等的情况。如2012-11和2012-11-01。时间戳是相等的 + long minAbsoluteValue = -1; + if (!absoluteValues.isEmpty()) { + // 如果timestamps的size为2,这是差值只有一个,因此要给默认值 + minAbsoluteValue = absoluteValues.get(0); + } + for (int i = 0; i < absoluteValues.size(); i++) { + for (int j = i + 1; j < absoluteValues.size(); j++) { + if (absoluteValues.get(i) > absoluteValues.get(j)) { + minAbsoluteValue = absoluteValues.get(j); + } else { + minAbsoluteValue = absoluteValues.get(i); + } + } + } + + if (minAbsoluteValue != -1) { + long[] timestampsLastTmp = map.get(minAbsoluteValue); + if (absoluteValues.size() > 1) { + timestamp = Math.max(timestampsLastTmp[0], timestampsLastTmp[1]); + } else if (absoluteValues.size() == 1) { + // 当timestamps的size为2,需要与当前时间作为参照 + long dateOne = timestampsLastTmp[0]; + long dateTwo = timestampsLastTmp[1]; + if ((Math.abs(dateOne - dateTwo)) < 100000000000L) { + timestamp = Math.max(timestampsLastTmp[0], timestampsLastTmp[1]); + } else { + long now = System.currentTimeMillis(); + if (Math.abs(dateOne - now) <= Math.abs(dateTwo - now)) { + timestamp = dateOne; + } else { + timestamp = dateTwo; + } + } + } + } + } else { + timestamp = timestamps.get(0); + } + } + + if (timestamp != 0) { + date = new Date(timestamp); + } + return date; + } + + /** + * 判断字符串是否为日期字符串 + * + * @param date 日期字符串 + * @return true or false + */ + public static boolean isDate(String date) { + boolean isDate = false; + if (date != null) { + if (StringToDate(date) != null) { + isDate = true; + } + } + return isDate; + } + + /** + * 获取日期字符串的日期风格。失敗返回null。 + * + * @param date 日期字符串 + * @return 日期风格 + */ + public static DateStyle getDateStyle(String date) { + DateStyle dateStyle = null; + Map map = new HashMap(); + List timestamps = new ArrayList(); + for (DateStyle style : DateStyle.values()) { + Date dateTmp = StringToDate(date, style.getValue()); + if (dateTmp != null) { + timestamps.add(dateTmp.getTime()); + map.put(dateTmp.getTime(), style); + } + } + dateStyle = map.get(getAccurateDate(timestamps).getTime()); + return dateStyle; + } + + /** + * 将日期字符串转化为日期。失败返回null。 + * + * @param date 日期字符串 + * @return 日期 + */ + public static Date StringToDate(String date) { + DateStyle dateStyle = null; + return StringToDate(date, dateStyle); + } + + /** + * 将日期字符串转化为日期。失败返回null。 + * + * @param date 日期字符串 + * @param parttern 日期格式 + * @return 日期 + */ + public static Date StringToDate(String date, String parttern) { + Date myDate = null; + if (date != null) { + try { + myDate = getDateFormat(parttern).parse(date); + } catch (Exception e) { + } + } + return myDate; + } + + /** + * 将日期字符串转化为日期。失败返回null。 + * + * @param date 日期字符串 + * @param dateStyle 日期风格 + * @return 日期 + */ + public static Date StringToDate(String date, DateStyle dateStyle) { + Date myDate = null; + if (dateStyle == null) { + List timestamps = new ArrayList(); + for (DateStyle style : DateStyle.values()) { + Date dateTmp = StringToDate(date, style.getValue()); + if (dateTmp != null) { + timestamps.add(dateTmp.getTime()); + } + } + myDate = getAccurateDate(timestamps); + } else { + myDate = StringToDate(date, dateStyle.getValue()); + } + return myDate; + } + + /** + * 将日期转化为日期字符串。失败返回null。 + * + * @param date 日期 + * @param parttern 日期格式 + * @return 日期字符串 + */ + public static String DateToString(Date date, String parttern) { + String dateString = null; + if (date != null) { + try { + dateString = getDateFormat(parttern).format(date); + } catch (Exception e) { + } + } + return dateString; + } + + /** + * 将日期转化为日期字符串。失败返回null。 + * + * @param date 日期 + * @param dateStyle 日期风格 + * @return 日期字符串 + */ + public static String DateToString(Date date, DateStyle dateStyle) { + String dateString = null; + if (dateStyle != null) { + dateString = DateToString(date, dateStyle.getValue()); + } + return dateString; + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param parttern 新日期格式 + * @return 新日期字符串 + */ + public static String StringToString(String date, String parttern) { + return StringToString(date, null, parttern); + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param dateStyle 新日期风格 + * @return 新日期字符串 + */ + public static String StringToString(String date, DateStyle dateStyle) { + return StringToString(date, null, dateStyle); + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param olddParttern 旧日期格式 + * @param newParttern 新日期格式 + * @return 新日期字符串 + */ + public static String StringToString(String date, String olddParttern, String newParttern) { + String dateString = null; + if (olddParttern == null) { + DateStyle style = getDateStyle(date); + if (style != null) { + Date myDate = StringToDate(date, style.getValue()); + dateString = DateToString(myDate, newParttern); + } + } else { + Date myDate = StringToDate(date, olddParttern); + dateString = DateToString(myDate, newParttern); + } + return dateString; + } + + /** + * 将日期字符串转化为另一日期字符串。失败返回null。 + * + * @param date 旧日期字符串 + * @param olddDteStyle 旧日期风格 + * @param newDateStyle 新日期风格 + * @return 新日期字符串 + */ + public static String StringToString(String date, DateStyle olddDteStyle, DateStyle newDateStyle) { + String dateString = null; + if (olddDteStyle == null) { + DateStyle style = getDateStyle(date); + dateString = StringToString(date, style.getValue(), newDateStyle.getValue()); + } else { + dateString = StringToString(date, olddDteStyle.getValue(), newDateStyle.getValue()); + } + return dateString; + } + + /** + * 增加日期的年份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加年份后的日期字符串 + */ + public static String addYear(String date, int yearAmount) { + return addInteger(date, Calendar.YEAR, yearAmount); + } + + /** + * 增加日期的年份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加年份后的日期 + */ + public static Date addYear(Date date, int yearAmount) { + return addInteger(date, Calendar.YEAR, yearAmount); + } + + /** + * 增加日期的月份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加月份后的日期字符串 + */ + public static String addMonth(String date, int yearAmount) { + return addInteger(date, Calendar.MONTH, yearAmount); + } + + /** + * 增加日期的月份。失败返回null。 + * + * @param date 日期 + * @param yearAmount 增加数量。可为负数 + * @return 增加月份后的日期 + */ + public static Date addMonth(Date date, int yearAmount) { + return addInteger(date, Calendar.MONTH, yearAmount); + } + + /** + * 增加日期的天数。失败返回null。 + * + * @param date 日期字符串 + * @param dayAmount 增加数量。可为负数 + * @return 增加天数后的日期字符串 + */ + public static String addDay(String date, int dayAmount) { + return addInteger(date, Calendar.DATE, dayAmount); + } + + /** + * 增加日期的天数。失败返回null。 + * + * @param date 日期 + * @param dayAmount 增加数量。可为负数 + * @return 增加天数后的日期 + */ + public static Date addDay(Date date, int dayAmount) { + return addInteger(date, Calendar.DATE, dayAmount); + } + + /** + * 增加日期的小时。失败返回null。 + * + * @param date 日期字符串 + * @param hourAmount 增加数量。可为负数 + * @return 增加小时后的日期字符串 + */ + public static String addHour(String date, int hourAmount) { + return addInteger(date, Calendar.HOUR_OF_DAY, hourAmount); + } + + /** + * 增加日期的小时。失败返回null。 + * + * @param date 日期 + * @param hourAmount 增加数量。可为负数 + * @return 增加小时后的日期 + */ + public static Date addHour(Date date, int hourAmount) { + return addInteger(date, Calendar.HOUR_OF_DAY, hourAmount); + } + + /** + * 增加日期的分钟。失败返回null。 + * + * @param date 日期字符串 + * @param hourAmount 增加数量。可为负数 + * @return 增加分钟后的日期字符串 + */ + public static String addMinute(String date, int hourAmount) { + return addInteger(date, Calendar.MINUTE, hourAmount); + } + + /** + * 增加日期的分钟。失败返回null。 + * + * @param date 日期 + * @param hourAmount 增加数量。可为负数 + * @return 增加分钟后的日期 + */ + public static Date addMinute(Date date, int hourAmount) { + return addInteger(date, Calendar.MINUTE, hourAmount); + } + + /** + * 增加日期的秒钟。失败返回null。 + * + * @param date 日期字符串 + * @param hourAmount 增加数量。可为负数 + * @return 增加秒钟后的日期字符串 + */ + public static String addSecond(String date, int hourAmount) { + return addInteger(date, Calendar.SECOND, hourAmount); + } + + /** + * 增加日期的秒钟。失败返回null。 + * + * @param date 日期 + * @param hourAmount 增加数量。可为负数 + * @return 增加秒钟后的日期 + */ + public static Date addSecond(Date date, int hourAmount) { + return addInteger(date, Calendar.SECOND, hourAmount); + } + + /** + * 获取日期的年份。失败返回0。 + * + * @param date 日期字符串 + * @return 年份 + */ + public static int getYear(String date) { + return getYear(StringToDate(date)); + } + + /** + * 获取日期的年份。失败返回0。 + * + * @param date 日期 + * @return 年份 + */ + public static int getYear(Date date) { + return getInteger(date, Calendar.YEAR); + } + + /** + * 获取日期的月份。失败返回0。 + * + * @param date 日期字符串 + * @return 月份 + */ + public static int getMonth(String date) { + return getMonth(StringToDate(date)); + } + + /** + * 获取日期的月份。失败返回0。 + * + * @param date 日期 + * @return 月份 + */ + public static int getMonth(Date date) { + return getInteger(date, Calendar.MONTH); + } + + /** + * 获取日期的天数。失败返回0。 + * + * @param date 日期字符串 + * @return 天 + */ + public static int getDay(String date) { + return getDay(StringToDate(date)); + } + + /** + * 获取日期的天数。失败返回0。 + * + * @param date 日期 + * @return 天 + */ + public static int getDay(Date date) { + return getInteger(date, Calendar.DATE); + } + + /** + * 获取日期的小时。失败返回0。 + * + * @param date 日期字符串 + * @return 小时 + */ + public static int getHour(String date) { + return getHour(StringToDate(date)); + } + + /** + * 获取日期的小时。失败返回0。 + * + * @param date 日期 + * @return 小时 + */ + public static int getHour(Date date) { + return getInteger(date, Calendar.HOUR_OF_DAY); + } + + /** + * 获取日期的分钟。失败返回0。 + * + * @param date 日期字符串 + * @return 分钟 + */ + public static int getMinute(String date) { + return getMinute(StringToDate(date)); + } + + /** + * 获取日期的分钟。失败返回0。 + * + * @param date 日期 + * @return 分钟 + */ + public static int getMinute(Date date) { + return getInteger(date, Calendar.MINUTE); + } + + /** + * 获取日期的秒钟。失败返回0。 + * + * @param date 日期字符串 + * @return 秒钟 + */ + public static int getSecond(String date) { + return getSecond(StringToDate(date)); + } + + /** + * 获取日期的秒钟。失败返回0。 + * + * @param date 日期 + * @return 秒钟 + */ + public static int getSecond(Date date) { + return getInteger(date, Calendar.SECOND); + } + + /** + * 获取日期 。默认yyyy-MM-dd格式。失败返回null。 + * + * @param date 日期字符串 + * @return 日期 + */ + public static String getDate(String date) { + return StringToString(date, DateStyle.YYYY_MM_DD); + } + + /** + * 获取日期。默认yyyy-MM-dd格式。失败返回null。 + * + * @param date 日期 + * @return 日期 + */ + public static String getDate(Date date) { + return DateToString(date, DateStyle.YYYY_MM_DD); + } + + /** + * 获取日期的时间。默认HH:mm:ss格式。失败返回null。 + * + * @param date 日期字符串 + * @return 时间 + */ + public static String getTime(String date) { + return StringToString(date, DateStyle.HH_MM_SS); + } + + /** + * 获取日期的时间。默认HH:mm:ss格式。失败返回null。 + * + * @param date 日期 + * @return 时间 + */ + public static String getTime(Date date) { + return DateToString(date, DateStyle.HH_MM_SS); + } + + /** + * 获取日期的星期。失败返回null。 + * + * @param date 日期字符串 + * @return 星期 + */ + public static Week getWeek(String date) { + Week week = null; + DateStyle dateStyle = getDateStyle(date); + if (dateStyle != null) { + Date myDate = StringToDate(date, dateStyle); + week = getWeek(myDate); + } + return week; + } + + /** + * 获取日期的星期。失败返回null。 + * + * @param date 日期 + * @return 星期 + */ + public static Week getWeek(Date date) { + Week week = null; + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int weekNumber = calendar.get(Calendar.DAY_OF_WEEK) - 1; + switch (weekNumber) { + case 0: + week = Week.SUNDAY; + break; + case 1: + week = Week.MONDAY; + break; + case 2: + week = Week.TUESDAY; + break; + case 3: + week = Week.WEDNESDAY; + break; + case 4: + week = Week.THURSDAY; + break; + case 5: + week = Week.FRIDAY; + break; + case 6: + week = Week.SATURDAY; + break; + default: + break; + } + return week; + } + + /** + * 获取两个日期相差的天数 + * + * @param date 日期字符串 + * @param otherDate 另一个日期字符串 + * @return 相差天数 + */ + public static int getIntervalDays(String date, String otherDate) { + return getIntervalDays(StringToDate(date), StringToDate(otherDate)); + } + + /** + * @param date 日期 + * @param otherDate 另一个日期 + * @return 相差天数 + */ + public static int getIntervalDays(Date date, Date otherDate) { + date = DateUtil.StringToDate(DateUtil.getDate(date)); + long time = Math.abs(date.getTime() - otherDate.getTime()); + return (int) time / (24 * 60 * 60 * 1000); + } + + /** + * 时间戳转换成日期格式字符串 + * + * @param seconds 精确到毫秒的字符串 + * @return + */ + public static String StampToDate(String seconds, DateStyle format) { + if (seconds == null || seconds.isEmpty() || "null".equals(seconds)) { + return ""; + } + SimpleDateFormat sdf = new SimpleDateFormat(format.getValue()); + return sdf.format(new Date(Long.valueOf(seconds))); + } + + /** + * 日期格式字符串转换成时间戳 + * + * @param date_str 字符串日期 + * @param format 如:yyyy-MM-dd HH:mm:ss + * @return + */ + public static String DateToTimeStamp(String date_str, DateStyle format) { + try { + SimpleDateFormat sdf = new SimpleDateFormat(format.getValue()); + return String.valueOf(sdf.parse(date_str).getTime() / 1000); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + @NotNull + public static Date getDateFromNow(int timeType, int timenum) { + Calendar cld = Calendar.getInstance(); + cld.set(timeType, cld.get(timeType) + timenum); + return cld.getTime(); + } + + /** + * 比较两个日期的大小,日期格式为yyyy-MM-dd + * + * @param endTime the first date + * @param startTime the second date + * @return true
false + */ + public static boolean isDate2Bigger(Date endTime, Date startTime) { + return isDate2Bigger(endTime, startTime, true); + } + + public static boolean isDate2Bigger(Date endTime, Date startTime, boolean equals) { + boolean isBigger; + if (endTime.getTime() > startTime.getTime()) { + isBigger = false; + } else if (endTime.getTime() < startTime.getTime()) { + isBigger = true; + } else { + isBigger = equals; + } + return isBigger; + } + + public static Date getNowDate() { + return new Date(); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java b/utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java new file mode 100644 index 0000000..c861623 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/utill/time/Week.java @@ -0,0 +1,43 @@ +package com.lennon.cn.utill.utill.time; + +/** + * Created by lennon on 2017/8/29. + */ + +public enum Week { + MONDAY("星期一", "Monday", "Mon.", 1), + TUESDAY("星期二", "Tuesday", "Tues.", 2), + WEDNESDAY("星期三", "Wednesday", "Wed.", 3), + THURSDAY("星期四", "Thursday", "Thur.", 4), + FRIDAY("星期五", "Friday", "Fri.", 5), + SATURDAY("星期六", "Saturday", "Sat.", 6), + SUNDAY("星期日", "Sunday", "Sun.", 7); + + String name_cn; + String name_en; + String name_enShort; + int number; + + Week(String name_cn, String name_en, String name_enShort, int number) { + this.name_cn = name_cn; + this.name_en = name_en; + this.name_enShort = name_enShort; + this.number = number; + } + + public String getChineseName() { + return name_cn; + } + + public String getName() { + return name_en; + } + + public String getShortName() { + return name_enShort; + } + + public int getNumber() { + return number; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java b/utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java new file mode 100644 index 0000000..4c304b0 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/DownLoad.java @@ -0,0 +1,109 @@ +package com.lennon.cn.utill.version; + +import android.app.Activity; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +import androidx.annotation.Nullable; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.base.BaseApplication; +import com.lennon.cn.utill.bean.Download; +import com.lennon.cn.utill.utill.StringUtils; +import com.lennon.cn.utill.utill.Utill; +import com.lennon.cn.utill.version.download.DownloadProgressListener; +import io.reactivex.functions.Consumer; + +import java.io.File; + + +/** + * Created by lennon on 2017/5/25. + */ + +public class DownLoad extends Service { + private String url; + private DownLoadListener listener; + int downloadCount = 0; + private File outputFile; + private boolean isReDown; + private Activity activity; + + public DownLoad(Activity activity, String url, boolean isReDown, DownLoadListener listener) { + this.url = url; + this.listener = listener; + this.isReDown = isReDown; + this.activity = activity; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + DownloadProgressListener listener = new DownloadProgressListener() { + @Override + public void update(long bytesRead, long contentLength, boolean done) { + int progress = (int) ((bytesRead * 100) / contentLength); + if ((downloadCount == 0) || progress > downloadCount) { + final Download download = new Download(); + download.setTotalFileSize(contentLength); + download.setCurrentFileSize(bytesRead); + download.setProgress(progress); + downloadCount = progress; + if (DownLoad.this.listener != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DownLoad.this.listener.update(download); + } + }); + } + } + + } + }; + outputFile = new File(BaseApplication.Companion.getDataFile(), Utill.INSTANCE.getFileName(url)); + + if (outputFile.exists()) { + if (isReDown) { + outputFile.delete(); + } else { + this.listener.downloadCompleted(outputFile); + return null; + } + } + + + String baseUrl = StringUtils.getHostName(url); + + new DownloadAPI(baseUrl, listener).downloadAPK(url, outputFile, new Consumer() { + + @Override + public void accept(Object o) throws Exception { + if (DownLoad.this.listener != null) { + DownLoad.this.listener.downloadCompleted(outputFile); + } + } + },new Consumer(){ + + @Override + public void accept(Throwable e) throws Exception { + e.printStackTrace(); + XLog.e("onError: " + e.getMessage()); + outputFile.delete(); + if (DownLoad.this.listener != null) { + DownLoad.this.listener.downloadError(outputFile); + } + XLog.e("onError: " + e.getMessage()); + } + }); + return null; + } + + public interface DownLoadListener { + void update(Download download); + + void downloadCompleted(File outputFile); + + void downloadError(File outputFile); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java b/utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java new file mode 100644 index 0000000..01ad688 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/DownloadAPI.java @@ -0,0 +1,76 @@ +package com.lennon.cn.utill.version; + +import androidx.annotation.NonNull; +import cn.droidlover.xdroidmvp.log.XLog; +import com.lennon.cn.utill.utill.FileUtil; +import com.lennon.cn.utill.version.download.DownloadProgressInterceptor; +import com.lennon.cn.utill.version.download.DownloadProgressListener; +import com.lennon.cn.utill.version.exception.CustomizeException; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Consumer; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; +import okhttp3.OkHttpClient; +import okhttp3.ResponseBody; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +/** + * Created by JokAr on 16/7/5. + */ +public class DownloadAPI { + private static final String TAG = "DownloadAPI"; + private static final int DEFAULT_TIMEOUT = 15; + public Retrofit retrofit; + + + public DownloadAPI(String url, DownloadProgressListener listener) { + DownloadProgressInterceptor interceptor = new DownloadProgressInterceptor(listener); + + OkHttpClient client = new OkHttpClient.Builder() + .addInterceptor(interceptor) + .retryOnConnectionFailure(true) + .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS) + .build(); + + + retrofit = new Retrofit.Builder() + .baseUrl(url) + .client(client) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build(); + } + + public void downloadAPK(@NonNull String url, final File file, Consumer consumer, Consumer e) { + XLog.d(TAG+" downloadAPK: " + url); + retrofit.create(DownloadService.class) + .download(url) + .subscribeOn(Schedulers.io()) + .unsubscribeOn(Schedulers.io()) + .map(new Function() { + @Override + public InputStream apply(ResponseBody responseBody) throws Exception { + return responseBody.byteStream(); + } + }) + .observeOn(Schedulers.io()) + .doOnNext(new Consumer() { + @Override + public void accept(InputStream inputStream) throws Exception { + try { + FileUtil.writeFile(inputStream, file); + } catch (IOException e) { + e.printStackTrace(); + throw new CustomizeException(e.getMessage(), e); + } + } + }) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(consumer,e); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java b/utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java new file mode 100644 index 0000000..f73b3aa --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/DownloadService.java @@ -0,0 +1,18 @@ +package com.lennon.cn.utill.version; + +import io.reactivex.Observable; +import okhttp3.ResponseBody; +import retrofit2.http.GET; +import retrofit2.http.Streaming; +import retrofit2.http.Url; + +/** + * Created by JokAr on 16/7/5. + */ +public interface DownloadService { + + + @Streaming + @GET + Observable download(@Url String url); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java new file mode 100644 index 0000000..a62cedf --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressInterceptor.java @@ -0,0 +1,28 @@ +package com.lennon.cn.utill.version.download; + +import okhttp3.Interceptor; +import okhttp3.Response; + +import java.io.IOException; + +/** + * Interceptor for download + * Created by JokAr on 16/5/11. + */ +public class DownloadProgressInterceptor implements Interceptor { + + private DownloadProgressListener listener; + + public DownloadProgressInterceptor(DownloadProgressListener listener) { + this.listener = listener; + } + + @Override + public Response intercept(Chain chain) throws IOException { + Response originalResponse = chain.proceed(chain.request()); + + return originalResponse.newBuilder() + .body(new DownloadProgressResponseBody(originalResponse.body(), listener)) + .build(); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java new file mode 100644 index 0000000..954ffce --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressListener.java @@ -0,0 +1,9 @@ +package com.lennon.cn.utill.version.download; + +/** + * 下载进度listener + * Created by JokAr on 16/5/11. + */ +public interface DownloadProgressListener { + void update(long bytesRead, long contentLength, boolean done); +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java new file mode 100644 index 0000000..51e2538 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/download/DownloadProgressResponseBody.java @@ -0,0 +1,61 @@ +package com.lennon.cn.utill.version.download; + +import okhttp3.MediaType; +import okhttp3.ResponseBody; +import okio.*; + +import java.io.IOException; + +/** + * ResponseBody for download + * Created by JokAr on 16/5/11. + */ +public class DownloadProgressResponseBody extends ResponseBody { + + private ResponseBody responseBody; + private DownloadProgressListener progressListener; + private BufferedSource bufferedSource; + + public DownloadProgressResponseBody(ResponseBody responseBody, + DownloadProgressListener progressListener) { + this.responseBody = responseBody; + this.progressListener = progressListener; + } + + @Override + public MediaType contentType() { + return responseBody.contentType(); + } + + @Override + public long contentLength() { + return responseBody.contentLength(); + } + + @Override + public BufferedSource source() { + if (bufferedSource == null) { + bufferedSource = Okio.buffer(source(responseBody.source())); + } + return bufferedSource; + } + + private Source source(Source source) { + return new ForwardingSource(source) { + long totalBytesRead = 0L; + + @Override + public long read(Buffer sink, long byteCount) throws IOException { + long bytesRead = super.read(sink, byteCount); + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += bytesRead != -1 ? bytesRead : 0; + + if (null != progressListener) { + progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1); + } + return bytesRead; + } + }; + + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java b/utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java new file mode 100644 index 0000000..4be62f7 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/version/exception/CustomizeException.java @@ -0,0 +1,11 @@ +package com.lennon.cn.utill.version.exception; + +/** + * Created by JokAr on 16/7/5. + */ +public class CustomizeException extends RuntimeException { + + public CustomizeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java b/utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java new file mode 100644 index 0000000..ad39c49 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/AutoLineFeedLayoutManager.java @@ -0,0 +1,53 @@ +package com.lennon.cn.utill.widget; + +import android.view.View; +import androidx.recyclerview.widget.RecyclerView; + +/** + * @author 机械革命 + */ +public class AutoLineFeedLayoutManager extends RecyclerView.LayoutManager { + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams( + RecyclerView.LayoutParams.WRAP_CONTENT, + RecyclerView.LayoutParams.WRAP_CONTENT); + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + detachAndScrapAttachedViews(recycler); + + int sumWidth = getWidth(); + + int curLineWidth = 0, curLineTop = 0; + int lastLineMaxHeight = 0; + for (int i = 0; i < getItemCount(); i++) { + View view = recycler.getViewForPosition(i); + + addView(view); + measureChildWithMargins(view, 0, 0); + int width = getDecoratedMeasuredWidth(view); + int height = getDecoratedMeasuredHeight(view); + + curLineWidth += width; + //不需要换行 + if (curLineWidth <= sumWidth) { + layoutDecorated(view, curLineWidth - width, curLineTop, curLineWidth, curLineTop + height); + //比较当前行多有item的最大高度 + lastLineMaxHeight = Math.max(lastLineMaxHeight, height); + } else {//换行 + curLineWidth = width; + if (lastLineMaxHeight == 0) { + lastLineMaxHeight = height; + } + //记录当前行top + curLineTop += lastLineMaxHeight; + + layoutDecorated(view, 0, curLineTop, width, curLineTop + height); + lastLineMaxHeight = height; + } + } + + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java b/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java new file mode 100644 index 0000000..1a1d5b5 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/ClearWithSpaceEditText.java @@ -0,0 +1,298 @@ +package com.lennon.cn.utill.widget; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.DigitsKeyListener; +import android.util.AttributeSet; +import android.widget.EditText; +import android.widget.Toast; + +import cn.droidlover.xdroidmvp.log.XLog; +import lennon.com.utill.R; + +/** + * 项目名称:android-pay + * 类描述: + * 创建人:LeoPoldCrossing + * 创建时间:16/10/28 + */ + +@SuppressLint("AppCompatCustomView") +public class ClearWithSpaceEditText extends EditText { + + private int contentType; + public static final int TYPE_PHONE = 0; + public static final int TYPE_CARD = 1; + public static final int TYPE_IDCARD = 2; + public static final int TYPE_MANAGER = 3; + private int maxLength = 100; + private int start, count, before; + private String digits; + + public ClearWithSpaceEditText(Context context) { + this(context, null); + } + + public ClearWithSpaceEditText(Context context, AttributeSet attrs) { + super(context, attrs); + parseAttributeSet(context, attrs); + } + + public ClearWithSpaceEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + parseAttributeSet(context, attrs); + } + + private void parseAttributeSet(Context context, AttributeSet attrs) { + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.clear_edit_attrs, 0, 0); + contentType = ta.getInt(R.styleable.clear_edit_attrs_input_type, 0); + ta.recycle(); + initType(); + setSingleLine(); + addTextChangedListener(watcher); + } + + private void initType() { + if (contentType == TYPE_PHONE) { + maxLength = 13; + digits = "0123456789 "; + setInputType(InputType.TYPE_CLASS_NUMBER); + } else if (contentType == TYPE_CARD) { + maxLength = 31; + digits = "0123456789 "; + setInputType(InputType.TYPE_CLASS_NUMBER); + } else if (contentType == TYPE_IDCARD) { + maxLength = 21; + digits = null; + setInputType(InputType.TYPE_CLASS_TEXT); + } else if (contentType == TYPE_MANAGER) { + maxLength = 16; + digits = "YCyc0123456789 "; + setInputType(InputType.TYPE_CLASS_TEXT); + } + setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)}); + } + + @Override + public void setInputType(int type) { + if (contentType == TYPE_PHONE || contentType == TYPE_CARD) { + type = InputType.TYPE_CLASS_NUMBER; + } else if (contentType == TYPE_IDCARD || contentType == TYPE_MANAGER) { + type = InputType.TYPE_CLASS_TEXT; + } + super.setInputType(type); + /* 非常重要:setKeyListener要在setInputType后面调用,否则无效。*/ + if (!TextUtils.isEmpty(digits)) { + final int finalType = type; + setKeyListener(new DigitsKeyListener() { + @Override + public int getInputType() { + return finalType; + } + + @Override + protected char[] getAcceptedChars() { + char[] data = digits.toCharArray(); + return data; + } + }); + } + } + + public void setContentType(int contentType) { + this.contentType = contentType; + initType(); + } + + private TextWatcher watcher = 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) { + ClearWithSpaceEditText.this.start = start; + ClearWithSpaceEditText.this.before = before; + ClearWithSpaceEditText.this.count = count; + } + + @Override + public void afterTextChanged(Editable s) { + if (s == null) { + return; + } + //判断是否是在中间输入,需要重新计算 + boolean isMiddle = (start + count) < (s.length()); + //在末尾输入时,是否需要加入空格 + boolean isNeedSpace = false; + if (!isMiddle && isSpace(s.length())) { + isNeedSpace = true; + } + boolean isNeedDelYC = false; + boolean yc = s.toString().toUpperCase().startsWith("YC"); + boolean y = s.toString().toUpperCase().startsWith("Y"); + if (yc) { + if (s.toString().indexOf("Y", 2) > 0 || s.toString().indexOf("C", 2) > 0) { + isNeedDelYC = true; + } + } else { + String s1 = s.toString().toUpperCase(); + if (y) { + s1 = s.toString().subSequence(1, s.length()).toString(); + } + if (s1.contains("Y") || s1.contains("C")) { + isNeedDelYC = true; + } + } + if (isMiddle || isNeedSpace || count > 1 || isNeedDelYC) { + String a = s.toString().toUpperCase(); + if (isNeedDelYC) { + String replace = a.replace("Y", ""); + replace = replace.replace("C", ""); + if (yc) { + a = "YC" + replace; + } else { + if (y) { + a = "Y" + replace; + } else { + a = replace; + } + } + } + String newStr = a; + newStr = newStr.replace(" ", ""); + StringBuilder sb = new StringBuilder(); + int spaceCount = 0; + for (int i = 0; i < newStr.length(); i++) { + sb.append(newStr.substring(i, i + 1)); + //如果当前输入的字符下一位为空格(i+1+1+spaceCount),因为i是从0开始计算的,所以一开始的时候需要先加1 + XLog.e(sb.toString()); + if (isSpace(i + 2 + spaceCount)) { + sb.append(" "); + XLog.e(sb.toString()); + spaceCount += 1; + } + } + removeTextChangedListener(watcher); + /* 该处原来是调用setText(sb)。 + * 但是如果调用该语句的话,在身份证输入框的情况下(允许字母和数字),当调用setText时,会导致输入法的跳转 + * 参照网上解决方法,将该句话替换成s.replace(...) + * 该种方法不会导致输入法的跳转。 + * 造成输入法跳转的原因可能是setText会重新唤起输入法控件*/ + s.replace(0, s.length(), sb); + //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴) + if (!isMiddle || count > 1) { + setSelection(Math.min(s.length(), maxLength)); + } else if (isMiddle) { + //如果是删除 + if (count == 0) { + //如果删除时,光标停留在空格的前面,光标则要往前移一位 + if (isSpace(start - before + 1)) { + setSelection(Math.max((start - before), 0)); + } else { + setSelection(Math.min((start - before + 1), s.length())); + } + } + //如果是增加 + else { + if (isSpace(start - before + count)) { + setSelection(Math.min((start + count - before + 1), s.length())); + } else { + setSelection(start + count - before); + } + } + } + addTextChangedListener(watcher); + } + } + }; + + /** + * 获取无空格的字符串 + * + * @return + */ + public String getNoSpaceText() { + String newStr = this.getText().toString().toUpperCase(); + if (!TextUtils.isEmpty(newStr)) { + String strNOSpace = newStr.replace(" ", ""); + if (!TextUtils.isEmpty(strNOSpace)) { + strNOSpace = strNOSpace.replace(" ", ""); + } + return strNOSpace; + } + return ""; + } + + public boolean checkTextRight() { + String text = getNoSpaceText(); + //这里做个简单的内容判断 + if (contentType == TYPE_PHONE) { + if (TextUtils.isEmpty(text)) { + Toast.makeText(getContext(), "手机号不能为空,请输入正确的手机号", Toast.LENGTH_SHORT); + } else if (text.length() < 11) { + Toast.makeText(getContext(), "手机号不足11位,请输入正确的手机号", Toast.LENGTH_SHORT); + } else { + return true; + } + } else if (contentType == TYPE_CARD) { + if (TextUtils.isEmpty(text)) { + Toast.makeText(getContext(), "银行卡号不能为空,请输入正确的银行卡号", Toast.LENGTH_SHORT); + } else if (text.length() < 14) { + Toast.makeText(getContext(), "银行卡号位数不正确,请输入正确的银行卡号", Toast.LENGTH_SHORT); + } else { + return true; + } + } else if (contentType == TYPE_IDCARD) { + if (TextUtils.isEmpty(text)) { + Toast.makeText(getContext(), "身份证号不能为空,请输入正确的身份证号", Toast.LENGTH_SHORT); + } else if (text.length() < 18) { + Toast.makeText(getContext(), "身份证号不正确,请输入正确的身份证号", Toast.LENGTH_SHORT); + } else { + return true; + } + } + return false; + } + + private boolean isSpace(int length) { + if (contentType == TYPE_PHONE) { + return isSpacePhone(length); + } else if (contentType == TYPE_CARD) { + return isSpaceCard(length); + } else if (contentType == TYPE_IDCARD) { + return isSpaceIDCard(length); + } else if (contentType == TYPE_MANAGER) { + return isSpaceManager(length); + } + return false; + } + + private boolean isSpaceManager(int length) { + XLog.e("-----------" + length + "---------" + getText()); + if (getNoSpaceText().startsWith("YC")) { + return length == 3 || length == 7 || (length > 7 && length < 16 && (length - 2) % 5 == 0); + } else { + return length >= 4 && (length == 4 || (length + 1) % 5 == 0) && length < 13; + } + } + + private boolean isSpacePhone(int length) { + return length >= 4 && (length == 4 || (length + 1) % 5 == 0); + } + + private boolean isSpaceCard(int length) { + return length % 5 == 0; + } + + private boolean isSpaceIDCard(int length) { + return length > 6 && (length == 7 || (length - 2) % 5 == 0); + } + +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java b/utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java new file mode 100644 index 0000000..00d22a0 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/ContainsEmojiEditText.java @@ -0,0 +1,155 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.text.*; +import android.util.AttributeSet; +import androidx.appcompat.widget.AppCompatEditText; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by lennon on 2017/9/4. + */ + +public class ContainsEmojiEditText extends AppCompatEditText { + //输入表情前的光标位置 + private int cursorPos; + //输入表情前EditText中的文本 + private String inputAfterText; + //是否重置了EditText的内容 + private boolean resetText; + + private Context mContext; + + public ContainsEmojiEditText(Context context) { + super(context); + this.mContext = context; + initEditText(); + } + + public ContainsEmojiEditText(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + initEditText(); + } + + public ContainsEmojiEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + this.mContext = context; + initEditText(); + } + + // 初始化edittext 控件 + private void initEditText() { + setEditTextInhibitInputSpeChat(); + addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int before, int count) { + if (!resetText) { + cursorPos = getSelectionEnd(); + // 这里用s.toString()而不直接用s是因为如果用s, + // 那么,inputAfterText和s在内存中指向的是同一个地址,s改变了, + // inputAfterText也就改变了,那么表情过滤就失败了 + inputAfterText = s.toString(); + } + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (!resetText) { + if (count >= 2) {//表情符号的字符长度最小为2 + CharSequence input = s.subSequence(start, start + count); + if (containsEmoji(input.toString())) { + resetText = true; +// Toast.makeText(mContext, "不支持输入Emoji表情符号", Toast.LENGTH_SHORT).show(); + //是表情符号就将文本还原为输入表情符号之前的内容 + setText(inputAfterText); + CharSequence text = getText(); + if (text instanceof Spannable) { + Spannable spanText = (Spannable) text; + Selection.setSelection(spanText, text.length()); + } + } + } + } else { + resetText = false; + } + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }); + } + + + /** + * 检测是否有emoji表情 + * + * @param source + * @return + */ + public static boolean containsEmoji(String source) { + int len = source.length(); + for (int i = 0; i < len; i++) { + char codePoint = source.charAt(i); + if (!isEmojiCharacter(codePoint)) { //如果不能匹配,则该字符是Emoji表情 + return true; + } + } + return false; + } + + /** + * 判断是否是Emoji + * + * @param codePoint 比较的单个字符 + * @return + */ + private static boolean isEmojiCharacter(char codePoint) { + return (codePoint == 0x0) || (codePoint == 0x9) || (codePoint == 0xA) || + (codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) || + ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000) + && (codePoint <= 0x10FFFF)); + } + + public static boolean isEmoji(String string) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern p = Pattern.compile("/[\u1F60-\u1F64]|[\u2702-\u27B0]|[\u1F68-\u1F6C]|[\u1F30-\u1F70]|[\u2600-\u26ff]/g"); + Matcher m = p.matcher(string); + return m.matches(); + } + + public boolean isEmoji2(String string) { + @SuppressWarnings("AlibabaAvoidPatternCompileInMethod") Pattern p = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]", + Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(string); + return m.find(); + } + + public void allowEditTextInputSpeChat() { + setFilters(new InputFilter[0]); + } + + /** + * 禁止EditText输入特殊字符 + */ + private void setEditTextInhibitInputSpeChat() { + InputFilter filter = new InputFilter() { + @Override + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { + String speChat = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]"; + Pattern pattern = Pattern.compile(speChat); + Matcher matcher = pattern.matcher(source.toString()); + if (matcher.find()) { + return ""; + } else { + return null; + } + } + }; + setFilters(new InputFilter[]{filter}); + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java b/utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java new file mode 100644 index 0000000..42512fd --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/CycleWheelView.java @@ -0,0 +1,597 @@ +package com.lennon.cn.utill.widget; +/** + * Copyright (C) 2015 + *

+ * CycleWheelView.java + *

+ * Description: + *

+ * Author: Liao Longhui + *

+ * Ver 1.0, 2015-07-15, Liao Longhui, Create file + */ + + +import android.content.Context; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import lennon.com.utill.R; + + +/** + * 可循环滚动的选择器 + * + * @author Liao Longhui + */ +public class CycleWheelView extends ListView { + + public static final String TAG = CycleWheelView.class.getSimpleName(); + + private static final int COLOR_DIVIDER_DEFALUT = Color.parseColor("#ffffff"); + + private static final int HEIGHT_DIVIDER_DEFAULT = 2; + + private static final int COLOR_SOLID_DEFAULT = Color.parseColor("#ffffff"); + + private static final int COLOR_SOLID_SELET_DEFAULT = Color.parseColor("#ffffff"); + + private static final int WHEEL_SIZE_DEFAULT = 3; + + + private Handler mHandler; + + + private CycleWheelViewAdapter mAdapter; + + /** + * Labels + */ + + private List mLabels; + + /** + * Color Of Selected Label + */ + + private int mLabelSelectColor = Color.WHITE; + + /** + * Color Of Unselected Label + */ + + private int mLabelColor = Color.GRAY; + + /** + * Gradual Alph + */ + + private float mAlphaGradual = 0.7f; + + /** + * Color Of Divider + */ + + private int dividerColor = COLOR_DIVIDER_DEFALUT; + + /** + * Height Of Divider + */ + + private int dividerHeight = HEIGHT_DIVIDER_DEFAULT; + + /** + * Color of Selected Solid + */ + + private int seletedSolidColor = COLOR_SOLID_SELET_DEFAULT; + + /** + * Color of Unselected Solid + */ + + private int solidColor = COLOR_SOLID_DEFAULT; + + /** + * Size Of Wheel , it should be odd number like 3 or greater + */ + + private int mWheelSize = WHEEL_SIZE_DEFAULT; + + /** + * res Id of Wheel Item Layout + */ + + private int mItemLayoutId; + + /** + * res Id of Label TextView + */ + + private int mItemLabelTvId; + + /** + * Height of Wheel Item + */ + + private int mItemHeight; + + + private boolean cylceEnable; + + + private int mCurrentPositon; + + + private WheelItemSelectedListener mItemSelectedListener; + + + public CycleWheelView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + + public CycleWheelView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + + public CycleWheelView(Context context) { + super(context); + } + + + private void init() { + mHandler = new Handler(); + mItemLayoutId = R.layout.item_cyclewheel; + mItemLabelTvId = R.id.tv_label_item_wheel; + mAdapter = new CycleWheelViewAdapter(); + setVerticalScrollBarEnabled(false); + setScrollingCacheEnabled(false); + setCacheColorHint(Color.TRANSPARENT); + setFadingEdgeLength(0); + setOverScrollMode(OVER_SCROLL_NEVER); + setDividerHeight(0); + setAdapter(mAdapter); + setOnScrollListener(new OnScrollListener() { + @Override + + + public void onScrollStateChanged(AbsListView view, int scrollState) { + if (scrollState == SCROLL_STATE_IDLE) { + View itemView = getChildAt(0); + if (itemView != null) { + float deltaY = itemView.getY(); + if (deltaY == 0) { + return; + } + if (Math.abs(deltaY) < mItemHeight / 2) { + smoothScrollBy(getDistance(deltaY), 50); + } else { + smoothScrollBy(getDistance(mItemHeight + deltaY), 50); + } + } + } + } + + + @Override + + + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, + int totalItemCount) { + refreshItems(); + } + + + }); + } + + + private int getDistance(float scrollDistance) { + if (Math.abs(scrollDistance) <= 2) { + return (int) scrollDistance; + } else if (Math.abs(scrollDistance) < 12) { + return scrollDistance > 0 ? 2 : -2; + } else { + return (int) (scrollDistance / 6); + } + } + + private void refreshItems() { + int offset = mWheelSize / 2; + int firstPosition = getFirstVisiblePosition(); + int position = 0; + if (getChildAt(0) == null) { + return; + } + if (Math.abs(getChildAt(0).getY()) <= mItemHeight / 2) { + position = firstPosition + offset; + } else { + position = firstPosition + offset + 1; + } + if (position == mCurrentPositon) { + return; + } + mCurrentPositon = position; + if (mItemSelectedListener != null) { + mItemSelectedListener.onItemSelected(getSelection(), getSelectLabel()); + } + resetItems(firstPosition, position, offset); + } + + private void resetItems(int firstPosition, int position, int offset) { + for (int i = position - offset - 1; i < position + offset + 1; i++) { + View itemView = getChildAt(i - firstPosition); + if (itemView == null) { + continue; + } + TextView labelTv = (TextView) itemView.findViewById(mItemLabelTvId); + if (position == i) { + labelTv.setTextColor(mLabelSelectColor); + labelTv.setTextSize(18); + itemView.setAlpha(1f); + } else { + labelTv.setTextColor(mLabelColor); + labelTv.setTextSize(16); + int delta = Math.abs(i - position); + double alpha = Math.pow(mAlphaGradual, delta); + itemView.setAlpha((float) alpha); + } + } + } + + + /** + * 设置滚轮的刻度列表 + * + * @param labels + */ + + + public void setLabels(List labels) { + mLabels = labels; + mAdapter.setData(mLabels); + mAdapter.notifyDataSetChanged(); + initView(); + } + + + /** + * 设置滚轮滚动监听 + * + * @param mItemSelectedListener + */ + + + public void setOnWheelItemSelectedListener(WheelItemSelectedListener mItemSelectedListener) { + this.mItemSelectedListener = mItemSelectedListener; + } + + + /** + * 获取滚轮的刻度列表 + * + * @return + */ + + + public List getLabels() { + return mLabels; + } + + + /** + * 设置滚轮是否为循环滚动 + * + * @param enable true-循环 false-单程 + */ + + + public void setCycleEnable(boolean enable) { + if (cylceEnable != enable) { + cylceEnable = enable; + mAdapter.notifyDataSetChanged(); + setSelection(getSelection()); + } + } + + + /* +* 滚动到指定位置 +*/ + + @Override + + + public void setSelection(final int position) { + mHandler.post(new Runnable() { + @Override + public void run() { + CycleWheelView.super.setSelection(getPosition(position)); + } + }); + } + + + private int getPosition(int positon) { + if (mLabels == null || mLabels.size() == 0) { + return 0; + } + if (cylceEnable) { + int d = Integer.MAX_VALUE / 2 / mLabels.size(); + return positon + d * mLabels.size(); + } + return positon; + } + + + /** + * 获取当前滚轮位置 + * + * @return + */ + + + public int getSelection() { + if (mCurrentPositon == 0) { + mCurrentPositon = mWheelSize / 2; + } + return (mCurrentPositon - mWheelSize / 2) % mLabels.size(); + } + + + /** + * 获取当前滚轮位置的刻度 + * + * @return + */ + + + public String getSelectLabel() { + int position = getSelection(); + position = position < 0 ? 0 : position; + try { + return mLabels.get(position); + } catch (Exception e) { + return ""; + } + } + + + /** + * 如果需要自定义滚轮每个Item,调用此方法设置自定义Item布局,自定义布局中需要一个TextView来显示滚轮刻度 + * + * @param itemResId 布局文件Id + * @param labelTvId 刻度TextView的资源Id + */ + + + public void setWheelItemLayout(int itemResId, int labelTvId) { + mItemLayoutId = itemResId; + mItemLabelTvId = labelTvId; + mAdapter = new CycleWheelViewAdapter(); + mAdapter.setData(mLabels); + setAdapter(mAdapter); + initView(); + } + + + /*** + * 设置未选中刻度文字颜色 + * + * @param labelColor + */ + + + public void setLabelColor(int labelColor) { + this.mLabelColor = labelColor; + resetItems(getFirstVisiblePosition(), mCurrentPositon, mWheelSize / 2); + } + + + /** + * 设置选中刻度文字颜色 + * + * @param labelSelectColor + */ + + + public void setLabelSelectColor(int labelSelectColor) { + this.mLabelSelectColor = labelSelectColor; + resetItems(getFirstVisiblePosition(), mCurrentPositon, mWheelSize / 2); + } + + + /** + * 设置滚轮刻度透明渐变值 + * + * @param alphaGradual + */ + + + public void setAlphaGradual(float alphaGradual) { + this.mAlphaGradual = alphaGradual; + resetItems(getFirstVisiblePosition(), mCurrentPositon, mWheelSize / 2); + } + + + /** + * 设置滚轮可显示的刻度数量,必须为奇数,且大于等于3 + * + * @param wheelSize + * @throws CycleWheelViewException 滚轮数量错误 + */ + + + public void setWheelSize(int wheelSize){ + if (wheelSize < 3 || wheelSize % 2 != 1) { +// throw new CycleWheelViewException("Wheel Size Error , Must Be 3,5,7,9..."); + mWheelSize = WHEEL_SIZE_DEFAULT; + } else { + mWheelSize = wheelSize; + initView(); + } + } + + /** + * 设置块的颜色 + * + * @param unselectedSolidColor 未选中的块的颜色 + * @param selectedSolidColor 选中的块的颜色 + */ + + public void setSolid(int unselectedSolidColor, int selectedSolidColor) { + this.solidColor = unselectedSolidColor; + this.seletedSolidColor = selectedSolidColor; + initView(); + } + + /** + * 设置分割线样式 + * + * @param dividerColor 分割线颜色 + * @param dividerHeight 分割线高度(px) + */ + + public void setDivider(int dividerColor, int dividerHeight) { + this.dividerColor = dividerColor; + this.dividerHeight = dividerHeight; + } + + @SuppressWarnings("deprecation") + private void initView() { + mItemHeight = measureHeight(); + ViewGroup.LayoutParams lp = getLayoutParams(); + lp.height = mItemHeight * mWheelSize; + mAdapter.setData(mLabels); + mAdapter.notifyDataSetChanged(); + Drawable backgroud = new Drawable() { + @Override + public void draw(Canvas canvas) { + int viewWidth = getWidth(); + Paint dividerPaint = new Paint(); + dividerPaint.setColor(dividerColor); + dividerPaint.setStrokeWidth(dividerHeight); + Paint seletedSolidPaint = new Paint(); + seletedSolidPaint.setColor(seletedSolidColor); + Paint solidPaint = new Paint(); + solidPaint.setColor(solidColor); + canvas.drawRect(0, 0, viewWidth, mItemHeight * (mWheelSize / 2), solidPaint); + canvas.drawRect(0, mItemHeight * (mWheelSize / 2 + 1), viewWidth, mItemHeight + * (mWheelSize), solidPaint); + canvas.drawRect(0, mItemHeight * (mWheelSize / 2), viewWidth, mItemHeight + * (mWheelSize / 2 + 1), seletedSolidPaint); + canvas.drawLine(0, mItemHeight * (mWheelSize / 2), viewWidth, mItemHeight + * (mWheelSize / 2), dividerPaint); + canvas.drawLine(0, mItemHeight * (mWheelSize / 2 + 1), viewWidth, mItemHeight + * (mWheelSize / 2 + 1), dividerPaint); + } + + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(ColorFilter cf) { + } + + @Override + public int getOpacity() { + return PixelFormat.UNKNOWN; + } + }; + setBackgroundDrawable(backgroud); + } + + private int measureHeight() { + View itemView = LayoutInflater.from(getContext()).inflate(mItemLayoutId, null); + itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + itemView.measure(w, h); + int height = itemView.getMeasuredHeight(); + // int width = view.getMeasuredWidth(); + return height; + } + + public interface WheelItemSelectedListener { + public void onItemSelected(int position, String label); + } + + public class CycleWheelViewException extends Exception { + + private static final long serialVersionUID = 1L; + + public CycleWheelViewException(String detailMessage) { + super(detailMessage); + } + } + + + public class CycleWheelViewAdapter extends BaseAdapter { + private List mData = new ArrayList(); + + public void setData(List mWheelLabels) { + mData.clear(); + mData.addAll(mWheelLabels); + } + + @Override + + + public int getCount() { + if (cylceEnable) { + return Integer.MAX_VALUE; + } + return mData.size() + mWheelSize - 1; + } + + + @Override + public Object getItem(int position) { + return ""; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public boolean isEnabled(int position) { + return false; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(getContext()).inflate(mItemLayoutId, null); + } + TextView textView = (TextView) convertView.findViewById(mItemLabelTvId); + if (position < mWheelSize / 2 + || (!cylceEnable && position >= mData.size() + mWheelSize / 2)) { + textView.setText(""); + convertView.setVisibility(View.INVISIBLE); + } else { + textView.setText(mData.get((position - mWheelSize / 2) % mData.size())); + convertView.setVisibility(View.VISIBLE); + } + return convertView; + } + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java b/utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java new file mode 100644 index 0000000..644a946 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/DrawableTextView.java @@ -0,0 +1,89 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import androidx.appcompat.widget.AppCompatTextView; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/17. + */ + +public class DrawableTextView extends AppCompatTextView { + + public DrawableTextView(Context context) { + super(context); + + } + + public DrawableTextView(Context context, AttributeSet attrs) { + super(context, attrs); + /** + * 取得自定义属性值 + */ + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView); + int drawableWidth = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableWidth, -1); + int drawableHeight = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableHeight, -1); + /** + * 取得TextView的Drawable(左上右下四个组成的数组值) + */ + Drawable[] drawables = getCompoundDrawables(); + Drawable textDrawable = null; + for (Drawable drawable : drawables) { + if (drawable != null) { + textDrawable = drawable; + } + } + /** + * 设置宽高 + */ + if (textDrawable != null && drawableWidth != -1 && drawableHeight != -1) { + textDrawable.setBounds(0, 0, drawableWidth, drawableHeight); + } + /** + * 设置给TextView + */ + setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]); + /** + * 回收ta + */ + ta.recycle(); + } + + public DrawableTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + /** + * 取得自定义属性值 + */ + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView); + int drawableWidth = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableWidth, -1); + int drawableHeight = ta.getDimensionPixelSize(R.styleable.DrawableTextView_drawableHeight, -1); + /** + * 取得TextView的Drawable(左上右下四个组成的数组值) + */ + Drawable[] drawables = getCompoundDrawables(); + Drawable textDrawable = null; + for (Drawable drawable : drawables) { + if (drawable != null) { + textDrawable = drawable; + } + } + /** + * 设置宽高 + */ + if (textDrawable != null && drawableWidth != -1 && drawableHeight != -1) { + textDrawable.setBounds(0, 0, drawableWidth, drawableHeight); + } + /** + * 设置给TextView + */ + setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]); + /** + * 回收ta + */ + ta.recycle(); + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java b/utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java new file mode 100644 index 0000000..3f28321 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/FlowLayoutManager.java @@ -0,0 +1,157 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.graphics.Rect; +import android.util.SparseArray; +import android.view.View; +import androidx.recyclerview.widget.RecyclerView; +import cn.droidlover.xdroidmvp.log.XLog; + +public class FlowLayoutManager extends RecyclerView.LayoutManager { + private final String TAG = this.getClass().getName(); + private SparseArray cachedViews = new SparseArray(); + private SparseArray layoutPoints = new SparseArray<>(); + private int totalWidth; + private int totalHeight; + private int mContentHeight; + private int mOffset; + private boolean mIsFullyLayout; + + public FlowLayoutManager(Context context, boolean isFullyLayout) { + mIsFullyLayout = isFullyLayout; + } + + @Override + public boolean supportsPredictiveItemAnimations() { + return true; + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + for (int i = 0; i < getItemCount(); ++i) { + View v = cachedViews.get(i); + Rect rect = layoutPoints.get(i); + layoutDecorated(v, rect.left, rect.top, rect.right, rect.bottom); + } + + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT); + } + + @Override + public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { + return dx; + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + int shouldOffset = 0; + if (mContentHeight - totalHeight > 0) { + int targetOffset = mOffset + dy; + if (targetOffset < 0) { + targetOffset = 0; + } else if (targetOffset > (mContentHeight - totalHeight)) { + targetOffset = (mContentHeight - totalHeight); + } + shouldOffset = targetOffset - mOffset; + offsetChildrenVertical(-shouldOffset); + mOffset = targetOffset; + } + + if (mIsFullyLayout) { + shouldOffset = dy; + } + return shouldOffset; + } + + @Override + public boolean canScrollHorizontally() { + return true; + } + + @Override + public boolean canScrollVertically() { + return true; + } + + @Override + public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) { + removeAllViews(); + } + + @Override + public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { + super.onMeasure(recycler, state, widthSpec, heightSpec); + final int widthMode = View.MeasureSpec.getMode(widthSpec); + final int heightMode = View.MeasureSpec.getMode(heightSpec); + final int widthSize = View.MeasureSpec.getSize(widthSpec); + final int heightSize = View.MeasureSpec.getSize(heightSpec); + int height; + switch (widthMode) { + case View.MeasureSpec.UNSPECIFIED: + XLog.d(TAG, "WidthMode is unspecified."); + break; + case View.MeasureSpec.AT_MOST: + break; + case View.MeasureSpec.EXACTLY: + break; + default: + break; + } + removeAndRecycleAllViews(recycler); + recycler.clear(); + cachedViews.clear(); + mContentHeight = 0; + totalWidth = widthSize - getPaddingRight() - getPaddingLeft(); + int left = getPaddingLeft(); + int top = getPaddingTop(); + int maxTop = top; + for (int i = 0; i < getItemCount(); ++i) { + View v = recycler.getViewForPosition(i); + addView(v); + measureChildWithMargins(v, 0, 0); + cachedViews.put(i, v); + } + for (int i = 0; i < getItemCount(); ++i) { + View v = cachedViews.get(i); + int w = getDecoratedMeasuredWidth(v); + int h = getDecoratedMeasuredHeight(v); + if (w > totalWidth - left) { + left = getPaddingLeft(); + top = maxTop; + } + Rect rect = new Rect(left, top, left + w, top + h); + layoutPoints.put(i, rect); + left = left + w; + if (top + h >= maxTop) { + maxTop = top + h; + } + } + + mContentHeight = maxTop - getPaddingTop(); + + height = mContentHeight + getPaddingTop() + getPaddingBottom(); + + switch (heightMode) { + case View.MeasureSpec.EXACTLY: + height = heightSize; + break; + case View.MeasureSpec.AT_MOST: + if (height > heightSize) { + height = heightSize; + } + break; + case View.MeasureSpec.UNSPECIFIED: + break; + default: + break; + } + + totalHeight = height - getPaddingTop() - getPaddingBottom(); + + setMeasuredDimension(widthSize, height); + } +} \ No newline at end of file diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java b/utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java new file mode 100644 index 0000000..8b77e7c --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/HeadBar.java @@ -0,0 +1,175 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.lennon.cn.utill.widget.interf.OnHeadBarListener; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/15. + */ + +public class HeadBar extends LinearLayout { + + TextView mLeftTv; + TextView mMidTv; + TextView mRightTv; + ImageView mRightIgv; + + + private Context mContext; + private OnHeadBarListener mOnHeadBarListener; + + public HeadBar(Context context) { + super(context); + } + + public HeadBar(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HeadBar); + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.headbar, this); + inintView(view); + setDefault(typedArray); + } + + public HeadBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + public void setStatusBar(int bar) { + ViewGroup.LayoutParams params = getLayoutParams(); + params.height += bar; + setLayoutParams(params); + setPadding(0, bar, 0, 0); + } + + /** + * 设置监听器 + */ + public void setOnHeadBarListener(OnHeadBarListener listener) { + this.mOnHeadBarListener = listener; + } + + public void setTextColor(int color) { + mLeftTv.setTextColor(color); + mRightTv.setTextColor(color); + mMidTv.setTextColor(color); + } + + /** + * 设置最左边显示栏位提示信息 + */ + public void setLeftMsg(String msg) { + setLeftMsg(R.mipmap.back, msg); + } + + public void setLeftMsg(int id, String msg) { + mLeftTv.setText(msg); + if (id == -1) { + return; + } + Drawable drawable = getResources().getDrawable(id); + drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight()); + mLeftTv.setCompoundDrawables(drawable, null, null, null); + } + + /** + * 设置HeadBar标题 + */ + public void setMidMsg(String msg) { + mMidTv.setVisibility(View.VISIBLE); + mMidTv.setText(msg); + } + + /** + * 设置最右边文字提示信息 + */ + public void setRightMsg(String msg) { + mRightTv.setVisibility(View.VISIBLE); + mRightTv.setText(msg); + mRightTv.setDrawingCacheEnabled(false); + mRightIgv.setVisibility(View.GONE); + } + + public void setRightMsg(int drawableId, String msg) { + mRightTv.setVisibility(View.VISIBLE); + mRightTv.setText(msg); + mRightTv.setDrawingCacheEnabled(true); + mRightIgv.setVisibility(VISIBLE); + mRightIgv.setImageDrawable(getResources().getDrawable(drawableId)); + } + + public void setRightIgv(int drawableId) { + mRightIgv.setVisibility(VISIBLE); + mRightIgv.setImageDrawable(getResources().getDrawable(drawableId)); + } + + private void setDefault(TypedArray typedArray) { + mLeftTv.setText(""); + mLeftTv.setDrawingCacheEnabled(false); + mMidTv.setText(""); + setRightMsg(""); + if (typedArray != null) { + mLeftTv.setTextColor(typedArray.getColor(R.styleable.HeadBar_text_color, Color.WHITE)); + mRightTv.setTextColor(typedArray.getColor(R.styleable.HeadBar_text_color, Color.WHITE)); + mMidTv.setTextColor(typedArray.getColor(R.styleable.HeadBar_text_color, Color.WHITE)); + } + mRightTv.setVisibility(GONE); + mRightIgv.setVisibility(GONE); + } + + private void inintView(View v) { + mLeftTv = (TextView) v.findViewById(R.id.headbar_left_tv); + mLeftTv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onLeft(); + } + } + }); + mMidTv = (TextView) v.findViewById(R.id.headbar_mid_tv); + mMidTv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onMid(); + } + } + }); + mRightTv = (TextView) v.findViewById(R.id.headbar_right_tv); + mRightTv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onRight(); + } + } + }); + mRightIgv = (ImageView) v.findViewById(R.id.headbar_right_igv); + mRightIgv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onRight(); + } + } + }); + } + + public void onRight() { + if (null != mOnHeadBarListener) { + mOnHeadBarListener.onRight(); + } + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java b/utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java new file mode 100644 index 0000000..f7cc6f7 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/MyRatingBar.java @@ -0,0 +1,244 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import lennon.com.utill.R; + + +/** + * Created by fulianchen on 24/7/13. + */ +public class MyRatingBar extends View { + //星星水平排列 + public static final int HORIZONTAL = 0; + //星星垂直排列 + public static final int VERTICAL = 1; + //实心图片 + private Bitmap mSolidBitmap; + //空心图片 + private Bitmap mHollowBitmap; + private Context context; + //最大的数量 + private int starMaxNumber; + private float starRating; + private Paint paint; + private int mSpaceWidth;//星星间隔 + private int mStarWidth;//星星宽度 + private int mStarHeight;//星星高度 + private boolean isIndicator;//是否是一个指示器(用户无法进行更改) + private int mOrientation; + + public MyRatingBar(Context context, AttributeSet attrs) { + super(context, attrs); + paint = new Paint(); + this.context = context; + TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyRatingBar); + mSpaceWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_space_width, 0); + mStarWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_width, 0); + mStarHeight = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_height, 0); + starMaxNumber = a.getInt(R.styleable.MyRatingBar_star_max, 0); + starRating = a.getFloat(R.styleable.MyRatingBar_star_rating, 0); + isIndicator = a.getBoolean(R.styleable.MyRatingBar_star_isIndicator, true); + mSolidBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(), a.getResourceId(R.styleable.MyRatingBar_star_solid, 0))); + mHollowBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(),a.getResourceId(R.styleable.MyRatingBar_star_hollow, 0))); + mOrientation = a.getInt(R.styleable.MyRatingBar_star_orientation, HORIZONTAL); + a.recycle(); + } + + public MyRatingBar(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + paint = new Paint(); + this.context = context; + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyRatingBar, defStyle, 0); + mSpaceWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_space_width, 0); + mStarWidth = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_width, 0); + mStarHeight = a.getDimensionPixelSize(R.styleable.MyRatingBar_star_height, 0); + starMaxNumber = a.getInt(R.styleable.MyRatingBar_star_max, 0); + starRating = a.getFloat(R.styleable.MyRatingBar_star_rating, 0); + mSolidBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(), a.getResourceId(R.styleable.MyRatingBar_star_solid, 0))); + mHollowBitmap = getZoomBitmap(BitmapFactory.decodeResource(context.getResources(),a.getResourceId(R.styleable.MyRatingBar_star_hollow, 0))); + mOrientation = a.getInt(R.styleable.MyRatingBar_star_orientation, HORIZONTAL); + a.recycle(); + } + + @Override + protected void onDraw(Canvas canvas) { + if(mHollowBitmap ==null|| mSolidBitmap ==null){ + return; + } + //绘制实心进度 + int solidStarNum = (int)starRating; + //绘制实心的起点位置 + int solidStartPoint = 0; + for(int i=1;i<=solidStarNum;i++){ + canvas.drawBitmap(mSolidBitmap, solidStartPoint, 0, paint); + solidStartPoint = solidStartPoint + mSpaceWidth + mHollowBitmap.getWidth(); + } + //虚心开始位置 + int hollowStartPoint = solidStartPoint; + //多出的实心部分起点 + int extraSolidStarPoint = hollowStartPoint; + //虚心数量 + int hollowStarNum = starMaxNumber - solidStarNum; + for(int j = 1;j<=hollowStarNum;j++){ + canvas.drawBitmap(mHollowBitmap, hollowStartPoint, 0, paint); + hollowStartPoint = hollowStartPoint + mSpaceWidth + mSolidBitmap.getWidth(); + } + //多出的实心长度 + int extraSolidLength = (int)((starRating - solidStarNum)* mHollowBitmap.getWidth()); + Rect rectSrc = new Rect(0,0,extraSolidLength, mHollowBitmap.getHeight()); + Rect dstF = new Rect(extraSolidStarPoint,0,extraSolidStarPoint+extraSolidLength, mHollowBitmap.getHeight()); + canvas.drawBitmap(mSolidBitmap, rectSrc, dstF, paint); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if(!isIndicator){ + switch (event.getAction()){ + case MotionEvent.ACTION_DOWN: + float starTotalWidth = starMaxNumber *(mStarWidth+mSpaceWidth)-mSpaceWidth; + if(event.getX()<=starTotalWidth){ + float newStarRating = (int)event.getX()/(mStarWidth+mSpaceWidth)+1; + setStarRating(newStarRating); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + + break; + } + } + return super.onTouchEvent(event); + } + + /** + * 设置星星的进度 + * @param starRating + */ + public void setStarRating(float starRating){ + this.starRating = starRating; + invalidate(); + } + + public float getStarRating() { + return starRating; + } + + + /** + * 获取缩放图片 + * @param bitmap + * @return + */ + public Bitmap getZoomBitmap(Bitmap bitmap){ + if(mStarWidth==0||mStarHeight==0){ + return bitmap; + } + // 获得图片的宽高 + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + // 设置想要的大小 + int newWidth = mStarWidth; + int newHeight = mStarHeight; + // 计算缩放比例 + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + // 取得想要缩放的matrix参数 + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + // 得到新的图片 + Bitmap newbm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, + true); + return newbm; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mOrientation==HORIZONTAL) { + setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec)); + } else { + setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec)); + } + } + + /** + * Determines the width of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The width of the view, honoring constraints from measureSpec + */ + private int measureLong(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if ((specMode == MeasureSpec.EXACTLY)) { + //We were told how big to be + result = specSize; + } else { + //Calculate the width according the views count + result = (int)(getPaddingLeft() + getPaddingRight() + + (mSpaceWidth+mStarWidth)*(starMaxNumber)); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + /** + * Determines the height of this view + * + * @param measureSpec + * A measureSpec packed into an int + * @return The height of the view, honoring constraints from measureSpec + */ + private int measureShort(int measureSpec) { + int result; + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + + if (specMode == MeasureSpec.EXACTLY) { + //We were told how big to be + result = specSize; + } else { + //Measure the height + result = (int)(mStarHeight + getPaddingTop() + getPaddingBottom()); + //Respect AT_MOST value if that was what is called for by measureSpec + if (specMode == MeasureSpec.AT_MOST) { + result = Math.min(result, specSize); + } + } + return result; + } + + public int getStarMaxNumber() { + return starMaxNumber; + } + + public void setStarMaxNumber(int starMaxNumber) { + this.starMaxNumber = starMaxNumber; + invalidate(); + } + + public boolean isIndicator() { + return isIndicator; + } + + public void setIsIndicator(boolean isIndicator) { + this.isIndicator = isIndicator; + } +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java b/utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java new file mode 100644 index 0000000..a2dd481 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/SearchScanView.java @@ -0,0 +1,224 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.*; +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/22. + */ + +public class SearchScanView extends LinearLayout { + + EditText mInputEdt; + ImageView mDeleteIgv; + ListView mLv; + TextView search; + ImageView scanImg; + + private Context mContext; + private SearchViewListener mListener; + + /** + * 提示adapter (推荐adapter) + */ + private ArrayAdapter mHistoryAdapter; + + public SearchScanView(Context context) { + super(context); + } + + public SearchScanView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + View view = LayoutInflater.from(context).inflate(R.layout.search_scan_view_layout, this); + inintView(view); + try { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SearchView); + mInputEdt.setHint(array.getString(R.styleable.SearchView_search_hint)); + if (array.getBoolean(R.styleable.SearchView_show_button, false)) { + search.setVisibility(View.VISIBLE); + } else { + search.setVisibility(View.GONE); + } + } catch (Exception e) { + e.printStackTrace(); + } + mInputEdt.addTextChangedListener(new EditChangedListener()); + mInputEdt.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + //关闭软键盘 + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + mListener.onSearchByButton(getText()); + return true; + } + return false; + } + }); + search.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + mListener.onSearchByButton(getText()); + } + }); + mDeleteIgv.setVisibility(GONE); + mLv.setOnItemClickListener(mOnItemClickListener); + } + + public SearchScanView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setSearchViewListener(SearchViewListener listener) { + this.mListener = listener; + } + + public void setText(String text) { + mInputEdt.setText(text); + } + + public String getText() { + return mInputEdt.getText().toString(); + } + + private void inintView(View v) { + mInputEdt = (EditText) v.findViewById(R.id.searchview_input_edt); + mDeleteIgv = (ImageView) v.findViewById(R.id.search_delete_igv); + search = v.findViewById(R.id.search_search); + mDeleteIgv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + }); + scanImg = v.findViewById(R.id.scan_img); + scanImg.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mListener != null) { + mListener.onScan(); + } + } + }); + mLv = (ListView) v.findViewById(R.id.searchview_lv); + } + + public void clearSearch() { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + + /** + * 通知监听者 进行搜索操作 + * + * @param text + */ + private void notifyStartSearching(String text) { + if (mListener != null) { + mListener.onResult(text); + } + //隐藏软键盘 + mLv.setVisibility(GONE); + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 设置热搜版提示 adapter + */ + public void setHistoryAdapter(ArrayAdapter adapter) { + this.mHistoryAdapter = adapter; + if (mLv.getAdapter() == null) { + mLv.setAdapter(mHistoryAdapter); + } + } + + private class EditChangedListener implements TextWatcher { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + if (!"".equals(charSequence.toString())) { + mDeleteIgv.setVisibility(VISIBLE); + mLv.setVisibility(GONE); + //更新autoComplete数据 + if (mListener != null) { + mListener.onSearch(charSequence + ""); + } + } else { + mDeleteIgv.setVisibility(GONE); + if (mHistoryAdapter != null) { + mLv.setAdapter(mHistoryAdapter); + } + if (null != mListener) { + mListener.onClear(); + } + mLv.setVisibility(VISIBLE); + } + + } + + @Override + public void afterTextChanged(Editable editable) { + } + } + + private AdapterView.OnItemClickListener mOnItemClickListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (i < mHistoryAdapter.getCount() - 1) { + String key = mHistoryAdapter.getItem(i).toString(); + notifyStartSearching(key); + } + } + }; + + /** + * search view回调方法 + */ + public interface SearchViewListener { + + /** + * 清除搜索内容 + */ + void onClear(); + + /** + * 更新自动补全内容 + * + * @param text + */ + void onSearch(String text); + + /** + * 搜索结果 + * + * @param text + */ + void onResult(String text); + + void onSearchByButton(String text); + + void onScan(); + + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java b/utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java new file mode 100644 index 0000000..600f978 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/SearchView.java @@ -0,0 +1,217 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.*; + +import lennon.com.utill.R; + +/** + * Created by dingyi on 2016/11/22. + */ + +public class SearchView extends LinearLayout { + + EditText mInputEdt; + ImageView mDeleteIgv; + ListView mLv; + TextView search; + + private Context mContext; + private SearchViewListener mListener; + + /** + * 提示adapter (推荐adapter) + */ + private ArrayAdapter mHistoryAdapter; + + public SearchView(Context context) { + super(context); + } + + public SearchView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + View view = LayoutInflater.from(context).inflate(R.layout.searchview_layout, this); + inintView(view); + try { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SearchView); + mInputEdt.setHint(array.getString(R.styleable.SearchView_search_hint)); + if (array.getBoolean(R.styleable.SearchView_show_button, false)) { + search.setVisibility(View.VISIBLE); + } else { + search.setVisibility(View.GONE); + } + } catch (Exception e) { + e.printStackTrace(); + } + mInputEdt.addTextChangedListener(new EditChangedListener()); + mInputEdt.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + //关闭软键盘 + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + if (mListener != null) { + mListener.onSearchByButton(getText()); + } + return true; + } + return false; + } + }); + search.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + if (mListener != null) { + mListener.onSearchByButton(getText()); + } + } + }); + mDeleteIgv.setVisibility(GONE); + mLv.setOnItemClickListener(mOnItemClickListener); + } + + public SearchView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public void setSearchViewListener(SearchViewListener listener) { + this.mListener = listener; + } + + public void setText(String text) { + mInputEdt.setText(text); + } + + public String getText() { + return mInputEdt.getText().toString(); + } + + private void inintView(View v) { + mInputEdt = (EditText) v.findViewById(R.id.searchview_input_edt); + mDeleteIgv = (ImageView) v.findViewById(R.id.search_delete_igv); + search = v.findViewById(R.id.search_search); + mDeleteIgv.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + }); + mLv = (ListView) v.findViewById(R.id.searchview_lv); + } + + public void clearSearch() { + mInputEdt.setText(""); + mDeleteIgv.setVisibility(GONE); + } + + /** + * 通知监听者 进行搜索操作 + * + * @param text + */ + private void notifyStartSearching(String text) { + if (mListener != null) { + mListener.onResult(text); + } + //隐藏软键盘 + mLv.setVisibility(GONE); + InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS); + } + + /** + * 设置热搜版提示 adapter + */ + public void setHistoryAdapter(ArrayAdapter adapter) { + this.mHistoryAdapter = adapter; + if (mLv.getAdapter() == null) { + mLv.setAdapter(mHistoryAdapter); + } + } + + private class EditChangedListener implements TextWatcher { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { + if (!"".equals(charSequence.toString())) { + mDeleteIgv.setVisibility(VISIBLE); + mLv.setVisibility(GONE); + //更新autoComplete数据 + if (mListener != null) { + mListener.onSearch(charSequence + ""); + } + } else { + mDeleteIgv.setVisibility(GONE); + if (mHistoryAdapter != null) { + mLv.setAdapter(mHistoryAdapter); + } + if (null != mListener) { + mListener.onClear(); + } + mLv.setVisibility(VISIBLE); + } + + } + + @Override + public void afterTextChanged(Editable editable) { + } + } + + private AdapterView.OnItemClickListener mOnItemClickListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (i < mHistoryAdapter.getCount() - 1) { + String key = mHistoryAdapter.getItem(i).toString(); + notifyStartSearching(key); + } + } + }; + + /** + * search view回调方法 + */ + public interface SearchViewListener { + + /** + * 清除搜索内容 + */ + void onClear(); + + /** + * 更新自动补全内容 + * + * @param text + */ + void onSearch(String text); + + /** + * 搜索结果 + * + * @param text + */ + void onResult(String text); + + void onSearchByButton(String text); + + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/Star.java b/utill/src/main/java/com/lennon/cn/utill/widget/Star.java new file mode 100644 index 0000000..3cef6ca --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/Star.java @@ -0,0 +1,178 @@ +package com.lennon.cn.utill.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import lennon.com.utill.R; + +/** + * 自定义评价星星类(任何形状) + * yueleng on 2017/1/9. + */ + +public class Star extends View { + //星星评分 + private float starMark = 0.0F; + //星星个数 + private int starNum = 5; + //星星高度 + private int starHeight; + //星星宽度 + private int starWidth; + //星星间距 + private int starDistance; + //星星背景 + private Drawable starBackgroundBitmap; + //动态星星 + private Bitmap starDrawDrawable; + //星星变化监听 + private OnStarChangeListener changeListener; + //是否可以点击 + private boolean isClick = true; + //画笔 + private Paint mPaint; + + public Star(Context mContext, AttributeSet attrs) { + super(mContext, attrs); + init(mContext, attrs); + } + + public Star(Context mContext, AttributeSet attrs, int defStyleAttr) { + super(mContext, attrs, defStyleAttr); + init(mContext, attrs); + } + + private void init(Context mContext, AttributeSet attrs) { + + //初始化控件属性 + TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.star); + starNum = typedArray.getInteger(R.styleable.star_starsNum, 5); + starHeight = (int) typedArray.getDimension(R.styleable.star_starHeight, 0); + starWidth = (int) typedArray.getDimension(R.styleable.star_starWidth, 0); + starDistance = (int) typedArray.getDimension(R.styleable.star_starDistance, 0); + isClick = typedArray.getBoolean(R.styleable.star_starClickable, true); + starBackgroundBitmap = typedArray.getDrawable(R.styleable.star_starBackground); + starDrawDrawable = drawableToBitmap(typedArray.getDrawable(R.styleable.star_starDrawBackground)); + typedArray.recycle(); + setClickable(isClick); + //初始化画笔 + mPaint = new Paint(); + //设置抗锯齿 + mPaint.setAntiAlias(true); + mPaint.setShader(new BitmapShader(starDrawDrawable, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension(starWidth * starNum + starDistance * (starNum - 1), starHeight); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (null == starDrawDrawable || null == starBackgroundBitmap) { + return; + } + for (int i = 0; i < starNum; i++) { + starBackgroundBitmap.setBounds(starDistance * i + starWidth * i, 0, starWidth * (i + 1) + starDistance * i, starHeight); + starBackgroundBitmap.draw(canvas); + } + if (starMark > 1) { + canvas.drawRect(0, 0, starWidth, starHeight, mPaint); + if (starMark - (int) (starMark) == 0) { + for (int i = 1; i < starMark; i++) { + canvas.translate(starDistance + starWidth, 0); + canvas.drawRect(0, 0, starWidth, starHeight, mPaint); + } + } else { + for (int i = 1; i < starMark - 1; i++) { + canvas.translate(starDistance + starWidth, 0); + canvas.drawRect(0, 0, starWidth, starHeight, mPaint); + } + canvas.translate(starDistance + starWidth, 0); + canvas.drawRect(0, 0, starWidth * (Math.round((starMark - (int) (starMark)) * 10) * 1.0f / 10), starHeight, mPaint); + } + } else { + canvas.drawRect(0, 0, starWidth * starMark, starHeight, mPaint); + } + + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int x = (int) event.getX(); + if (x < 0) + x = 0; + if (x > getMeasuredWidth()) + x = getMeasuredWidth(); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum)); + break; + case MotionEvent.ACTION_MOVE: + setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum)); + break; + case MotionEvent.ACTION_UP: + setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum)); + break; + } + return true; + } + + /** + * 设置分数 + */ + public void setMark(Float mark) { + starMark = Math.round(mark * 10) * 1.0f / 10; + if (null != changeListener) { + changeListener.onStarChange(starMark); + } + invalidate(); + } + + /** + * 设置监听 + */ + public void setStarChangeLister(OnStarChangeListener starChangeLister) { + changeListener = starChangeLister; + } + + /** + * 获取分数 + */ + public float getMark() { + return starMark; + } + + /** + * 星星数量变化监听接口 + */ + public interface OnStarChangeListener { + void onStarChange(Float mark); + } + + /** + * drawable转bitmap + */ + private Bitmap drawableToBitmap(Drawable drawable) { + if (drawable == null) { + return null; + } + Bitmap bitmap = Bitmap.createBitmap(starWidth, starHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, starWidth, starHeight); + drawable.draw(canvas); + return bitmap; + } + +} diff --git a/utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java b/utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java new file mode 100644 index 0000000..1592ef3 --- /dev/null +++ b/utill/src/main/java/com/lennon/cn/utill/widget/interf/OnHeadBarListener.java @@ -0,0 +1,12 @@ +package com.lennon.cn.utill.widget.interf; + +/** + * 头部导航栏监听器 + * Created by dingyi on 2016/11/15. + */ + +public class OnHeadBarListener{ + public void onLeft(){}; + public void onMid(){}; + public void onRight(){}; +} diff --git a/utill/src/main/res/anim/pull_in_left.xml b/utill/src/main/res/anim/pull_in_left.xml new file mode 100644 index 0000000..ddc965d --- /dev/null +++ b/utill/src/main/res/anim/pull_in_left.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/utill/src/main/res/anim/pull_in_right.xml b/utill/src/main/res/anim/pull_in_right.xml new file mode 100644 index 0000000..52e0996 --- /dev/null +++ b/utill/src/main/res/anim/pull_in_right.xml @@ -0,0 +1,10 @@ + + + + diff --git a/utill/src/main/res/anim/push_out_left.xml b/utill/src/main/res/anim/push_out_left.xml new file mode 100644 index 0000000..daad3da --- /dev/null +++ b/utill/src/main/res/anim/push_out_left.xml @@ -0,0 +1,10 @@ + + + + diff --git a/utill/src/main/res/anim/push_out_right.xml b/utill/src/main/res/anim/push_out_right.xml new file mode 100644 index 0000000..699897f --- /dev/null +++ b/utill/src/main/res/anim/push_out_right.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/utill/src/main/res/anim/pw_push_bottom_in.xml b/utill/src/main/res/anim/pw_push_bottom_in.xml new file mode 100644 index 0000000..348e755 --- /dev/null +++ b/utill/src/main/res/anim/pw_push_bottom_in.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/utill/src/main/res/anim/pw_push_bottom_out.xml b/utill/src/main/res/anim/pw_push_bottom_out.xml new file mode 100644 index 0000000..6f72ad6 --- /dev/null +++ b/utill/src/main/res/anim/pw_push_bottom_out.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/utill/src/main/res/anim/scale_in.xml b/utill/src/main/res/anim/scale_in.xml new file mode 100644 index 0000000..55e3d02 --- /dev/null +++ b/utill/src/main/res/anim/scale_in.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/anim/scale_out.xml b/utill/src/main/res/anim/scale_out.xml new file mode 100644 index 0000000..259d515 --- /dev/null +++ b/utill/src/main/res/anim/scale_out.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/btn_ac5454.xml b/utill/src/main/res/drawable/btn_ac5454.xml new file mode 100644 index 0000000..7382b7d --- /dev/null +++ b/utill/src/main/res/drawable/btn_ac5454.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/btn_ac5454_f.xml b/utill/src/main/res/drawable/btn_ac5454_f.xml new file mode 100644 index 0000000..5a9ab46 --- /dev/null +++ b/utill/src/main/res/drawable/btn_ac5454_f.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/btn_bfbfbf_frame.xml b/utill/src/main/res/drawable/btn_bfbfbf_frame.xml new file mode 100644 index 0000000..49e2363 --- /dev/null +++ b/utill/src/main/res/drawable/btn_bfbfbf_frame.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_button.xml b/utill/src/main/res/drawable/common_button.xml new file mode 100644 index 0000000..27028bd --- /dev/null +++ b/utill/src/main/res/drawable/common_button.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_button_bfbfbf.xml b/utill/src/main/res/drawable/common_button_bfbfbf.xml new file mode 100644 index 0000000..4a359ee --- /dev/null +++ b/utill/src/main/res/drawable/common_button_bfbfbf.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_button_fffff.xml b/utill/src/main/res/drawable/common_button_fffff.xml new file mode 100644 index 0000000..17c5c53 --- /dev/null +++ b/utill/src/main/res/drawable/common_button_fffff.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/common_dialog_frame.xml b/utill/src/main/res/drawable/common_dialog_frame.xml new file mode 100644 index 0000000..2830244 --- /dev/null +++ b/utill/src/main/res/drawable/common_dialog_frame.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_f2f2f2.xml b/utill/src/main/res/drawable/conner_f2f2f2.xml new file mode 100644 index 0000000..e88451c --- /dev/null +++ b/utill/src/main/res/drawable/conner_f2f2f2.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_fd0202.xml b/utill/src/main/res/drawable/conner_fd0202.xml new file mode 100644 index 0000000..27028bd --- /dev/null +++ b/utill/src/main/res/drawable/conner_fd0202.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_ffffff.xml b/utill/src/main/res/drawable/conner_ffffff.xml new file mode 100644 index 0000000..0dd9ff4 --- /dev/null +++ b/utill/src/main/res/drawable/conner_ffffff.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/conner_right_fd0202.xml b/utill/src/main/res/drawable/conner_right_fd0202.xml new file mode 100644 index 0000000..96bc3b2 --- /dev/null +++ b/utill/src/main/res/drawable/conner_right_fd0202.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/linearlayout_fffff_frame.xml b/utill/src/main/res/drawable/linearlayout_fffff_frame.xml new file mode 100644 index 0000000..b4eae12 --- /dev/null +++ b/utill/src/main/res/drawable/linearlayout_fffff_frame.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/price_selector.xml b/utill/src/main/res/drawable/price_selector.xml new file mode 100644 index 0000000..4501886 --- /dev/null +++ b/utill/src/main/res/drawable/price_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_line.xml b/utill/src/main/res/drawable/table_line.xml new file mode 100644 index 0000000..74576f9 --- /dev/null +++ b/utill/src/main/res/drawable/table_line.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_radio.xml b/utill/src/main/res/drawable/table_radio.xml new file mode 100644 index 0000000..62ef00c --- /dev/null +++ b/utill/src/main/res/drawable/table_radio.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_select.xml b/utill/src/main/res/drawable/table_select.xml new file mode 100644 index 0000000..6c8fc3d --- /dev/null +++ b/utill/src/main/res/drawable/table_select.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/table_unselect.xml b/utill/src/main/res/drawable/table_unselect.xml new file mode 100644 index 0000000..0c75b99 --- /dev/null +++ b/utill/src/main/res/drawable/table_unselect.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/drawable/web_progress.xml b/utill/src/main/res/drawable/web_progress.xml new file mode 100644 index 0000000..4b52c78 --- /dev/null +++ b/utill/src/main/res/drawable/web_progress.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/common_alert_dialog.xml b/utill/src/main/res/layout/common_alert_dialog.xml new file mode 100644 index 0000000..410d110 --- /dev/null +++ b/utill/src/main/res/layout/common_alert_dialog.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/custom_dialog.xml b/utill/src/main/res/layout/custom_dialog.xml new file mode 100644 index 0000000..489c8e3 --- /dev/null +++ b/utill/src/main/res/layout/custom_dialog.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/customprogressdialog.xml b/utill/src/main/res/layout/customprogressdialog.xml new file mode 100644 index 0000000..489c8e3 --- /dev/null +++ b/utill/src/main/res/layout/customprogressdialog.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/dialog_bottom_search_select.xml b/utill/src/main/res/layout/dialog_bottom_search_select.xml new file mode 100644 index 0000000..5b456b0 --- /dev/null +++ b/utill/src/main/res/layout/dialog_bottom_search_select.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/dialog_bottom_select.xml b/utill/src/main/res/layout/dialog_bottom_select.xml new file mode 100644 index 0000000..a211c9b --- /dev/null +++ b/utill/src/main/res/layout/dialog_bottom_select.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/headbar.xml b/utill/src/main/res/layout/headbar.xml new file mode 100644 index 0000000..b03c19f --- /dev/null +++ b/utill/src/main/res/layout/headbar.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/item_base_view.xml b/utill/src/main/res/layout/item_base_view.xml new file mode 100644 index 0000000..1599e8d --- /dev/null +++ b/utill/src/main/res/layout/item_base_view.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/item_cyclewheel.xml b/utill/src/main/res/layout/item_cyclewheel.xml new file mode 100644 index 0000000..e90b18f --- /dev/null +++ b/utill/src/main/res/layout/item_cyclewheel.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/item_string_item.xml b/utill/src/main/res/layout/item_string_item.xml new file mode 100644 index 0000000..54824a2 --- /dev/null +++ b/utill/src/main/res/layout/item_string_item.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/search_scan_view_layout.xml b/utill/src/main/res/layout/search_scan_view_layout.xml new file mode 100644 index 0000000..9880ebc --- /dev/null +++ b/utill/src/main/res/layout/search_scan_view_layout.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/layout/searchview_layout.xml b/utill/src/main/res/layout/searchview_layout.xml new file mode 100644 index 0000000..1086374 --- /dev/null +++ b/utill/src/main/res/layout/searchview_layout.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/mipmap-hdpi/ic_rating_bar_hollow.png b/utill/src/main/res/mipmap-hdpi/ic_rating_bar_hollow.png new file mode 100644 index 0000000..5f7c977 Binary files /dev/null and b/utill/src/main/res/mipmap-hdpi/ic_rating_bar_hollow.png differ diff --git a/utill/src/main/res/mipmap-hdpi/ic_rating_bar_solid.png b/utill/src/main/res/mipmap-hdpi/ic_rating_bar_solid.png new file mode 100644 index 0000000..b8d646d Binary files /dev/null and b/utill/src/main/res/mipmap-hdpi/ic_rating_bar_solid.png differ diff --git a/utill/src/main/res/mipmap-xhdpi/back.png b/utill/src/main/res/mipmap-xhdpi/back.png new file mode 100644 index 0000000..e19369f Binary files /dev/null and b/utill/src/main/res/mipmap-xhdpi/back.png differ diff --git a/utill/src/main/res/mipmap-xxhdpi/cha2.png b/utill/src/main/res/mipmap-xxhdpi/cha2.png new file mode 100644 index 0000000..5a123ec Binary files /dev/null and b/utill/src/main/res/mipmap-xxhdpi/cha2.png differ diff --git a/utill/src/main/res/mipmap-xxhdpi/scan.png b/utill/src/main/res/mipmap-xxhdpi/scan.png new file mode 100644 index 0000000..7d6b793 Binary files /dev/null and b/utill/src/main/res/mipmap-xxhdpi/scan.png differ diff --git a/utill/src/main/res/raw/beep.ogg b/utill/src/main/res/raw/beep.ogg new file mode 100644 index 0000000..dc6e719 Binary files /dev/null and b/utill/src/main/res/raw/beep.ogg differ diff --git a/utill/src/main/res/values/attrs.xml b/utill/src/main/res/values/attrs.xml new file mode 100644 index 0000000..f02d064 --- /dev/null +++ b/utill/src/main/res/values/attrs.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + //星星数量 + + //星星宽度 + + //星星高度 + + //星星间距 + + //星星背景 + + //星星变化背景 + + //控件是否可以点击 + + + + + + + + + + + + + + + + diff --git a/utill/src/main/res/values/color.xml b/utill/src/main/res/values/color.xml new file mode 100644 index 0000000..dc192bc --- /dev/null +++ b/utill/src/main/res/values/color.xml @@ -0,0 +1,43 @@ + + + #BF0001 + #00000000 + #333333 + #00000000 + #50000000 + #EB0000 + #b5b5b5 + #dcdcdc + #cf333333 + #d3d3d3 + #FFFFFF + #ffffff + #7f000000 + #dddddd + #e6e6e6 + #f2f2f2 + #DBDBDB + #ffcc00 + #dcdcdc + #f20c0c + #bfbfbf + #777777 + #c60c1b + #b22222 + #fd0202 + #f82629 + #ac5454 + #6495ed + #3F51B5 + #0f83d0 + #878787 + #999999 + #CCCCCC + #EAEAEA + #EAEAEA + #DDDDDD + #666666 + #6c6c6c + #d82043 + #737373 + \ No newline at end of file diff --git a/utill/src/main/res/values/dimens.xml b/utill/src/main/res/values/dimens.xml new file mode 100644 index 0000000..6bcda6d --- /dev/null +++ b/utill/src/main/res/values/dimens.xml @@ -0,0 +1,73 @@ + + + + 45dp + + 10sp + 12sp + 14sp + 16sp + 18sp + 20sp + 22sp + 24sp + 30sp + + -50dp + -60dp + -70dp + -75dp + + + 0.5dp + 0dp + 1dp + 2dp + 3dp + 4dp + 5dp + 6dp + 7dp + 8dp + 10dp + 12dp + 14dp + 15dp + 16dp + 18dp + 20dp + 22dp + 25dp + 26dp + 30dp + 40dp + 45dp + 46dp + 48dp + 50dp + 55dp + 56dp + 60dp + 70dp + 80dp + 85dp + 90dp + 100dp + 110dp + 120dp + 130dp + 135dp + 140dp + 150dp + 165dp + 170dp + 180dp + 200dp + 250dp + 300dp + 320dp + 340dp + 350dp + 425dp + + \ No newline at end of file diff --git a/utill/src/main/res/values/strings.xml b/utill/src/main/res/values/strings.xml new file mode 100644 index 0000000..ac876e3 --- /dev/null +++ b/utill/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + Utill + 获取验证码 + 重新获取 + + 请输入烟品名称 + diff --git a/utill/src/main/res/values/style.xml b/utill/src/main/res/values/style.xml new file mode 100644 index 0000000..fa3678b --- /dev/null +++ b/utill/src/main/res/values/style.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utill/src/main/res/values/values.xml b/utill/src/main/res/values/values.xml new file mode 100644 index 0000000..e35e82f --- /dev/null +++ b/utill/src/main/res/values/values.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/utill/uploadAliRelease.gradle b/utill/uploadAliRelease.gradle new file mode 100644 index 0000000..b916e46 --- /dev/null +++ b/utill/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/utill/uploadAliSnapshot.gradle b/utill/uploadAliSnapshot.gradle new file mode 100644 index 0000000..72490f0 --- /dev/null +++ b/utill/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/utill/uploadLocal.gradle b/utill/uploadLocal.gradle new file mode 100644 index 0000000..e371e0d --- /dev/null +++ b/utill/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'lennon-utill' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/xrecycler/build.gradle b/xrecycler/build.gradle new file mode 100644 index 0000000..8131bd2 --- /dev/null +++ b/xrecycler/build.gradle @@ -0,0 +1,64 @@ +apply plugin: 'com.android.library' + +group = rootProject.groupId +version = '1.0.7' // 版本 + +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + +} + +dependencies { + api fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies["recyclerview-v7"] + api rootProject.ext.dependencies["annotations"] + api rootProject.ext.dependencies["XStateController"] + api rootProject.ext.dependencies["support-v4"] +} + + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +// 生成sourceJar和javaDocJar构件 +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile +} +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +apply from:'uploadLocal.gradle' +//apply from:'uploadAliRelease.gradle' +//apply from:'uploadAliSnapshot.gradle' \ No newline at end of file diff --git a/xrecycler/src/main/AndroidManifest.xml b/xrecycler/src/main/AndroidManifest.xml new file mode 100644 index 0000000..78feb01 --- /dev/null +++ b/xrecycler/src/main/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java new file mode 100644 index 0000000..bb624bd --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/LoadMoreUIHandler.java @@ -0,0 +1,11 @@ +package cn.droidlover.xrecyclerview; + +/** + * Created by wanglei on 2016/10/30. + */ + +public interface LoadMoreUIHandler { + void onLoading(); + + void onLoadFinish(boolean hasMore); +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java new file mode 100644 index 0000000..0ed585c --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerAdapter.java @@ -0,0 +1,302 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Created by wanglei on 2016/10/30. + */ + +public abstract class RecyclerAdapter extends RecyclerView.Adapter { + + protected Context context; + protected List data = new ArrayList(); + private RecyclerItemCallback recItemClick; + + public RecyclerAdapter(Context context) { + this.context = context; + } + + /** + * 设置数据源 + * + * @param data + */ + public void setData(List data) { + this.data.clear(); + if (data != null) { + this.data.addAll(data); + } + notifyDataSetChanged(); + } + + /** + * 设置数据源 + * + * @param data + */ + public void setData(T[] data) { + if (data != null && data.length > 0) { + setData(Arrays.asList(data)); + } + } + + /** + * 添加数据 + * + * @param data + */ + public void addData(List data) { + int preSize = this.data.size(); + if (data != null && data.size() > 0) { + if (this.data == null) { + this.data = new ArrayList(); + } + this.data.addAll(data); + notifyItemRangeInserted(preSize, this.data.size()); + } + notifyDataSetChanged(); + } + + /** + * 添加数据 + * + * @param data + */ + public void addData(T[] data) { + addData(Arrays.asList(data)); + } + + + /** + * 删除元素 + * + * @param element + */ + public void removeElement(T element) { + if (data.contains(element)) { + int position = data.indexOf(element); + data.remove(element); + notifyItemRemoved(position); + if (position < data.size()) { + notifyItemChanged(position,data.size()-position); + } + } + } + + /** + * 删除元素 + * + * @param position + */ + public void removeElement(int position) { + if (data != null && data.size() > position) { + data.remove(position); + notifyItemRemoved(position); + if (position < data.size()) { + notifyItemChanged(position,data.size()-position); + } + } + } + + /** + * 删除元素 + * + * @param elements + */ + public void removeElements(List elements) { + if (data != null && elements != null && elements.size() > 0 + && data.size() >= elements.size()) { + + for (T element : elements) { + if (data.contains(element)) { + data.remove(element); + } + } + + notifyDataSetChanged(); + } + } + + /** + * 删除元素 + * + * @param elements + */ + public void removeElements(T[] elements) { + if (elements != null && elements.length > 0) { + removeElements(Arrays.asList(elements)); + } + } + + /** + * 更新元素 + * + * @param element + * @param position + */ + public void updateElement(T element, int position) { + if (position >= 0 && data.size() > position) { + data.remove(position); + data.add(position, element); + notifyItemChanged(position); + } + } + + /** + * 添加元素 + * + * @param element + */ + public void addElement(T element) { + if (element != null) { + if (this.data == null) { + this.data = new ArrayList(); + } + data.add(element); + notifyItemInserted(this.data.size()); + } + } + + public void addElement(int position, T element) { + if (element != null) { + if (this.data == null) { + this.data = new ArrayList(); + } + data.add(position, element); + notifyItemInserted(position); + } + } + + /** + * 清除数据源 + */ + public void clearData() { + if (this.data != null) { + this.data.clear(); + notifyDataSetChanged(); + } + } + + + /** + * 设置控件可见 + * + * @param view + */ + protected void setVisible(View view) { + view.setVisibility(View.VISIBLE); + } + + /** + * 设置控件不可见 + * + * @param view + */ + protected void setGone(View view) { + view.setVisibility(View.GONE); + } + + /** + * 设置控件不可见 + * + * @param view + */ + protected void setInvisible(View view) { + view.setVisibility(View.INVISIBLE); + } + + /** + * 设置控件可用 + * + * @param view + */ + protected void setEnable(View view) { + view.setEnabled(true); + } + + /** + * 设置控件不可用 + * + * @param view + */ + protected void setDisable(View view) { + view.setEnabled(false); + } + + /** + * 获取图片资源 + * + * @param resId + * @return + */ + protected Drawable getDrawable(int resId) { + return context.getResources().getDrawable(resId); + } + + /** + * 获取字符串资源 + * + * @param resId + * @return + */ + protected String getString(int resId) { + return context.getResources().getString(resId); + } + + /** + * 获取颜色资源 + * + * @param resId + * @return + */ + protected int getColor(int resId) { + return context.getResources().getColor(resId); + } + + /** + * 获取数据源 + * + * @return + */ + public List getDataSource() { + return data; + } + + + public void setRecItemClick(RecyclerItemCallback recItemClick) { + this.recItemClick = recItemClick; + } + + public RecyclerItemCallback getRecItemClick() { + return recItemClick; + } + + + @Override + public int getItemViewType(int position) { + return super.getItemViewType(position); + } + + @Override + public abstract F onCreateViewHolder(ViewGroup parent, int viewType); + + @Override + public abstract void onBindViewHolder(F holder, int position); + + @Override + public int getItemCount() { + return data == null || data.isEmpty() ? 0 : data.size(); + } + + +} + + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java new file mode 100644 index 0000000..6e25f9d --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/RecyclerItemCallback.java @@ -0,0 +1,29 @@ +package cn.droidlover.xrecyclerview; + +/** + * Created by wanglei on 2016/10/30. + */ + +public abstract class RecyclerItemCallback { + /** + * 单击事件 + * + * @param position 位置 + * @param model 实体 + * @param tag 标签 + * @param holder 控件 + */ + public void onItemClick(int position, T model, int tag, F holder) { + } + + /** + * 长按事件 + * + * @param position 位置 + * @param model 实体 + * @param tag 标签 + * @param holder 控件 + */ + public void onItemLongClick(int position, T model, int tag, F holder) { + } +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java new file mode 100644 index 0000000..6e4e4ce --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/SimpleLoadMoreFooter.java @@ -0,0 +1,55 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class SimpleLoadMoreFooter extends RelativeLayout implements LoadMoreUIHandler { + + TextView tvMsg; + ProgressBar progressBar; + + public SimpleLoadMoreFooter(Context context) { + this(context, null); + } + + public SimpleLoadMoreFooter(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SimpleLoadMoreFooter(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setupView(); + } + + private void setupView() { + inflate(getContext(), R.layout.x_view_footer_load_more_simple, this); + tvMsg = (TextView) findViewById(R.id.tv_msg); + progressBar = (ProgressBar) findViewById(R.id.progressBar); + } + + @Override + public void onLoading() { + setVisibility(VISIBLE); + tvMsg.setText("加载中"); + progressBar.setVisibility(VISIBLE); + } + + @Override + public void onLoadFinish(boolean hasMore) { + if (hasMore) { + setVisibility(GONE); + } else { + setVisibility(VISIBLE); + tvMsg.setText("没有更多数据"); + progressBar.setVisibility(GONE); + } + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java new file mode 100644 index 0000000..64b916f --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XDataObserver.java @@ -0,0 +1,47 @@ +package cn.droidlover.xrecyclerview; + + +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XDataObserver extends RecyclerView.AdapterDataObserver { + + private XRecyclerAdapter adapter; + + public XDataObserver(XRecyclerAdapter adapter) { + this.adapter = adapter; + } + + @Override + public void onChanged() { + super.onChanged(); + adapter.notifyDataSetChanged(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + super.onItemRangeChanged(positionStart, itemCount); + adapter.notifyItemRangeChanged(positionStart + adapter.getHeaderSize(), itemCount); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + adapter.notifyItemRangeInserted(positionStart + adapter.getHeaderSize(), itemCount); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + super.onItemRangeMoved(fromPosition, toPosition, itemCount); + adapter.notifyItemRangeChanged(fromPosition + adapter.getHeaderSize(), itemCount + adapter.getHeaderSize() + toPosition); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + adapter.notifyItemRangeRemoved(positionStart + adapter.getHeaderSize(), itemCount); + } +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java new file mode 100644 index 0000000..b952179 --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XHeadFootViewHolder.java @@ -0,0 +1,18 @@ +package cn.droidlover.xrecyclerview; + +import android.view.View; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XHeadFootViewHolder extends RecyclerView.ViewHolder { + + public XHeadFootViewHolder(View itemView) { + super(itemView); + itemView.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); + itemView.setTag(this); + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java new file mode 100644 index 0000000..1b796cb --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerAdapter.java @@ -0,0 +1,305 @@ +package cn.droidlover.xrecyclerview; + +import android.view.View; +import android.view.ViewGroup; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XRecyclerAdapter extends RecyclerView.Adapter implements Cloneable { + private static final int ITEM_TYPE_HEADER = Integer.MAX_VALUE + 1; //headerView + private static final int ITEM_TYPE_NORMAL = ITEM_TYPE_HEADER + 1 * 1000; //normalItem + private static final int ITEM_TYPE_FOOTER = ITEM_TYPE_HEADER + 2 * 1000; //footerView + + private RecyclerView.Adapter baseAdapter; + private List headerViewList = new ArrayList(); + private List footerViewList = new ArrayList(); + private Map adapterClassMap; + + private RecyclerView.AdapterDataObserver observer = new XDataObserver(this); + + public XRecyclerAdapter(RecyclerView.Adapter baseAdapter) { + this(baseAdapter, null, null); + } + + public XRecyclerAdapter(RecyclerView.Adapter adapter, List headerViewList, List footerViewList) { + if (headerViewList != null && headerViewList.size() > 0) { + this.headerViewList.addAll(headerViewList); + } + if (footerViewList != null && footerViewList.size() > 0) { + this.footerViewList.addAll(footerViewList); + } + if (adapterClassMap == null) { + adapterClassMap = new HashMap(); + } + setAdapter(adapter); + } + + private void setAdapter(RecyclerView.Adapter adapter) { + if (adapter == null) { + return; + } + + if (baseAdapter != null) { + baseAdapter.unregisterAdapterDataObserver(observer); + } + baseAdapter = adapter; + Class clazz = baseAdapter.getClass(); + if (!adapterClassMap.containsKey(clazz)) { + putClass(clazz); + } + baseAdapter.registerAdapterDataObserver(observer); + } + + private void putClass(Class clazz) { + adapterClassMap.put(clazz, Integer.valueOf(ITEM_TYPE_FOOTER + 100 * adapterClassMap.size())); + } + + private int getMapClassIntValue() { + return adapterClassMap.get(baseAdapter.getClass()).intValue(); + } + + /** + * 数据是否为空 + * + * @return + */ + private boolean isEmpty() { + return getDataCount() == 0; + } + + /** + * 添加headview + * + * @param pos + * @param view + * @return + */ + public boolean addHeadView(int pos, View view) { + if (view == null || headerViewList.contains(view)) { + return false; + } + + headerViewList.add(pos, view); + notifyItemInserted(headerViewList.indexOf(Integer.valueOf(pos))); + return true; + } + + /** + * 添加headView + * + * @param view + * @return + */ + public boolean addHeadView(View view) { + return addHeadView(getHeaderSize(), view); + } + + /** + * 添加footView + * + * @param pos + * @param view + * @return + */ + public boolean addFootView(int pos, View view) { + if (view == null || footerViewList.contains(view)) { + return false; + } + + footerViewList.add(pos, view); + notifyItemInserted(getHeaderSize() + getDataCount() + pos); + + return true; + } + + public boolean addFootView(View view) { + return addFootView(getFooterSize(), view); + } + + /** + * 删除headView + * + * @param view + * @return + */ + public boolean removeHeadView(View view) { + if (view == null && !headerViewList.contains(view)) { + return false; + } + + int pos = headerViewList.indexOf(view); + boolean result = headerViewList.remove(view); + + if (result) { + notifyItemRemoved(pos); + if (view.getParent() != null) { + ((ViewGroup) view.getParent()).removeView(view); + } + } + return result; + } + + public boolean removeAllHeadersView() { + headerViewList.clear(); + notifyDataSetChanged(); + return true; + } + + public boolean removeAllFootView() { + footerViewList.clear(); + notifyDataSetChanged(); + return true; + } + + /** + * 删除footView + * + * @param view + * @return + */ + public boolean removeFootView(View view) { + boolean result = false; + + if (!footerViewList.contains(view)) { + result = false; + } + + int pos = footerViewList.indexOf(view); + if (pos > -1) { + result = footerViewList.remove(view); + } + if (result) { + notifyItemRemoved(1 + pos + getHeaderSize() + getDataCount()); + } + return result; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + int index = 0; + if (viewType < ITEM_TYPE_HEADER + getHeaderSize()) { + index = viewType + ITEM_TYPE_HEADER; + return new XHeadFootViewHolder(headerViewList.get(index)); + } + if ((getDataCount() > 0 && viewType < ITEM_TYPE_NORMAL + getDataCount()) + || viewType <= ITEM_TYPE_NORMAL + getFooterSize()) { + index = viewType - ITEM_TYPE_NORMAL; + if (index < footerViewList.size()) { + return new XHeadFootViewHolder(footerViewList.get(index)); + } + } + index = viewType - getMapClassIntValue(); + return baseAdapter.onCreateViewHolder(parent, index); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + int headViewSize = getHeaderSize(); + if (position >= headViewSize && (position < headViewSize + getDataCount())) { + baseAdapter.onBindViewHolder(holder, position - headViewSize); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) { + int headViewSize = getHeaderSize(); + if (position >= headViewSize && (position < headViewSize + getDataCount())) { + baseAdapter.onBindViewHolder(holder, position - headViewSize, payloads); + } + } + + @Override + public int getItemCount() { + return getHeaderSize() + getDataCount() + getFooterSize(); + } + + @Override + public int getItemViewType(int position) { + if (getDataCount() > 0) { + if (position < getHeaderSize()) { + return ITEM_TYPE_HEADER + position; + } + if (position < getHeaderSize() + getDataCount()) { + return getMapClassIntValue() + baseAdapter.getItemViewType(position - getHeaderSize()); + } + return ITEM_TYPE_NORMAL + position - getHeaderSize() - getDataCount(); + } else { + if (getHeaderSize() > 0 && position < getHeaderSize()) { + return ITEM_TYPE_HEADER + position; + } + return ITEM_TYPE_NORMAL + position - getHeaderSize(); + } + + } + + /** + * 判断当前位置是否是header or footer + * + * @param position + * @return + */ + public boolean isHeaderOrFooter(int position) { + return position < getHeaderSize() || position >= getHeaderSize() + getDataCount(); + } + + public int getDataCount() { + return baseAdapter.getItemCount(); + } + + /** + * 获取headView的数量 + * + * @return + */ + public int getHeaderSize() { + return headerViewList != null ? headerViewList.size() : 0; + } + + /** + * 获取footerView的数量 + * + * @return + */ + public int getFooterSize() { + return footerViewList != null ? footerViewList.size() : 0; + } + + public RecyclerView.Adapter getAdapter() { + return baseAdapter; + } + + @Override + protected Object clone() throws CloneNotSupportedException { + XRecyclerAdapter adapter = new XRecyclerAdapter(getAdapter()); + adapter.headerViewList = headerViewList; + adapter.footerViewList = footerViewList; + return adapter; + } + + /** + * 获取headViews + * + * @return + */ + public List getHeaderViewList() { + return headerViewList; + } + + /** + * 获取footViews + * + * @return + */ + public List getFooterViewList() { + return footerViewList; + } + +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java new file mode 100644 index 0000000..dfd059b --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerContentLayout.java @@ -0,0 +1,165 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.util.AttributeSet; + +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import cn.droidlover.xstatecontroller.XStateController; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XRecyclerContentLayout extends XStateController implements XRecyclerView.StateCallback, SwipeRefreshLayout.OnRefreshListener { + + private int padding; + private int paddingLeft; + private int paddingRight; + private int paddingTop; + private int paddingBottom; + private int scrollbarStyle; + private int backgroundColor; + private boolean clipToPadding; + private boolean scrollbarNone = false; + + SwipeRefreshLayout swipeRefreshLayout; + XRecyclerView recyclerView; + + + public XRecyclerContentLayout(Context context) { + this(context, null); + } + + public XRecyclerContentLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public XRecyclerContentLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + setupAttrs(context, attrs); + inflateView(context); + } + + private void setupAttrs(Context context, AttributeSet attrs) { + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.XRecyclerContentLayout); + + backgroundColor = typedArray.getColor(R.styleable.XRecyclerContentLayout_recyclerBackgroundColor, Color.WHITE); + padding = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPadding, -1.0f); + paddingLeft = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingLeft, 0.0f); + paddingRight = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingRight, 0.0f); + paddingTop = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingTop, 0.0f); + paddingBottom = (int) typedArray.getDimension(R.styleable.XRecyclerContentLayout_recyclerPaddingBottom, 0.0f); + scrollbarStyle = typedArray.getInt(R.styleable.XRecyclerContentLayout_recyclerScrollbarStyle, 2); + clipToPadding = typedArray.getBoolean(R.styleable.XRecyclerContentLayout_recyclerClipToPadding, false); + scrollbarNone = typedArray.getBoolean(R.styleable.XRecyclerContentLayout_recyclerScrollbarNone, false); + + typedArray.recycle(); + } + + private void inflateView(Context context) { + inflate(context, R.layout.x_view_recycler_content_layout, this); + } + + @Override + protected void onFinishInflate() { + swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout); + recyclerView = (XRecyclerView) findViewById(R.id.recyclerView); + + swipeRefreshLayout.setEnabled(false); + swipeRefreshLayout.setColorSchemeResources( + R.color.x_red, + R.color.x_blue, + R.color.x_yellow, + R.color.x_green + ); + swipeRefreshLayout.setOnRefreshListener(this); + if (padding != -1) { + recyclerView.setPadding(padding, padding, padding, padding); + } else { + recyclerView.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + } + recyclerView.setClipToPadding(clipToPadding); + + if (scrollbarNone) { + recyclerView.setVerticalScrollBarEnabled(false); + recyclerView.setHorizontalScrollBarEnabled(false); + } else { + recyclerView.setScrollBarStyle(scrollbarStyle); + } + + recyclerView.setBackgroundColor(backgroundColor); + + recyclerView.stateCallback(this); + + super.onFinishInflate(); + } + + @Override + public void setDisplayState(int displayState) { + XRecyclerAdapter adapter = recyclerView.getAdapter(); + if (adapter != null && adapter.getItemCount() > 0) { + super.setDisplayState(STATE_CONTENT); + return; + } + super.setDisplayState(displayState); + } + + public void setDisplayState(int state, boolean isForce) { + if (isForce) { + super.setDisplayState(state); + return; + } + setDisplayState(state); + } + + @Override + public void showEmpty() { + setDisplayState(STATE_EMPTY, true); + } + + @Override + public void showError() { + setDisplayState(STATE_ERROR, true); + } + + @Override + public void showLoading() { + setDisplayState(STATE_LOADING, true); + } + + @Override + public void notifyEmpty() { + showEmpty(); + } + + @Override + public void notifyContent() { + showContent(); + } + + @Override + public void refreshState(boolean isRefresh) { + swipeRefreshLayout.setRefreshing(isRefresh); + } + + @Override + public void refreshEnabled(boolean isEnabled) { + swipeRefreshLayout.setEnabled(isEnabled); + } + + @Override + public void onRefresh() { + recyclerView.onRefresh(); + } + + public XRecyclerView getRecyclerView() { + return recyclerView; + } + + public SwipeRefreshLayout getSwipeRefreshLayout() { + return swipeRefreshLayout; + } +} diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java new file mode 100644 index 0000000..109ab3d --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/XRecyclerView.java @@ -0,0 +1,688 @@ +package cn.droidlover.xrecyclerview; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import java.util.Collections; +import java.util.List; + +import android.widget.Adapter; +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.*; +import cn.droidlover.xrecyclerview.divider.HorizontalDividerItemDecoration; +import cn.droidlover.xrecyclerview.divider.VerticalDividerItemDecoration; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class XRecyclerView extends RecyclerView { + + private float xFactor = 1.0f; + private float yFactor = 1.0f; + + LayoutManagerType layoutManagerType; //LayoutManager类型 + private int[] lastScrollPositions; //瀑布流位置存储 + private int[] firstScrollPositions; + + LoadMoreUIHandler loadMoreUIHandler; + View loadMoreView; //加载更多控件 + + private boolean loadMore = false; + private int totalPage = 1; + private int currentPage = 1; + private boolean isRefresh = false; + private boolean isRefreshEnabled = true; //是否可刷新 + private int lastVelocityY = 0; + + XRecyclerAdapter adapter; + + StateCallback stateCallback; + OnRefreshAndLoadMoreListener onRefreshAndLoadMoreListener; + + public static final int LOAD_MORE_ITEM_SLOP = 2; + + public XRecyclerView(Context context) { + this(context, null); + } + + public XRecyclerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public XRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setUpView(); + } + + private void setUpView() { + addOnScrollListener(processMoreListener); + } + + public void adapter(Adapter adapter) { + setAdapter(adapter); + } + + @Override + public void setAdapter(Adapter adapter) { + if (adapter == null) { + return; + } + + if (!(adapter instanceof XRecyclerAdapter)) { + adapter = new XRecyclerAdapter(adapter); + } + super.setAdapter(adapter); + if (adapter.getItemCount() > 0) { + if (getStateCallback() != null) { + getStateCallback().notifyContent(); + } + } + + final XRecyclerAdapter finalAdapter = (XRecyclerAdapter) adapter; + adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + super.onChanged(); + update(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + super.onItemRangeMoved(fromPosition, toPosition, itemCount); + update(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + update(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + update(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + super.onItemRangeChanged(positionStart, itemCount); + update(); + } + + private void update() { + int dataCount = finalAdapter.getDataCount(); + if (dataCount > 0) { + if (isRefresh) { + isRefresh = false; + } + if (loadMore) { + loadMoreCompleted(); + } + if (getStateCallback() != null) { + getStateCallback().notifyContent(); + } + } else { + if (finalAdapter.getHeaderSize() > 0 || finalAdapter.getFooterSize() > 0) { + if (loadMoreView != null) { + loadMoreView.setVisibility(GONE); + } + } else { + if (getStateCallback() != null) { + getStateCallback().notifyEmpty(); + } + } + + } + + if (getStateCallback() != null) { + getStateCallback().refreshState(false); + } + } + }); + + this.adapter = (XRecyclerAdapter) adapter; + + } + + public XRecyclerView stateCallback(StateCallback stateCallback) { + this.stateCallback = stateCallback; + return this; + } + + public StateCallback getStateCallback() { + return stateCallback; + } + + @Override + public XRecyclerAdapter getAdapter() { + return adapter; + } + + public void onRefresh() { + currentPage = 1; + isRefresh = true; + if (getOnRefreshAndLoadMoreListener() != null) { + getOnRefreshAndLoadMoreListener().onRefresh(); + } + } + + @Override + public void setLayoutManager(LayoutManager layout) { + if (layout == null) { + throw new IllegalArgumentException("LayoutManager can not be null."); + } + super.setLayoutManager(layout); + + if (layout instanceof GridLayoutManager) { + int spanCount = ((GridLayoutManager) layout).getSpanCount(); + setSpanLookUp(layout, spanCount); + } + + if (layout instanceof StaggeredGridLayoutManager) { + int spanCount = ((StaggeredGridLayoutManager) layout).getSpanCount(); + setSpanLookUp(layout, spanCount); + } + } + + @Override + public boolean fling(int velocityX, int velocityY) { + lastVelocityY = velocityY; + return super.fling((int) (velocityX * xFactor), (int) (velocityY * yFactor)); + } + + public XRecyclerView xFactor(float xFactor) { + this.xFactor = xFactor; + return this; + } + + public XRecyclerView yFactor(float yFactor) { + this.yFactor = yFactor; + return this; + } + + + public XRecyclerView verticalLayoutManager(Context context) { + LinearLayoutManager manager = new LinearLayoutManager(context); + manager.setOrientation(RecyclerView.VERTICAL); + setLayoutManager(manager); + return this; + } + + public XRecyclerView horizontalLayoutManager(Context context) { + LinearLayoutManager manager = new LinearLayoutManager(context); + manager.setOrientation(RecyclerView.HORIZONTAL); + setLayoutManager(manager); + return this; + } + + public XRecyclerView gridLayoutManager(Context context, int spanCount) { + GridLayoutManager manager = new GridLayoutManager(context, spanCount); + setLayoutManager(manager); + return this; + } + + public XRecyclerView verticalStaggeredLayoutManager(int spanCount) { + StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL); + setLayoutManager(manager); + return this; + } + + public XRecyclerView horizontalStaggeredLayoutManager(int spanCount) { + StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.HORIZONTAL); + setLayoutManager(manager); + return this; + } + + public int getLastVisibleItemPosition() { + return getLastVisibleItemPosition(getLayoutManager()); + } + + private int getLastVisibleItemPosition(RecyclerView.LayoutManager layoutManager) { + int lastVisibleItemPosition = -1; + if (layoutManagerType == null) { + if (layoutManager instanceof LinearLayoutManager) { + layoutManagerType = LayoutManagerType.LINEAR; + } else if (layoutManager instanceof GridLayoutManager) { + layoutManagerType = LayoutManagerType.GRID; + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + layoutManagerType = LayoutManagerType.STAGGERED_GRID; + } else { + throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); + } + } + + switch (layoutManagerType) { + case LINEAR: + lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); + break; + case GRID: + lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition(); + break; + case STAGGERED_GRID: + StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; + if (lastScrollPositions == null) { + lastScrollPositions = new int[staggeredGridLayoutManager.getSpanCount()]; + } + + staggeredGridLayoutManager.findLastVisibleItemPositions(lastScrollPositions); + lastVisibleItemPosition = findMax(lastScrollPositions); + break; + default: + break; + } + return lastVisibleItemPosition; + } + + + private int getFirstVisibleItemPosition(RecyclerView.LayoutManager layoutManager) { + int firstVisibleItemPosition = -1; + if (layoutManagerType == null) { + if (layoutManager instanceof LinearLayoutManager) { + layoutManagerType = LayoutManagerType.LINEAR; + } else if (layoutManager instanceof GridLayoutManager) { + layoutManagerType = LayoutManagerType.GRID; + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + layoutManagerType = LayoutManagerType.STAGGERED_GRID; + } else { + throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager"); + } + } + + switch (layoutManagerType) { + case LINEAR: + firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); + break; + case GRID: + firstVisibleItemPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition(); + break; + case STAGGERED_GRID: + StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager; + if (lastScrollPositions == null) { + lastScrollPositions = new int[staggeredGridLayoutManager.getSpanCount()]; + } + + staggeredGridLayoutManager.findLastVisibleItemPositions(firstScrollPositions); + firstVisibleItemPosition = findMin(firstScrollPositions); + break; + default: + break; + } + return firstVisibleItemPosition; + } + + + private int findMax(int[] lastPositions) { + int max = Integer.MIN_VALUE; + for (int value : lastPositions) { + if (value > max) { + max = value; + } + } + return max; + } + + private int findMin(int[] positions) { + int min = Integer.MIN_VALUE; + for (int value : positions) { + if (value < min) { + min = value; + } + } + return min; + } + + + private void setSpanLookUp(RecyclerView.LayoutManager layoutManager, final int spanCount) { + if (layoutManager instanceof GridLayoutManager) { + ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (adapter != null) { + return adapter.isHeaderOrFooter(position) ? spanCount : 1; + } + return GridLayoutManager.DEFAULT_SPAN_COUNT; + } + }); + } else if (layoutManager instanceof StaggeredGridLayoutManager) { + ((StaggeredGridLayoutManager) layoutManager).setSpanCount(spanCount); + } + + } + + /** + * 设置SpanSizeLookup + * + * @param layoutManager + * @param lookup + */ + public void setGridSpanLookUp(GridLayoutManager layoutManager, final GridLayoutManager.SpanSizeLookup lookup) { + layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if (adapter != null) { + if (adapter.isHeaderOrFooter(position)) { + return 1; + } + + return lookup.getSpanSize(position); + } + return GridLayoutManager.DEFAULT_SPAN_COUNT; + } + }); + } + + public boolean addHeaderView(int position, View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addHeadView(position, view); + } + return result; + } + + public boolean addHeaderView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addHeadView(view); + } + return result; + } + + public boolean removeHeaderView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.removeHeadView(view); + } + return result; + } + + public boolean removeAllHeaderView() { + boolean result = false; + if (adapter != null) { + result = adapter.removeAllHeadersView(); + } + return result; + } + + public boolean addFooterView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addFootView(view); + } + return result; + } + + public boolean addFooterView(int position, View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.addFootView(position, view); + } + return result; + } + + public boolean removeFooterView(View view) { + boolean result = false; + if (view == null) { + return result; + } + if (adapter != null) { + result = adapter.removeFootView(view); + } + return result; + } + + public boolean removeAllFootView() { + boolean result = false; + if (adapter != null) { + result = adapter.removeAllFootView(); + } + return result; + } + + public int getHeaderCount() { + int count = 0; + if (adapter != null) { + count = adapter.getHeaderSize(); + } + return count; + } + + public List getHeaderViewList() { + if (adapter != null) { + return adapter.getHeaderViewList(); + } + return Collections.EMPTY_LIST; + } + + public int getFooterCount() { + int count = 0; + if (adapter != null) { + return adapter.getFooterSize(); + } + return count; + } + + public List getFooterViewList() { + if (adapter != null) { + return adapter.getFooterViewList(); + } + return Collections.EMPTY_LIST; + } + + /** + * 使用默认的加载更多 + */ + public void useDefLoadMoreView() { + SimpleLoadMoreFooter loadMoreFooter = new SimpleLoadMoreFooter(getContext()); + setLoadMoreView(loadMoreFooter); + setLoadMoreUIHandler(loadMoreFooter); + } + + + public void loadMoreFooterView(View view) { + setLoadMoreView(view); + setLoadMoreUIHandler((LoadMoreUIHandler) view); + } + + /** + * 设置加载更多布局 + * + * @param view + */ + public void setLoadMoreView(View view) { + if (loadMoreView != null && loadMoreView != view) { + removeFooterView(view); + } + loadMoreView = view; + + addFooterView(view); + } + + public void setLoadMoreUIHandler(LoadMoreUIHandler loadMoreUIHandler) { + this.loadMoreUIHandler = loadMoreUIHandler; + } + + private void loadMoreCompleted() { + if (loadMoreUIHandler != null) { + loadMoreUIHandler.onLoadFinish(totalPage > currentPage); + } + loadMore = false; + if (getStateCallback() != null) { + changeRefreshEnableState(true); + getStateCallback().notifyContent(); + } + } + + public void setPage(final int currentPage, final int totalPage) { + this.currentPage = currentPage; + this.totalPage = totalPage; + if (loadMoreUIHandler != null) { + loadMoreUIHandler.onLoadFinish(totalPage > currentPage); + } + } + + /** + * 改变 刷新可用 的状态 + * + * @param isEnabled + */ + private void changeRefreshEnableState(boolean isEnabled) { + if (!isRefreshEnabled) { + return; + } + if (getStateCallback() != null) { + getStateCallback().refreshEnabled(isEnabled); + } + } + + /** + * 刷新数据 + */ + public void refreshData() { + if (getStateCallback() != null) { + getStateCallback().refreshState(true); + } + if (getOnRefreshAndLoadMoreListener() != null) { + getOnRefreshAndLoadMoreListener().onRefresh(); + } + } + + RecyclerView.OnScrollListener processMoreListener = new RecyclerView.OnScrollListener() { + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + + if (adapter == null + || recyclerView.getLayoutManager() == null + || isRefresh) { + return; + } + + int totalCount = adapter.getItemCount(); + + if (newState == RecyclerView.SCROLL_STATE_IDLE + && !loadMore + && lastVelocityY > 0 + && getLastVisibleItemPosition(recyclerView.getLayoutManager()) + LOAD_MORE_ITEM_SLOP > totalCount) { + + if (totalPage > currentPage) { + loadMore = true; + + if (getOnRefreshAndLoadMoreListener() != null) { + getOnRefreshAndLoadMoreListener().onLoadMore(++currentPage); + + changeRefreshEnableState(false); + + if (loadMoreUIHandler != null) { + loadMoreUIHandler.onLoading(); + } + } + } else { + loadMoreCompleted(); + } + + } else { + changeRefreshEnableState(true); + } + + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + } + }; + + public XRecyclerView setOnRefreshAndLoadMoreListener(OnRefreshAndLoadMoreListener onRefreshAndLoadMoreListener) { + this.onRefreshAndLoadMoreListener = onRefreshAndLoadMoreListener; + changeRefreshEnableState(true); + return this; + } + + public XRecyclerView noDivider() { + setItemAnimator(new DefaultItemAnimator()); + setHasFixedSize(true); + return this; + } + + public XRecyclerView horizontalDivider(@ColorRes int colorRes, @DimenRes int dimenRes) { + setItemAnimator(new DefaultItemAnimator()); + setHasFixedSize(true); + addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()) + .colorResId(colorRes) + .size(getContext().getResources().getDimensionPixelSize(dimenRes)) + .build() + ); + return this; + } + + public XRecyclerView verticalDivider(@ColorRes int colorRes, @DimenRes int dimenRes) { + setItemAnimator(new DefaultItemAnimator()); + setHasFixedSize(true); + addItemDecoration(new VerticalDividerItemDecoration.Builder(getContext()) + .colorResId(colorRes) + .size(getContext().getResources().getDimensionPixelSize(dimenRes)) + .build() + ); + return this; + } + + + public OnRefreshAndLoadMoreListener getOnRefreshAndLoadMoreListener() { + return onRefreshAndLoadMoreListener; + } + + public boolean isRefreshEnabled() { + return isRefreshEnabled; + } + + /** + * 设置是否可下拉刷新 + * + * @param refreshEnabled + */ + public void setRefreshEnabled(boolean refreshEnabled) { + isRefreshEnabled = refreshEnabled; + } + + + enum LayoutManagerType { + LINEAR, GRID, STAGGERED_GRID + } + + public interface StateCallback { + void notifyEmpty(); + + void notifyContent(); + + void refreshState(boolean isRefresh); + + void refreshEnabled(boolean isEnabled); + } + + public interface OnRefreshAndLoadMoreListener { + void onRefresh(); + + void onLoadMore(int page); + } +} \ No newline at end of file diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java new file mode 100644 index 0000000..8cb6f7c --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/FlexibleDividerDecoration.java @@ -0,0 +1,342 @@ +package cn.droidlover.xrecyclerview.divider; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; + +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; +import cn.droidlover.xrecyclerview.XRecyclerAdapter; + +/** + * Created by wanglei on 2016/10/30. + */ + +public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration { + + private static final int DEFAULT_SIZE = 2; + private static final int[] ATTRS = new int[]{ + android.R.attr.listDivider + }; + + protected enum DividerType { + DRAWABLE, PAINT, COLOR + } + + protected DividerType mDividerType = DividerType.DRAWABLE; + protected VisibilityProvider mVisibilityProvider; + protected PaintProvider mPaintProvider; + protected ColorProvider mColorProvider; + protected DrawableProvider mDrawableProvider; + protected SizeProvider mSizeProvider; + protected boolean mShowLastDivider; + private Paint mPaint; + + protected FlexibleDividerDecoration(Builder builder) { + if (builder.mPaintProvider != null) { + mDividerType = DividerType.PAINT; + mPaintProvider = builder.mPaintProvider; + } else if (builder.mColorProvider != null) { + mDividerType = DividerType.COLOR; + mColorProvider = builder.mColorProvider; + mPaint = new Paint(); + setSizeProvider(builder); + } else { + mDividerType = DividerType.DRAWABLE; + if (builder.mDrawableProvider == null) { + TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS); + final Drawable divider = a.getDrawable(0); + a.recycle(); + mDrawableProvider = new DrawableProvider() { + @Override + public Drawable drawableProvider(int position, RecyclerView parent) { + return divider; + } + }; + } else { + mDrawableProvider = builder.mDrawableProvider; + } + mSizeProvider = builder.mSizeProvider; + } + + mVisibilityProvider = builder.mVisibilityProvider; + mShowLastDivider = builder.mShowLastDivider; + } + + private void setSizeProvider(Builder builder) { + mSizeProvider = builder.mSizeProvider; + if (mSizeProvider == null) { + mSizeProvider = new SizeProvider() { + @Override + public int dividerSize(int position, RecyclerView parent) { + return DEFAULT_SIZE; + } + }; + } + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + int lastChildPosition = -1; + int childCount = mShowLastDivider ? parent.getChildCount() : parent.getChildCount() - 1; + for (int i = 0; i < childCount; i++) { + View child = parent.getChildAt(i); + int childPosition = parent.getChildAdapterPosition(child); + + if (childPosition < lastChildPosition) { + // Avoid remaining divider when animation starts + continue; + } + + + lastChildPosition = childPosition; + + if (ViewCompat.getAlpha(child) < 1) { + // Avoid remaining divider when animation starts + continue; + } + + if (mVisibilityProvider.shouldHideDivider(childPosition, parent)) { + continue; + } + + //headerView or footerView avoid remain divider + RecyclerView.Adapter adapter = parent.getAdapter(); + if (adapter != null) { + if (adapter instanceof XRecyclerAdapter) { + if (((XRecyclerAdapter) adapter).isHeaderOrFooter(childPosition)) { + continue; + } + } + } + + Rect bounds = getDividerBound(childPosition, parent, child); + switch (mDividerType) { + case DRAWABLE: + Drawable drawable = mDrawableProvider.drawableProvider(childPosition, parent); + drawable.setBounds(bounds); + drawable.draw(c); + break; + case PAINT: + mPaint = mPaintProvider.dividerPaint(childPosition, parent); + c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint); + break; + case COLOR: + mPaint.setColor(mColorProvider.dividerColor(childPosition, parent)); + mPaint.setStrokeWidth(mSizeProvider.dividerSize(childPosition, parent)); + c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint); + break; + default: + break; + } + } + } + + @Override + public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) { + int position = parent.getChildAdapterPosition(v); + setItemOffsets(rect, position, parent); + } + + protected abstract Rect getDividerBound(int position, RecyclerView parent, View child); + + protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent); + + /** + * Interface for controlling divider visibility + */ + public interface VisibilityProvider { + + /** + * Returns true if divider should be hidden. + * + * @param position Divider position + * @param parent RecyclerView + * @return True if the divider at position should be hidden + */ + boolean shouldHideDivider(int position, RecyclerView parent); + } + + /** + * Interface for controlling paint instance for divider drawing + */ + public interface PaintProvider { + + /** + * Returns {@link android.graphics.Paint} for divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Paint instance + */ + Paint dividerPaint(int position, RecyclerView parent); + } + + /** + * Interface for controlling divider color + */ + public interface ColorProvider { + + /** + * Returns {@link android.graphics.Color} value of divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Color value + */ + int dividerColor(int position, RecyclerView parent); + } + + /** + * Interface for controlling drawable object for divider drawing + */ + public interface DrawableProvider { + + /** + * Returns drawable instance for divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Drawable instance + */ + Drawable drawableProvider(int position, RecyclerView parent); + } + + /** + * Interface for controlling divider size + */ + public interface SizeProvider { + + /** + * Returns size value of divider. + * Height for horizontal divider, width for vertical divider + * + * @param position Divider position + * @param parent RecyclerView + * @return Size of divider + */ + int dividerSize(int position, RecyclerView parent); + } + + public static class Builder { + + private Context mContext; + protected Resources mResources; + private PaintProvider mPaintProvider; + private ColorProvider mColorProvider; + private DrawableProvider mDrawableProvider; + private SizeProvider mSizeProvider; + private VisibilityProvider mVisibilityProvider = new VisibilityProvider() { + @Override + public boolean shouldHideDivider(int position, RecyclerView parent) { + return false; + } + }; + private boolean mShowLastDivider = false; + + public Builder(Context context) { + mContext = context; + mResources = context.getResources(); + } + + public T paint(final Paint paint) { + return paintProvider(new PaintProvider() { + @Override + public Paint dividerPaint(int position, RecyclerView parent) { + return paint; + } + }); + } + + public T paintProvider(PaintProvider provider) { + mPaintProvider = provider; + return (T) this; + } + + public T color(final int color) { + return colorProvider(new ColorProvider() { + @Override + public int dividerColor(int position, RecyclerView parent) { + return color; + } + }); + } + + public T colorResId(@ColorRes int colorId) { + return color(mResources.getColor(colorId)); + } + + public T colorProvider(ColorProvider provider) { + mColorProvider = provider; + return (T) this; + } + + public T drawable(@DrawableRes int id) { + return drawable(mResources.getDrawable(id)); + } + + public T drawable(final Drawable drawable) { + return drawableProvider(new DrawableProvider() { + @Override + public Drawable drawableProvider(int position, RecyclerView parent) { + return drawable; + } + }); + } + + public T drawableProvider(DrawableProvider provider) { + mDrawableProvider = provider; + return (T) this; + } + + public T size(final int size) { + return sizeProvider(new SizeProvider() { + @Override + public int dividerSize(int position, RecyclerView parent) { + return size; + } + }); + } + + public T sizeResId(@DimenRes int sizeId) { + return size(mResources.getDimensionPixelSize(sizeId)); + } + + public T sizeProvider(SizeProvider provider) { + mSizeProvider = provider; + return (T) this; + } + + public T visibilityProvider(VisibilityProvider provider) { + mVisibilityProvider = provider; + return (T) this; + } + + public T showLastDivider() { + mShowLastDivider = true; + return (T) this; + } + + protected void checkBuilderParams() { + if (mPaintProvider != null) { + if (mColorProvider != null) { + throw new IllegalArgumentException( + "Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider."); + } + if (mSizeProvider != null) { + throw new IllegalArgumentException( + "Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider."); + } + } + } + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java new file mode 100644 index 0000000..890d367 --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/HorizontalDividerItemDecoration.java @@ -0,0 +1,144 @@ +package cn.droidlover.xrecyclerview.divider; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; +import androidx.annotation.DimenRes; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration { + + private MarginProvider mMarginProvider; + + protected HorizontalDividerItemDecoration(Builder builder) { + super(builder); + mMarginProvider = builder.mMarginProvider; + } + + @Override + protected Rect getDividerBound(int position, RecyclerView parent, View child) { + Rect bounds = new Rect(0, 0, 0, 0); + int transitionX = (int) ViewCompat.getTranslationX(child); + int transitionY = (int) ViewCompat.getTranslationY(child); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + bounds.left = parent.getPaddingLeft() + + mMarginProvider.dividerLeftMargin(position, parent) + transitionX; + bounds.right = parent.getWidth() - parent.getPaddingRight() - + mMarginProvider.dividerRightMargin(position, parent) + transitionX; + + int dividerSize = getDividerSize(position, parent); + if (mDividerType == DividerType.DRAWABLE) { + bounds.top = child.getBottom() + params.topMargin + transitionY; + bounds.bottom = bounds.top + dividerSize; + } else { + bounds.top = child.getBottom() + params.topMargin + dividerSize / 2 + transitionY; + bounds.bottom = bounds.top; + } + + return bounds; + } + + @Override + protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) { + outRect.set(0, 0, 0, getDividerSize(position, parent)); + } + + private int getDividerSize(int position, RecyclerView parent) { + if (mPaintProvider != null) { + return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth(); + } else if (mSizeProvider != null) { + return mSizeProvider.dividerSize(position, parent); + } else if (mDrawableProvider != null) { + Drawable drawable = mDrawableProvider.drawableProvider(position, parent); + return drawable.getIntrinsicHeight(); + } + throw new RuntimeException("failed to get size"); + } + + /** + * Interface for controlling divider margin + */ + public interface MarginProvider { + + /** + * Returns left margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return left margin + */ + int dividerLeftMargin(int position, RecyclerView parent); + + /** + * Returns right margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return right margin + */ + int dividerRightMargin(int position, RecyclerView parent); + } + + public static class Builder extends FlexibleDividerDecoration.Builder { + + private MarginProvider mMarginProvider = new MarginProvider() { + @Override + public int dividerLeftMargin(int position, RecyclerView parent) { + return 0; + } + + @Override + public int dividerRightMargin(int position, RecyclerView parent) { + return 0; + } + }; + + public Builder(Context context) { + super(context); + } + + public Builder margin(final int leftMargin, final int rightMargin) { + return marginProvider(new MarginProvider() { + @Override + public int dividerLeftMargin(int position, RecyclerView parent) { + return leftMargin; + } + + @Override + public int dividerRightMargin(int position, RecyclerView parent) { + return rightMargin; + } + }); + } + + public Builder margin(int horizontalMargin) { + return margin(horizontalMargin, horizontalMargin); + } + + public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) { + return margin(mResources.getDimensionPixelSize(leftMarginId), + mResources.getDimensionPixelSize(rightMarginId)); + } + + public Builder marginResId(@DimenRes int horizontalMarginId) { + return marginResId(horizontalMarginId, horizontalMarginId); + } + + public Builder marginProvider(MarginProvider provider) { + mMarginProvider = provider; + return this; + } + + public HorizontalDividerItemDecoration build() { + checkBuilderParams(); + return new HorizontalDividerItemDecoration(this); + } + } +} + diff --git a/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java new file mode 100644 index 0000000..dc8477d --- /dev/null +++ b/xrecycler/src/main/java/cn/droidlover/xrecyclerview/divider/VerticalDividerItemDecoration.java @@ -0,0 +1,143 @@ +package cn.droidlover.xrecyclerview.divider; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; +import androidx.annotation.DimenRes; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Created by wanglei on 2016/10/30. + */ + +public class VerticalDividerItemDecoration extends FlexibleDividerDecoration { + + private MarginProvider mMarginProvider; + + protected VerticalDividerItemDecoration(Builder builder) { + super(builder); + mMarginProvider = builder.mMarginProvider; + } + + @Override + protected Rect getDividerBound(int position, RecyclerView parent, View child) { + Rect bounds = new Rect(0, 0, 0, 0); + int transitionX = (int) ViewCompat.getTranslationX(child); + int transitionY = (int) ViewCompat.getTranslationY(child); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + bounds.top = parent.getPaddingTop() + + mMarginProvider.dividerTopMargin(position, parent) + transitionY; + bounds.bottom = parent.getHeight() - parent.getPaddingBottom() - + mMarginProvider.dividerBottomMargin(position, parent) + transitionY; + + int dividerSize = getDividerSize(position, parent); + if (mDividerType == DividerType.DRAWABLE) { + bounds.left = child.getRight() + params.leftMargin + transitionX; + bounds.right = bounds.left + dividerSize; + } else { + bounds.left = child.getRight() + params.leftMargin + dividerSize / 2 + transitionX; + bounds.right = bounds.left; + } + + return bounds; + } + + @Override + protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) { + outRect.set(0, 0, getDividerSize(position, parent), 0); + } + + private int getDividerSize(int position, RecyclerView parent) { + if (mPaintProvider != null) { + return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth(); + } else if (mSizeProvider != null) { + return mSizeProvider.dividerSize(position, parent); + } else if (mDrawableProvider != null) { + Drawable drawable = mDrawableProvider.drawableProvider(position, parent); + return drawable.getIntrinsicWidth(); + } + throw new RuntimeException("failed to get size"); + } + + /** + * Interface for controlling divider margin + */ + public interface MarginProvider { + + /** + * Returns top margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return top margin + */ + int dividerTopMargin(int position, RecyclerView parent); + + /** + * Returns bottom margin of divider. + * + * @param position Divider position + * @param parent RecyclerView + * @return bottom margin + */ + int dividerBottomMargin(int position, RecyclerView parent); + } + + public static class Builder extends FlexibleDividerDecoration.Builder { + + private MarginProvider mMarginProvider = new MarginProvider() { + @Override + public int dividerTopMargin(int position, RecyclerView parent) { + return 0; + } + + @Override + public int dividerBottomMargin(int position, RecyclerView parent) { + return 0; + } + }; + + public Builder(Context context) { + super(context); + } + + public Builder margin(final int topMargin, final int bottomMargin) { + return marginProvider(new MarginProvider() { + @Override + public int dividerTopMargin(int position, RecyclerView parent) { + return topMargin; + } + + @Override + public int dividerBottomMargin(int position, RecyclerView parent) { + return bottomMargin; + } + }); + } + + public Builder margin(int verticalMargin) { + return margin(verticalMargin, verticalMargin); + } + + public Builder marginResId(@DimenRes int topMarginId, @DimenRes int bottomMarginId) { + return margin(mResources.getDimensionPixelSize(topMarginId), + mResources.getDimensionPixelSize(bottomMarginId)); + } + + public Builder marginResId(@DimenRes int verticalMarginId) { + return marginResId(verticalMarginId, verticalMarginId); + } + + public Builder marginProvider(MarginProvider provider) { + mMarginProvider = provider; + return this; + } + + public VerticalDividerItemDecoration build() { + checkBuilderParams(); + return new VerticalDividerItemDecoration(this); + } + } +} diff --git a/xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml b/xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml new file mode 100644 index 0000000..3ced55f --- /dev/null +++ b/xrecycler/src/main/res/layout/x_view_footer_load_more_simple.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml b/xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml new file mode 100644 index 0000000..9a6038d --- /dev/null +++ b/xrecycler/src/main/res/layout/x_view_recycler_content_layout.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/xrecycler/src/main/res/values/attrs.xml b/xrecycler/src/main/res/values/attrs.xml new file mode 100644 index 0000000..72835cf --- /dev/null +++ b/xrecycler/src/main/res/values/attrs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xrecycler/src/main/res/values/colors.xml b/xrecycler/src/main/res/values/colors.xml new file mode 100644 index 0000000..a5a4085 --- /dev/null +++ b/xrecycler/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #FFC93437 + #FF375BF1 + #FFF7D23E + #FF34A350 + \ No newline at end of file diff --git a/xrecycler/src/main/res/values/strings.xml b/xrecycler/src/main/res/values/strings.xml new file mode 100644 index 0000000..6d51856 --- /dev/null +++ b/xrecycler/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Library + diff --git a/xrecycler/uploadAliRelease.gradle b/xrecycler/uploadAliRelease.gradle new file mode 100644 index 0000000..fb178ed --- /dev/null +++ b/xrecycler/uploadAliRelease.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'xrecycler' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-release-TFq04W/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/xrecycler/uploadAliSnapshot.gradle b/xrecycler/uploadAliSnapshot.gradle new file mode 100644 index 0000000..6f2f686 --- /dev/null +++ b/xrecycler/uploadAliSnapshot.gradle @@ -0,0 +1,27 @@ +apply plugin: 'maven' + +def artifactId = 'xrecycler' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def user = isHasFile ? properties.getProperty("aliyun.userName") : System.getenv("aliyun.userName") +def key = isHasFile ? properties.getProperty("aliyun.password") : System.getenv("aliyun.password") + +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'https://packages.aliyun.com/maven/repository/2065511-snapshot-fEzFv7/') { + authentication( + userName: user, + password: key + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file diff --git a/xrecycler/uploadLocal.gradle b/xrecycler/uploadLocal.gradle new file mode 100644 index 0000000..fd90855 --- /dev/null +++ b/xrecycler/uploadLocal.gradle @@ -0,0 +1,26 @@ +apply plugin: 'maven' + +def artifactId = 'xrecycler' +Properties properties = new Properties() +boolean isHasFile = false +if (project.rootProject.file('local.properties') != null) { + isHasFile = true + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} +def userName = isHasFile ? properties.getProperty("userName") : System.getenv("userName") +def password = isHasFile ? properties.getProperty("password") : System.getenv("password") +uploadArchives { + repositories { + mavenDeployer { + repository(url: 'http://127.0.0.1:8081/repository/maven-releases/') { + authentication( + userName: userName, + password: password + ) + } + pom.version = version + pom.artifactId = artifactId + pom.groupId = rootProject.ext.groupId + } + } +} \ No newline at end of file