├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_launcher_round.png
│ │ │ │ └── ic_launcher_foreground.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_key_board.xml
│ │ │ │ └── activity_main.xml
│ │ │ └── drawable
│ │ │ │ └── ic_launcher_background.xml
│ │ ├── java
│ │ │ └── apk98
│ │ │ │ └── com
│ │ │ │ └── androidutils
│ │ │ │ ├── KeyBoardActivity.kt
│ │ │ │ └── MainActivity.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── apk98
│ │ │ └── com
│ │ │ └── androidutils
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── apk98
│ │ └── com
│ │ └── androidutils
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── androidutilslib
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ ├── anim
│ │ │ │ ├── popup_enter.xml
│ │ │ │ └── popup_exit.xml
│ │ │ └── layout
│ │ │ │ └── popuwindow.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── apk98
│ │ │ └── com
│ │ │ └── androidutilslib
│ │ │ ├── test.kt
│ │ │ ├── utils
│ │ │ ├── CloseIoUtils.kt
│ │ │ ├── KeyBoardUtils.kt
│ │ │ ├── KeyBoardHeightUtils.kt
│ │ │ ├── ResourcesUtils.kt
│ │ │ ├── SharedPreferencesUtil.kt
│ │ │ ├── ScreenUtils.kt
│ │ │ ├── KeyBoardUI.kt
│ │ │ ├── ValidatorUtils.kt
│ │ │ ├── LogUtils.kt
│ │ │ ├── FileUtils.kt
│ │ │ └── TimeUtils.kt
│ │ │ └── widget
│ │ │ └── NullMenuEditText.kt
│ ├── test
│ │ └── java
│ │ │ └── apk98
│ │ │ └── com
│ │ │ └── androidutilslib
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── apk98
│ │ └── com
│ │ └── androidutilslib
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .gitignore
├── README.md
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/androidutilslib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':androidutilslib'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidUtils
3 |
4 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidUtilsLib
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/androidutilslib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/570622566/AndroidUtils/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/test.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib
2 |
3 | /**
4 | * Created by laijian on 2017/8/4.
5 | */
6 | class test {
7 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /gradle
8 | /captures
9 | .externalNativeBuild
10 | /app/build
11 | /androidutilslib/build
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AndroidUtils
2 | 用Kotlin写的android一些常用的工具
3 |
4 | FileUtils 文件操作类:获取根目录,创建,删除,写入数据等功能
5 |
6 | LogUtils 日志类:可保存,切分日志文件,可快速跳转到日志行代码
7 |
8 | ScreenUtils 分辨率:获取屏幕分辨率,分辨率转换,获取当前屏幕截图,获取标题栏状态栏高度
9 |
10 | TimeUtils 时间工具: 字符串转日期类型,获取当前日期,获取时间差
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/res/anim/popup_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/res/anim/popup_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/apk98/com/androidutils/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutils
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/apk98/com/androidutils/KeyBoardActivity.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutils
2 |
3 | import android.support.v7.app.AppCompatActivity
4 | import android.os.Bundle
5 | import apk98.com.androidutilslib.utils.KeyBoardUI
6 |
7 | class KeyBoardActivity : AppCompatActivity() {
8 |
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | setContentView(R.layout.activity_key_board)
12 | KeyBoardUI.buildKeyBoardUI(this)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/androidutilslib/src/test/java/apk98/com/androidutilslib/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_key_board.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/CloseIoUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import java.io.Closeable
4 | import java.io.IOException
5 |
6 | /**
7 | * Created by laijian on 2017/8/10.
8 | * 关闭io 工具类
9 | */
10 | object CloseIoUtils {
11 |
12 | /**
13 | * 关闭IO
14 |
15 | * @param closeables closeables
16 | */
17 | fun closeIO(vararg closeables: Closeable) {
18 | if (closeables == null) return
19 | closeables
20 | .filterNotNull()
21 | .forEach {
22 | try {
23 | it!!.close()
24 | } catch (e: IOException) {
25 | e.printStackTrace()
26 | }
27 | }
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/apk98/com/androidutils/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutils
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getTargetContext()
22 | assertEquals("apk98.com.androidutils", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/androidutilslib/src/androidTest/java/apk98/com/androidutilslib/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("apk98.com.androidutilslib.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/laijian/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/androidutilslib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/laijian/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/androidutilslib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 |
4 | android {
5 | compileSdkVersion 26
6 | buildToolsVersion "26.0.0"
7 |
8 |
9 | defaultConfig {
10 | minSdkVersion 19
11 | targetSdkVersion 26
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16 |
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(dir: 'libs', include: ['*.jar'])
28 | implementation 'com.android.support:appcompat-v7:26.0.1'
29 | testImplementation 'junit:junit:4.12'
30 | androidTestImplementation 'com.android.support.test:runner:1.0.0'
31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.0'
32 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
33 | }
34 | repositories {
35 | mavenCentral()
36 | }
37 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/res/layout/popuwindow.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
27 |
28 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | compileSdkVersion 26
9 | buildToolsVersion "26.0.0"
10 | defaultConfig {
11 | applicationId "apk98.com.androidutils"
12 | minSdkVersion 19
13 | targetSdkVersion 26
14 | versionCode 1
15 | versionName "1.0"
16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
17 | }
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | }
25 |
26 | dependencies {
27 | implementation fileTree(include: ['*.jar'], dir: 'libs')
28 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
29 | implementation 'com.android.support:appcompat-v7:26.0.1'
30 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
31 | testImplementation 'junit:junit:4.12'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.0'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.0'
34 | compile 'com.lovedise:permissiongen:0.0.6'
35 | implementation project(':androidutilslib')
36 | }
37 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/widget/NullMenuEditText.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.widget
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.ActionMode
6 | import android.view.Menu
7 | import android.view.MenuItem
8 | import android.widget.EditText
9 |
10 |
11 | /**
12 | * Created by laijian on 2017/8/31.
13 | */
14 | class NullMenuEditText(context: Context, attrs: AttributeSet) : EditText(context, attrs) {
15 |
16 | internal fun canPaste(): Boolean {
17 | return false
18 | }
19 |
20 | internal fun canCut(): Boolean {
21 | return false
22 | }
23 |
24 | internal fun canCopy(): Boolean {
25 | return false
26 | }
27 |
28 | internal fun canSelectAllText(): Boolean {
29 | return false
30 | }
31 |
32 | internal fun canSelectText(): Boolean {
33 | return false
34 | }
35 |
36 | internal fun textCanBeSelected(): Boolean {
37 | return false
38 | }
39 |
40 | init {
41 | isLongClickable = false
42 | setTextIsSelectable(false)
43 | customSelectionActionModeCallback = object : ActionMode.Callback {
44 | override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
45 | return false
46 | }
47 |
48 | override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
49 | return false
50 | }
51 |
52 | override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
53 | return false
54 | }
55 |
56 | override fun onDestroyActionMode(mode: ActionMode) {
57 |
58 | }
59 | }
60 |
61 | }
62 |
63 | override fun onTextContextMenuItem(id: Int): Boolean {
64 | return true
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/apk98/com/androidutils/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutils
2 |
3 | import android.Manifest
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.support.v7.app.AppCompatActivity
7 | import android.view.View
8 | import apk98.com.androidutilslib.utils.KeyBoardUI
9 | import apk98.com.androidutilslib.utils.LogUtils
10 | import apk98.com.androidutilslib.utils.SharedPreferencesUtil
11 | import kotlinx.android.synthetic.main.activity_main.*
12 | import kr.co.namee.permissiongen.PermissionGen
13 | import kr.co.namee.permissiongen.PermissionSuccess
14 |
15 |
16 | class MainActivity : AppCompatActivity() {
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContentView(R.layout.activity_main)
21 | PermissionGen.needPermission(this, 200, arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE))
22 | showlog_bt.setOnClickListener({
23 | var msg = ""
24 | var i = 0
25 | val num = 100
26 | while (i < num) {
27 | msg += i
28 | i++
29 | }
30 | LogUtils.saveSd(true).logSize(50 * 1024L).e(msg = msg)
31 | })
32 |
33 | keyboard_bt.setOnClickListener({
34 | val intent = Intent(this, KeyBoardActivity::class.java)
35 | startActivity(intent)
36 | })
37 | }
38 |
39 |
40 | @PermissionSuccess(requestCode = 200)
41 | fun openCamera() {
42 |
43 | }
44 |
45 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array,
46 | grantResults: IntArray) {
47 | PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults)
48 | }
49 |
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/KeyBoardUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Context.INPUT_METHOD_SERVICE
6 | import android.view.View
7 | import android.view.inputmethod.InputMethodManager
8 | import android.widget.EditText
9 |
10 |
11 | /**
12 | * Created by laijian on 2017/8/31.
13 | * 软键盘开关工具类
14 | */
15 | object KeyBoardUtils {
16 |
17 | /**
18 | * 打开软键盘
19 | *
20 | * @param mEditText
21 | * @param mContext
22 | */
23 | fun openKeybord(mEditText: EditText, mContext: Context) {
24 | val imm = mContext
25 | .getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
26 | imm.showSoftInput(mEditText, InputMethodManager.RESULT_SHOWN)
27 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,
28 | InputMethodManager.HIDE_IMPLICIT_ONLY)
29 | }
30 |
31 | /**
32 | * 关闭软键盘
33 | *
34 | * @param mEditText
35 | * @param mContext
36 | */
37 | fun closeKeybord(mEditText: EditText, mContext: Context) {
38 | val imm = mContext.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
39 | imm.hideSoftInputFromWindow(mEditText.windowToken, 0)
40 | }
41 |
42 |
43 | /**
44 | * des:隐藏软键盘,这种方式参数为activity
45 | *
46 | * @param activity
47 | */
48 | fun hideInputForce(activity: Activity?) {
49 | if (activity == null || activity.currentFocus == null)
50 | return
51 | (activity.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager)
52 | .hideSoftInputFromWindow(activity.currentFocus!!
53 | .windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
54 | }
55 |
56 | /**
57 | * 打开键盘
58 | */
59 | fun showInput(context: Context, view: View) {
60 | val imm = context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
61 | if (imm != null) {
62 | view.requestFocus()
63 | imm!!.showSoftInput(view, 0)
64 | }
65 | }
66 |
67 |
68 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/KeyBoardHeightUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.graphics.Rect
6 | import android.os.Build
7 | import android.view.View
8 | import android.view.ViewTreeObserver
9 | import android.widget.FrameLayout
10 |
11 |
12 | /**
13 | * Created by laijian on 2017/8/31.
14 | * 监听软键盘高度
15 | */
16 | class KeyBoardHeightUtils constructor(activity: Activity, private val keyBoardHeightListener: KeyBoardHeightListener) {
17 | private val mChildOfContent: View//activity 的布局View
18 | private var usableHeightPrevious: Int = 0//activity的View的可视高度
19 | private val globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }
20 |
21 | init {
22 | val content = activity.findViewById(android.R.id.content) as FrameLayout
23 | mChildOfContent = content.getChildAt(0)
24 | mChildOfContent.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
25 | content.setOnClickListener({
26 | KeyBoardUtils.hideInputForce(activity)
27 | })
28 | }
29 |
30 | private fun possiblyResizeChildOfContent() {
31 | val usableHeightNow = computeUsableHeight()
32 | if (usableHeightNow != usableHeightPrevious) {
33 | val usableHeightSansKeyboard = mChildOfContent.rootView.height
34 | val heightDifference = usableHeightSansKeyboard - usableHeightNow
35 | if (heightDifference > usableHeightSansKeyboard / 4) {
36 | // keyboard probably just became visible
37 | keyBoardHeightListener.showKeyBoard(usableHeightSansKeyboard - heightDifference, mChildOfContent)
38 | } else {
39 | // keyboard probably just became hidden
40 | keyBoardHeightListener.hideKeyBoard(usableHeightSansKeyboard, mChildOfContent)
41 |
42 | }
43 | mChildOfContent.requestLayout()
44 | usableHeightPrevious = usableHeightNow
45 | }
46 | }
47 |
48 | private fun computeUsableHeight(): Int {
49 | val r = Rect()
50 | mChildOfContent.getWindowVisibleDisplayFrame(r)
51 | return r.bottom - r.top
52 | }
53 |
54 | interface KeyBoardHeightListener {
55 | fun showKeyBoard(height: Int, contentView: View?)
56 | fun hideKeyBoard(height: Int, contentView: View?)
57 |
58 | }
59 |
60 | @SuppressLint("ObsoleteSdkInt")
61 | fun removeKeyboardHeightListener() {
62 |
63 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
64 | mChildOfContent.viewTreeObserver.removeGlobalOnLayoutListener(globalLayoutListener)
65 | } else {
66 | mChildOfContent.viewTreeObserver.removeOnGlobalLayoutListener(globalLayoutListener)
67 | }
68 | }
69 |
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/ResourcesUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * Created by laijian on 2017/8/31.
7 | * 获取资源id
8 | */
9 | object ResourcesUtils {
10 | fun getIdResources(context: Context, idName: String): Int = context.getResourcesId(idName, "id")
11 |
12 | fun getDrawableResources(context: Context, drawableName: String): Int = context.getResourcesId(drawableName, "drawable")
13 |
14 | fun getColorResources(context: Context, colorName: String): Int = context.getResourcesId(colorName, "color")
15 |
16 | fun getStringResources(context: Context, stringName: String): Int = context.getResourcesId(stringName, "string")
17 |
18 | fun getLayoutResources(context: Context, layoutName: String): Int = context.getResourcesId(layoutName, "layout")
19 |
20 | fun getAttrResources(context: Context, attrName: String): Int = context.getResourcesId(attrName, "attr")
21 |
22 | fun getStyleResources(context: Context, styleName: String): Int = context.getResourcesId(styleName, "style")
23 |
24 |
25 | private fun Context.getResourcesId(resourcesName: String, defType: String): Int = this.resources.getIdentifier(resourcesName, defType, this.packageName)
26 |
27 |
28 | /**
29 | * context.getResources().getIdentifier 无法获取到 styleable 的数据
30 | *
31 | * @param name
32 | * @return
33 | * @paramcontext
34 | */
35 |
36 | fun getStyleable(context: Context, name: String): Int {
37 |
38 | return (getResourceId(context, name, "styleable") as Int).toInt()
39 |
40 | }
41 |
42 | /**
43 | * 获取 styleable 的 ID 号数组
44 | *
45 | * @param name
46 | * @return
47 | * @paramcontext
48 | */
49 | fun getStyleableArray(context: Context, name: String): IntArray {
50 | return getResourceId(context, name, "styleable") as IntArray
51 | }
52 |
53 | /**
54 | * 对于 context.getResources().getIdentifier 无法获取的数据 , 或者数组
55 | *
56 | *
57 | * 资源反射值
58 | *
59 | * @param name
60 | * @param type
61 | * @return
62 | * @paramcontext
63 | */
64 |
65 | private fun getResourceId(context: Context, name: String, type: String): Any? {
66 |
67 | val className = context.packageName + ".R"
68 |
69 | try {
70 |
71 | val cls = Class.forName(className)
72 |
73 | for (childClass in cls.classes) {
74 |
75 | val simple = childClass.simpleName
76 |
77 | if (simple == type) {
78 |
79 | for (field in childClass.fields) {
80 |
81 | val fieldName = field.name
82 | if (fieldName == name) {
83 | println(fieldName)
84 | return field.get(null)
85 |
86 | }
87 |
88 | }
89 |
90 | }
91 |
92 | }
93 |
94 | } catch (e: Exception) {
95 |
96 | e.printStackTrace()
97 |
98 | }
99 |
100 | return null
101 |
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/SharedPreferencesUtil.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.SharedPreferences
6 |
7 | /**
8 | * Created by laijian on 2017/8/14.
9 | * SharedPreferences 数据保存
10 | */
11 | object SharedPreferencesUtil {
12 |
13 | fun putBase(context: Context, tableName: String = "apk98_table", keyName: String, value: Any): Boolean {
14 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
15 | val editor: SharedPreferences.Editor = sharedPreferences.edit()
16 | when (value) {
17 | is Int -> editor.putInt(keyName, value)
18 | is Boolean -> editor.putBoolean(keyName, value)
19 | is Float -> editor.putFloat(keyName, value)
20 | is String -> editor.putString(keyName, value)
21 | is Long -> editor.putLong(keyName, value)
22 | else -> throw IllegalArgumentException("SharedPreferences can,t be save this type")
23 | }
24 | return editor.commit()
25 | }
26 |
27 | fun putStringSet(context: Context, tableName: String = "apk98_table", keyName: String, value: Set): Boolean {
28 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
29 | val editor: SharedPreferences.Editor = sharedPreferences.edit()
30 | editor.putStringSet(keyName, value)
31 | return editor.commit()
32 | }
33 |
34 | fun getInt(context: Context, tableName: String = "apk98_table", keyName: String, defaultValue: Int = -1): Int {
35 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
36 | return sharedPreferences.getInt(keyName, defaultValue)
37 | }
38 |
39 | fun getBoolean(context: Context, tableName: String = "apk98_table", keyName: String, defaultValue: Boolean = false): Boolean {
40 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
41 | return sharedPreferences.getBoolean(keyName, defaultValue)
42 | }
43 |
44 | fun getFloat(context: Context, tableName: String = "apk98_table", keyName: String, defaultValue: Float = -1F): Float {
45 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
46 | return sharedPreferences.getFloat(keyName, defaultValue)
47 | }
48 |
49 | fun getString(context: Context, tableName: String = "apk98_table", keyName: String, defaultValue: String? = null): String? {
50 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
51 | return sharedPreferences.getString(keyName, defaultValue)
52 | }
53 |
54 | fun getLong(context: Context, tableName: String = "apk98_table", keyName: String, defaultValue: Long = -1L): Long {
55 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
56 | return sharedPreferences.getLong(keyName, defaultValue)
57 | }
58 |
59 | fun getStringSet(context: Context, tableName: String = "apk98_table", keyName: String, defaultValue: Set? = null): Set? {
60 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
61 | return sharedPreferences.getStringSet(keyName, defaultValue)
62 | }
63 |
64 | fun removeKeyName(context: Context, tableName: String = "apk98_table", keyName: String): Boolean {
65 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
66 | val editor: SharedPreferences.Editor = sharedPreferences.edit()
67 | editor.remove(keyName)
68 | return editor.commit()
69 | }
70 |
71 | fun clearTableName(context: Context, tableName: String = "apk98_table"): Boolean {
72 | val sharedPreferences: SharedPreferences = context.getSharedPreferences(tableName, Activity.MODE_PRIVATE)
73 | val editor: SharedPreferences.Editor = sharedPreferences.edit()
74 | editor.clear()
75 | return editor.commit()
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
42 |
47 |
52 |
57 |
62 |
67 |
72 |
77 |
82 |
87 |
92 |
97 |
102 |
107 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/ScreenUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.graphics.Bitmap
4 | import android.app.Activity
5 | import android.content.Context
6 | import android.graphics.Rect
7 | import android.util.DisplayMetrics
8 | import android.view.View
9 | import android.view.Window
10 | import android.view.WindowManager
11 |
12 |
13 | /**
14 | * Created by laijian on 2017/8/31.
15 | * 获取分辨率 及 分辨率转换 工具类
16 | */
17 | object ScreenUtils {
18 | /**
19 | * 获得屏幕宽高pix
20 | *
21 | * @param context
22 | * @return
23 | */
24 | fun getScreenWidth(context: Context): IntArray {
25 |
26 | val wm = context
27 | .getSystemService(Context.WINDOW_SERVICE) as WindowManager
28 | val outMetrics = DisplayMetrics()
29 | wm.defaultDisplay.getMetrics(outMetrics)
30 | return intArrayOf(outMetrics.widthPixels, outMetrics.heightPixels)
31 | }
32 |
33 |
34 | /**
35 | * 获得状态栏的高度pix
36 | *
37 | * @param context
38 | * @return
39 | */
40 | fun getStatusHeight(context: Context): Int {
41 |
42 | var statusHeight = -1
43 | try {
44 | val clazz = Class.forName("com.android.internal.R\$dimen")
45 | val statusobj = clazz.newInstance()
46 | val height = Integer.parseInt(clazz.getField("status_bar_height")
47 | .get(statusobj).toString())
48 | statusHeight = context.resources.getDimensionPixelSize(height)
49 | } catch (e: Exception) {
50 | e.printStackTrace()
51 | }
52 |
53 | return statusHeight
54 | }
55 |
56 | /***
57 | * 获得标题栏的高度pix
58 | *
59 | * @param activity
60 | * @param context
61 | * @return
62 | */
63 | fun getTitleHeight(activity: Activity, context: Context): Int {
64 | val contentTop = activity.window.findViewById(Window.ID_ANDROID_CONTENT).top
65 | return contentTop - getStatusHeight(context)
66 | }
67 |
68 |
69 | /**
70 | * 获取当前屏幕截图,包含状态栏
71 | *
72 | * @param activity
73 | * @return
74 | */
75 | fun snapShotWithStatusBar(activity: Activity): Bitmap? {
76 | val view = activity.window.decorView
77 | view.isDrawingCacheEnabled = true
78 | view.buildDrawingCache()
79 | val bmp = view.drawingCache
80 | val screen = getScreenWidth(activity)
81 | val width = screen[0]
82 | val height = screen[1]
83 | val bp: Bitmap? = Bitmap.createBitmap(bmp, 0, 0, width, height)
84 | view.destroyDrawingCache()
85 | return bp
86 |
87 | }
88 |
89 | /**
90 | * 获取当前屏幕截图,不包含状态栏
91 | *
92 | * @param activity
93 | * @return
94 | */
95 | fun snapShotWithoutStatusBar(activity: Activity): Bitmap? {
96 | val view = activity.window.decorView
97 | view.isDrawingCacheEnabled = true
98 | view.buildDrawingCache()
99 | val bmp = view.drawingCache
100 | val frame = Rect()
101 | activity.window.decorView.getWindowVisibleDisplayFrame(frame)
102 | val statusBarHeight = frame.top
103 | val screen = getScreenWidth(activity)
104 | val width = screen[0]
105 | val height = screen[1]
106 | var bp: Bitmap? = Bitmap.createBitmap(bmp, 0, statusBarHeight, width, height - statusBarHeight)
107 | view.destroyDrawingCache()
108 | return bp
109 |
110 | }
111 |
112 | /**
113 | * 将px值转换为dip或dp值,保证尺寸大小不变
114 | *
115 | * @param pxValue (DisplayMetrics类中属性density)
116 | * @return
117 | */
118 | fun px2dip(context: Context, pxValue: Float): Int {
119 | val scale = context.resources.displayMetrics.density
120 | return (pxValue / scale + 0.5f).toInt()
121 | }
122 |
123 | /**
124 | * 将dip或dp值转换为px值,保证尺寸大小不变
125 | *
126 | * @param dipValue (DisplayMetrics类中属性density)
127 | * @return
128 | */
129 | fun dip2px(context: Context, dipValue: Float): Int {
130 | val scale = context.resources.displayMetrics.density
131 | return (dipValue * scale + 0.5f).toInt()
132 | }
133 |
134 | /**
135 | * 将px值转换为sp值,保证文字大小不变
136 | *
137 | * @param pxValue (DisplayMetrics类中属性scaledDensity)
138 | * @return
139 | */
140 | fun px2sp(context: Context, pxValue: Float): Int {
141 | val fontScale = context.resources.displayMetrics.scaledDensity
142 | return (pxValue / fontScale + 0.5f).toInt()
143 | }
144 |
145 | /**
146 | * 将sp值转换为px值,保证文字大小不变
147 | *
148 | * @param spValue (DisplayMetrics类中属性scaledDensity)
149 | * @return
150 | */
151 | fun sp2px(context: Context, spValue: Float): Int {
152 | val fontScale = context.resources.displayMetrics.scaledDensity
153 | return (spValue * fontScale + 0.5f).toInt()
154 | }
155 | }
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/KeyBoardUI.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.app.Activity
4 | import android.app.Dialog
5 | import android.content.Context
6 | import android.graphics.Rect
7 | import android.text.Editable
8 | import android.text.TextUtils
9 | import android.text.TextWatcher
10 | import android.util.DisplayMetrics
11 | import android.view.LayoutInflater
12 | import android.view.View
13 | import android.widget.EditText
14 | import android.widget.RelativeLayout
15 | import apk98.com.androidutilslib.widget.NullMenuEditText
16 |
17 |
18 | /**
19 | * Created by laijian on 2017/8/31.
20 | * 软键盘上显示EditText
21 | */
22 | class KeyBoardUI private constructor(private val activity: Activity) : KeyBoardHeightUtils.KeyBoardHeightListener {
23 |
24 | private lateinit var edtextView: EditText //activity的输入框
25 | private lateinit var mDialog: Dialog
26 | private lateinit var popuEdtext: NullMenuEditText //popu的输入框
27 | private var screenWeight = 0//屏幕宽度
28 | private var keyBoardHeightUtils: KeyBoardHeightUtils? = null
29 |
30 | init {
31 | getScreen()
32 | initDialog()
33 | keyBoardHeightUtils = KeyBoardHeightUtils(activity, this)
34 | }
35 |
36 | private fun getScreen() {
37 | val dm = DisplayMetrics()
38 | activity.windowManager.defaultDisplay.getMetrics(dm)
39 | screenWeight = dm.widthPixels
40 | }
41 |
42 | private fun initDialog() {
43 | val inflater = activity.baseContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
44 | val popuView = inflater.inflate(ResourcesUtils.getLayoutResources(activity, "popuwindow"), null)
45 | val populay: RelativeLayout = popuView.findViewById(ResourcesUtils.getIdResources(activity, "popu_lay"))
46 | popuEdtext = popuView.findViewById(ResourcesUtils.getIdResources(activity, "ed_text"))
47 | mDialog = Dialog(activity, ResourcesUtils.getStyleResources(activity, "dialog"))
48 | mDialog.setContentView(popuView)
49 | populay.setOnClickListener({
50 | KeyBoardUtils.closeKeybord(popuEdtext, activity)
51 | mDialog.dismiss()
52 |
53 | })
54 |
55 | }
56 |
57 | private fun checkViewVisiable(): Boolean {
58 | val localRect = Rect()
59 | return edtextView.getLocalVisibleRect(localRect)
60 | }
61 |
62 | private fun onEdChange() {
63 |
64 | var hintStr = ""
65 | var text = ""
66 |
67 | if (!TextUtils.isEmpty(edtextView.text)) {
68 | text = edtextView.text.toString()
69 | }
70 | if (!TextUtils.isEmpty(edtextView.hint)) {
71 | hintStr = edtextView.hint.toString()
72 | }
73 | popuEdtext.findFocus()
74 | popuEdtext.inputType = edtextView.inputType
75 | popuEdtext.hint = hintStr
76 | popuEdtext.setText(text)
77 | popuEdtext.setSelection(text.length)
78 | popuEdtext.maxEms = edtextView.maxEms
79 | popuEdtext.addTextChangedListener(object : TextWatcher {
80 | override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
81 |
82 | }
83 |
84 | override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
85 | edtextView.setText(s)
86 | edtextView.setSelection(s.length)
87 | }
88 |
89 | override fun afterTextChanged(s: Editable) {
90 |
91 | }
92 | })
93 | popuEdtext.setOnEditorActionListener(edLis@ { _, actionId, _ ->
94 | if (actionId == 0) {
95 | KeyBoardUtils.closeKeybord(popuEdtext, activity)
96 | mDialog.dismiss()
97 | return@edLis true
98 | }
99 | false
100 | })
101 | }
102 |
103 | override fun showKeyBoard(height: Int, contentView: View?) {
104 | if (contentView != null) {
105 | val childView = contentView.findFocus()
106 | if (childView != null) {
107 | if (childView is EditText) {
108 | edtextView = childView
109 | if (!checkViewVisiable()) {
110 | mDialog.show()
111 | val dialogWindow = mDialog.window
112 | val p = dialogWindow.attributes // 获取对话框当前的参数值
113 | p.height = height // 高度设置为屏幕的0.6,根据实际情况调整
114 | p.width = screenWeight // 宽度设置为屏幕的0.65,根据实际情况调整
115 | dialogWindow.attributes = p
116 | dialogWindow.setWindowAnimations(ResourcesUtils.getStyleResources(activity, "PopupAnimation"))
117 | onEdChange()
118 | KeyBoardUtils.openKeybord(edtextView!!, activity)
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
125 | override fun hideKeyBoard(height: Int, contentView: View?) {
126 | mDialog.dismiss()
127 | }
128 |
129 |
130 | companion object {
131 | fun buildKeyBoardUI(activity: Activity): KeyBoardUI = KeyBoardUI(activity)
132 |
133 | }
134 |
135 | fun removeKeyboardHeightListener() {
136 | keyBoardHeightUtils?.removeKeyboardHeightListener()
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/ValidatorUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import java.util.regex.Pattern
4 |
5 | /**
6 | * Created by laijian on 2017/8/31.
7 | */
8 | object ValidatorUtils {
9 | /**
10 | * 手机号码,中间4位星号替换
11 | *
12 | * @param phone 手机号
13 | * @return 星号替换的手机号
14 | */
15 | fun phoneNoHide(phone: String): String {
16 | // 括号表示组,被替换的部分$n表示第n组的内容
17 | // 正则表达式中,替换字符串,括号的意思是分组,在replace()方法中,
18 | // 参数二中可以使用$n(n为数字)来依次引用模式串中用括号定义的字串。
19 | // "(\d{3})\d{4}(\d{4})", "$1****$2"的这个意思就是用括号,
20 | // 分为(前3个数字)中间4个数字(最后4个数字)替换为(第一组数值,保持不变$1)(中间为*)(第二组数值,保持不变$2)
21 | return phone.replace("(\\d{3})\\d{4}(\\d{4})".toRegex(), "$1****$2")
22 | }
23 |
24 | /**
25 | * 银行卡号,保留最后4位,其他星号替换
26 | *
27 | * @param cardId 卡号
28 | * @return 星号替换的银行卡号
29 | */
30 | fun cardIdHide(cardId: String): String {
31 | return cardId.replace("\\d{15}(\\d{3})".toRegex(), "**** **** **** **** $1")
32 | }
33 |
34 | /**
35 | * 身份证号,中间10位星号替换
36 | *
37 | * @param id 身份证号
38 | * @return 星号替换的身份证号
39 | */
40 | fun idHide(id: String): String {
41 | return id.replace("(\\d{4})\\d{10}(\\d{4})".toRegex(), "$1** **** ****$2")
42 | }
43 |
44 | /**
45 | * 是否为车牌号(沪A88888)
46 | *
47 | * @param vehicleNo 车牌号
48 | * @return 是否为车牌号
49 | */
50 |
51 | fun checkVehicleNo(vehicleNo: String): Boolean {
52 | val pattern = Pattern.compile("^[\u4e00-\u9fa5]{1}[a-zA-Z]{1}[a-zA-Z_0-9]{5}$")
53 | return pattern.matcher(vehicleNo).find()
54 |
55 | }
56 |
57 | /**
58 | * 验证身份证号码
59 | *
60 | * @param idCard 居民身份证号码15位或18位,最后一位可能是数字或字母
61 | * @return 验证成功返回true,验证失败返回false
62 | */
63 | fun checkIdCard(idCard: String): Boolean {
64 | val regex = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}"
65 | return Pattern.matches(regex, idCard)
66 | }
67 |
68 | /**
69 | * 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港))
70 | *
71 | * @param mobile 移动、联通、电信运营商的号码段
72 | *
73 | * 移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡)
74 | * 、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)
75 | *
76 | * 联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)
77 | *
78 | * 电信的号段:133、153、180(未启用)、189
79 | * @return 验证成功返回true,验证失败返回false
80 | */
81 | fun checkMobile(mobile: String): Boolean {
82 | val regex = "(\\+\\d+)?1[3458]\\d{9}$"
83 | return Pattern.matches(regex, mobile)
84 | }
85 |
86 | /**
87 | * 验证固定电话号码
88 | *
89 | * @param phone 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447
90 | *
91 | * **国家(地区) 代码 :**标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9 的一位或多位数字,
92 | * 数字之后是空格分隔的国家(地区)代码。
93 | *
94 | * **区号(城市代码):**这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号——
95 | * 对不使用地区或城市代码的国家(地区),则省略该组件。
96 | *
97 | * **电话号码:**这包含从 0 到 9 的一个或多个数字
98 | * @return 验证成功返回true,验证失败返回false
99 | */
100 | fun checkPhone(phone: String): Boolean {
101 | val regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$"
102 | return Pattern.matches(regex, phone)
103 | }
104 |
105 | /**
106 | * 验证Email
107 | *
108 | * @param email email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商
109 | * @return 验证成功返回true,验证失败返回false
110 | */
111 | fun checkEmail(email: String): Boolean {
112 | val regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?"
113 | return Pattern.matches(regex, email)
114 | }
115 |
116 | /**
117 | * 验证整数(正整数和负整数)
118 | *
119 | * @param digit 一位或多位0-9之间的整数
120 | * @return 验证成功返回true,验证失败返回false
121 | */
122 | fun checkDigit(digit: String): Boolean {
123 | val regex = "\\-?[1-9]\\d+"
124 | return Pattern.matches(regex, digit)
125 | }
126 |
127 | /**
128 | * 验证整数和浮点数(正负整数和正负浮点数)
129 | *
130 | * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30
131 | * @return 验证成功返回true,验证失败返回false
132 | */
133 | fun checkDecimals(decimals: String): Boolean {
134 | val regex = "\\-?[1-9]\\d+(\\.\\d+)?"
135 | return Pattern.matches(regex, decimals)
136 | }
137 |
138 | /**
139 | * 验证空白字符
140 | *
141 | * @param blankSpace 空白字符,包括:空格、\t、\n、\r、\f、\x0B
142 | * @return 验证成功返回true,验证失败返回false
143 | */
144 | fun checkBlankSpace(blankSpace: String): Boolean {
145 | val regex = "\\s+"
146 | return Pattern.matches(regex, blankSpace)
147 | }
148 |
149 | /**
150 | * 验证中文
151 | *
152 | * @param chinese 中文字符
153 | * @return 验证成功返回true,验证失败返回false
154 | */
155 | fun checkChinese(chinese: String): Boolean {
156 | val regex = "^[\u4E00-\u9FA5]+$"
157 | return Pattern.matches(regex, chinese)
158 | }
159 |
160 | /**
161 | * 验证日期(年月日)
162 | *
163 | * @param birthday 日期,格式:1992-09-03,或1992.09.03
164 | * @return 验证成功返回true,验证失败返回false
165 | */
166 | fun checkBirthday(birthday: String): Boolean {
167 | val regex = "[1-9]{4}([-./])\\d{1,2}\\1\\d{1,2}"
168 | return Pattern.matches(regex, birthday)
169 | }
170 |
171 | /**
172 | * 验证URL地址
173 | *
174 | * @param url 格式:http://blog.csdn.net:80/xyang81/article/details/7705960? 或 http://www.csdn.net:80
175 | * @return 验证成功返回true,验证失败返回false
176 | */
177 | fun checkURL(url: String): Boolean {
178 | val regex = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?"
179 | return Pattern.matches(regex, url)
180 | }
181 |
182 | /**
183 | * 匹配中国邮政编码
184 | *
185 | * @param postcode 邮政编码
186 | * @return 验证成功返回true,验证失败返回false
187 | */
188 | fun checkPostcode(postcode: String): Boolean {
189 | val regex = "[1-9]\\d{5}"
190 | return Pattern.matches(regex, postcode)
191 | }
192 |
193 | /**
194 | * 匹配IP地址(简单匹配,格式,如:192.168.1.1,127.0.0.1,没有匹配IP段的大小)
195 | *
196 | * @param ipAddress IPv4标准地址
197 | * @return 验证成功返回true,验证失败返回false
198 | */
199 | fun checkIpAddress(ipAddress: String): Boolean {
200 | val regex = "[1-9](\\d{1,2})?\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))\\.(0|([1-9](\\d{1,2})?))"
201 | return Pattern.matches(regex, ipAddress)
202 | }
203 |
204 | }
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/LogUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.util.Log
4 | import java.io.File
5 | import java.text.SimpleDateFormat
6 | import java.util.*
7 | import java.util.concurrent.ExecutorService
8 | import java.util.concurrent.Executors
9 |
10 |
11 | /**
12 | * Created by laijian on 2017/8/4.
13 | * 日志工具类
14 | */
15 | object LogUtils {
16 |
17 | private val TOP_LINE = "" +
18 | "\n^^^^^^^^^^^^^less code,less bug^^^^^^^^^^^^^^\n" +
19 | " _ooOoo_\n" +
20 | " o8888888o\n" +
21 | " 88\" . \"88\n" +
22 | " (| -_- |)\n" +
23 | " O\\ = /O\n" +
24 | " ____/`---'\\____\n" +
25 | " .' \\\\| |// `.\n" +
26 | " / \\\\||| : |||// \\\n" +
27 | " / _||||| -:- |||||- \\\n" +
28 | " | | \\\\\\ - /// | |\n" +
29 | " | \\_| ''\\---/'' | |\n" +
30 | " \\ .-\\__ `-` ___/-. /\n" +
31 | " ___`. .' /--.--\\ `. . __\n" +
32 | " .\"\" '< `.___\\_<|>_/___.' >'\"\".\n" +
33 | " | | : `- \\`.;`\\ _ /`;.`/ - ` : | |\n" +
34 | " \\ \\ `-. \\_ __\\ /__ _/ .-` / /\n" +
35 | "======`-.____`-.___\\_____/___.-`____.-'======\n" +
36 | " `=---='\n" +
37 | "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
38 | " 佛祖保佑 永无BUG\n" +
39 | "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"
40 |
41 | private val TOP_BORDER = "╔══════════════════════════════════════════════════════════════════════════════════════════════════════════"
42 | private val LEFT_BORDER = "║ "
43 | private val BOTTOM_BORDER = "╚══════════════════════════════════════════════════════════════════════════════════════════════════════════"
44 | private var debug: Boolean = true//是否打印log
45 | private var savesd: Boolean = false//是否存log到sd卡
46 | private val CHUNK_SIZE = 106 //设置字节数
47 | private var logDir = ""//设置文件存储目录
48 | private var logSize = 2 * 1024 * 1024L//设置log文件大小 k
49 | private val execu: ExecutorService = Executors.newFixedThreadPool(1)
50 |
51 | init {
52 | Log.e("www.hotapk.cn_log", TOP_LINE)
53 | initLogFile()
54 | }
55 |
56 |
57 | fun v(tag: String = "www.hotapk.cn_log", msg: String) = debug.debugLog(tag, msg, Log.VERBOSE)
58 | fun d(tag: String = "www.hotapk.cn_log", msg: String) = debug.debugLog(tag, msg, Log.DEBUG)
59 | fun i(tag: String = "www.hotapk.cn_log", msg: String) = debug.debugLog(tag, msg, Log.INFO)
60 | fun w(tag: String = "www.hotapk.cn_log", msg: String) = debug.debugLog(tag, msg, Log.WARN)
61 | fun e(tag: String = "www.hotapk.cn_log", msg: String) = debug.debugLog(tag, msg, Log.ERROR)
62 |
63 |
64 | private fun targetStackTraceMSg(): String {
65 | val targetStackTraceElement = getTargetStackTraceElement()
66 | if (targetStackTraceElement != null) {
67 | return "at ${targetStackTraceElement.className}.${targetStackTraceElement.methodName}(${targetStackTraceElement.fileName}:${targetStackTraceElement.lineNumber})"
68 | } else {
69 | return ""
70 | }
71 | }
72 |
73 | private fun getTargetStackTraceElement(): StackTraceElement? {
74 | var targetStackTrace: StackTraceElement? = null
75 | var shouldTrace = false
76 | val stackTrace = Thread.currentThread().stackTrace
77 | for (stackTraceElement in stackTrace) {
78 | val isLogMethod = stackTraceElement.className == LogUtils::class.java.name
79 | if (shouldTrace && !isLogMethod) {
80 | targetStackTrace = stackTraceElement
81 | break
82 | }
83 | shouldTrace = isLogMethod
84 | }
85 | return targetStackTrace
86 | }
87 |
88 |
89 | private fun initLogFile() {
90 | logDir = "${FileUtils.getRootDir()}/hotapk.cn"
91 | FileUtils.mkDir(logDir)
92 | }
93 |
94 | private fun Boolean.debugLog(tag: String, msg: String, type: Int) {
95 | if (!this) {
96 | return
97 | }
98 | val newMsg = msgFormat(msg)
99 |
100 | savesd.saveToSd("${SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(Date())}\n${targetStackTraceMSg()}", msg)
101 | when (type) {
102 | Log.VERBOSE -> Log.v(tag, newMsg)
103 | Log.DEBUG -> Log.d(tag, newMsg)
104 | Log.INFO -> Log.i(tag, newMsg)
105 | Log.WARN -> Log.w(tag, newMsg)
106 | Log.ERROR -> Log.e(tag, newMsg)
107 | }
108 |
109 | }
110 |
111 | private fun msgFormat(msg: String): String {
112 | val bytes: ByteArray = msg.toByteArray()
113 | val length = bytes.size
114 | val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
115 | var newMsg = "$TOP_BORDER\n$LEFT_BORDER\t${sdf.format(Date())}\n$LEFT_BORDER\t${targetStackTraceMSg()}"
116 | if (length > CHUNK_SIZE) {
117 | var i = 0
118 | while (i < length) {
119 | val count = Math.min(length - i, CHUNK_SIZE)
120 | val tempStr = String(bytes, i, count)
121 | newMsg += "\n$LEFT_BORDER\t$tempStr"
122 | i += CHUNK_SIZE
123 | }
124 | } else {
125 | newMsg += "\n$LEFT_BORDER\t$msg"
126 | }
127 | newMsg += "\n$BOTTOM_BORDER"
128 | return newMsg
129 |
130 | }
131 |
132 | private fun Boolean.saveToSd(tag: String, msg: String) {
133 | if (!this) {
134 | return
135 | }
136 | execu.submit({
137 | val data = SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date())
138 | var files = FileUtils.sortByTime(File(logDir))?.filter { it -> it.name.contains(data) }
139 | var filepath: String
140 | if (files != null && files.isNotEmpty()) {
141 | val length: Long = FileUtils.getLeng(files[0])
142 | if (length > logSize) {
143 | val id = files[0].name.replace("${data}_", "").replace(".log", "").toInt() + 1
144 | filepath = "$logDir/${data}_$id.log"
145 | FileUtils.creatFile(filepath)
146 | } else {
147 | filepath = files[0].absolutePath
148 | }
149 | } else {
150 | filepath = "$logDir/${data}_1.log"
151 | FileUtils.creatFile(filepath)
152 | }
153 | FileUtils.appendText(File(filepath), "\r\n$tag\n$msg")
154 | })
155 |
156 | }
157 |
158 |
159 | /**
160 | * 是否打印log输出
161 | * @param debug
162 | */
163 | fun debug(debug: Boolean): LogUtils {
164 | LogUtils.debug = debug
165 | return this
166 | }
167 |
168 | /**
169 | * 是否保存到sd卡
170 | * @param savesd
171 | */
172 | fun saveSd(savesd: Boolean): LogUtils {
173 | LogUtils.savesd = savesd
174 | return this
175 | }
176 |
177 | /**
178 | * 设置每个log的文件大小
179 | * @param logSize 文件大小 byte
180 | */
181 | fun logSize(logSize: Long): LogUtils {
182 | LogUtils.logSize = logSize
183 | return this
184 |
185 | }
186 |
187 | /**
188 | * 设置log文件目录
189 | * @param logDir 文件目录
190 | */
191 | fun logDir(logDir: String): LogUtils {
192 | LogUtils.logDir = logDir
193 | return this
194 | }
195 |
196 |
197 | }
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/FileUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import android.os.Environment
4 | import java.io.*
5 |
6 | /**
7 | * Created by laijian on 2017/8/4.
8 | * 文件操作工具类
9 | */
10 | object FileUtils {
11 |
12 | /**
13 | * 获取根目录
14 | */
15 | fun getRootDir(): String {
16 |
17 | return if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
18 | Environment.getExternalStorageDirectory()
19 | .absolutePath
20 | } else {
21 | ""
22 | }
23 |
24 | }
25 |
26 | /**
27 | * 可创建多个文件夹
28 | * dirPath 文件路径
29 | */
30 | fun mkDir(dirPath: String) {
31 |
32 | val dirArray = dirPath.split("/".toRegex())
33 | var pathTemp = ""
34 | for (i in 1 until dirArray.size) {
35 | pathTemp = "$pathTemp/${dirArray[i]}"
36 | val newF = File("${dirArray[0]}$pathTemp")
37 | if (!newF.exists()) {
38 | val cheatDir: Boolean = newF.mkdir()
39 | println(cheatDir)
40 | }
41 | }
42 |
43 | }
44 |
45 | /**
46 | * 创建文件
47 | *
48 | * dirpath 文件目录
49 | * fileName 文件名称
50 | */
51 | fun creatFile(dirPath: String = getRootDir(), fileName: String) {
52 | val file = File("$dirPath/$fileName")
53 | if (!file.exists()) {
54 | file.createNewFile()
55 | }
56 |
57 | }
58 |
59 | /**
60 | * 创建文件
61 | * filePath 文件路径
62 | */
63 | fun creatFile(filePath: String) {
64 | val file = File(filePath)
65 | if (!file.exists()) {
66 | file.createNewFile()
67 | }
68 | }
69 |
70 | /**
71 | * 创建文件
72 | * filePath 文件路径
73 | */
74 | fun creatFile(filePath: File) {
75 | if (!filePath.exists()) {
76 | filePath.createNewFile()
77 | }
78 | }
79 |
80 | /**
81 | * 删除文件
82 | *
83 | * dirpath 文件目录
84 | * fileName 文件名称
85 | */
86 | fun delFile(dirpath: String = getRootDir(), fileName: String): Boolean {
87 | val file = File("$dirpath/$fileName")
88 | if (file.checkFile()) {
89 | return false
90 | }
91 | return file.delete()
92 | }
93 |
94 | /**
95 | * 删除文件
96 | * filepath 文件路径
97 | */
98 | fun delFile(filepath: File): Boolean {
99 | if (filepath.checkFile()) {
100 | return false
101 | }
102 | return filepath.delete()
103 | }
104 |
105 | /**
106 | * 删除文件
107 | * filepath 文件路径
108 | */
109 | fun delFile(filepath: String): Boolean {
110 | val file = File(filepath)
111 | if (file.checkFile()) {
112 | return false
113 | }
114 | return file.delete()
115 | }
116 |
117 |
118 | /**
119 | * 删除文件夹
120 | * dirPath 文件路径
121 | */
122 | fun delDir(dirpath: String) {
123 | val dir = File(dirpath)
124 | deleteDirWihtFile(dir)
125 | }
126 |
127 | fun deleteDirWihtFile(dir: File?) {
128 | if (dir!!.checkFile())
129 | return
130 | for (file in dir.listFiles()) {
131 | if (file.isFile)
132 | file.delete() // 删除所有文件
133 | else if (file.isDirectory)
134 | deleteDirWihtFile(file) // 递规的方式删除文件夹
135 | }
136 | dir.delete()// 删除目录本身
137 | }
138 |
139 | private fun File.checkFile(): Boolean {
140 | return this == null || !this.exists() || !this.isDirectory
141 | }
142 |
143 | /**
144 | * 修改SD卡上的文件或目录名
145 | * oldFilePath 旧文件或文件夹路径
146 | * newFilePath 新文件或文件夹路径
147 | */
148 | fun renameFile(oldFilePath: String, newFilePath: String): Boolean {
149 | val oldFile = File(oldFilePath)
150 | val newFile = File(newFilePath)
151 | return oldFile.renameTo(newFile)
152 | }
153 |
154 |
155 | /**
156 | * 拷贝一个文件
157 | * srcFile源文件
158 | * destFile目标文件
159 | */
160 | @Throws(IOException::class)
161 | fun copyFileTo(srcFile: File, destFile: File): Boolean {
162 | return if (srcFile.isDirectory || destFile.isDirectory) {
163 | false
164 | } else {
165 | val fis = FileInputStream(srcFile)
166 | val fos = FileOutputStream(destFile)
167 | fis.copyTo(fos)
168 | fos.flush()
169 | CloseIoUtils.closeIO(fos, fis)
170 | true
171 | }
172 | }
173 |
174 | /**
175 | *拷贝目录下的所有文件到指定目录
176 | * srcDir 原目录
177 | * destDir 目标目录
178 | */
179 | fun copyFilesTo(srcDir: File, destDir: File): Boolean {
180 | if (!srcDir.isDirectory || !destDir.isDirectory) return false// 判断是否是目录
181 | if (!destDir.exists()) return false// 判断目标目录是否存在
182 | val srcFiles = srcDir.listFiles()
183 | srcFiles.forEach {
184 | if (it.isFile) {
185 | val destFile = File("${destDir.path}/${it.name}")
186 | copyFileTo(it, destFile)
187 | } else {
188 | val theDestDir = File("${destDir.path}/${it.name}")
189 | copyFilesTo(it, theDestDir)
190 | }
191 | }
192 |
193 | return true
194 | }
195 |
196 | /**
197 | * 移动一个文件
198 | */
199 | fun moveFileTo(srcFile: File, destFile: File): Boolean {
200 | if (srcFile.isDirectory || destFile.isDirectory) return false
201 | val iscopy = copyFileTo(srcFile, destFile)
202 | return if (!iscopy) {
203 | false
204 | } else {
205 | delFile(srcFile)
206 | true
207 | }
208 |
209 | }
210 |
211 | /**
212 | * 移动目录下的所有文件到指定目录
213 | * srcDir 原路径
214 | * destDir 目标路径
215 | */
216 | @Throws(IOException::class)
217 | fun moveFilesTo(srcDir: File, destDir: File): Boolean {
218 | if (!srcDir.isDirectory or !destDir.isDirectory) {
219 | return false
220 | } else {
221 | val srcDirFiles = srcDir.listFiles()
222 | srcDirFiles.forEach {
223 | if (it.isFile) {
224 | val oneDestFile = File("${destDir.path}/${it.name}")
225 | moveFileTo(it, oneDestFile)
226 | delFile(it)
227 | } else {
228 | val oneDestFile = File(destDir.path + "//"
229 | + it.name)
230 | moveFilesTo(it, oneDestFile)
231 | delDir(it.absolutePath)
232 | }
233 |
234 | }
235 | return true
236 | }
237 |
238 | }
239 |
240 | /**
241 | * 文件转byte数组
242 | * file 文件路径
243 | */
244 | @Throws(IOException::class)
245 | fun file2byte(file: File): ByteArray? {
246 | var bytes: ByteArray? = null
247 | if (file != null) {
248 | val inp = FileInputStream(file)
249 | val length = file.length() as Int
250 | if (length > Integer.MAX_VALUE) {// 当文件的长度超过了int的最大值
251 | inp.close()
252 | return null
253 | }
254 | bytes = inp.readBytes(length)
255 | CloseIoUtils.closeIO(inp)
256 | }
257 | return bytes
258 | }
259 |
260 | /**
261 | * 文件读取
262 | * filePath 文件路径
263 | */
264 | fun readFile(filePath: File): String? {
265 | if (!filePath.isFile) {
266 | return null
267 | } else {
268 | return filePath.readText()
269 | }
270 | }
271 |
272 | /**
273 | * 文件读取
274 | * strPath 文件路径
275 | */
276 | fun readFile(strPath: String): String? {
277 | return readFile(File(strPath))
278 | }
279 |
280 | /**
281 | * InputStream 转字符串
282 | */
283 | fun readInp(inp: InputStream): String? {
284 | val bytes: ByteArray = inp.readBytes()
285 | return String(bytes)
286 | }
287 |
288 | /**
289 | * BufferedReader 转字符串
290 | */
291 | fun readBuff(buff: BufferedReader): String? {
292 | return buff.readText()
293 | }
294 |
295 | /**
296 | * 写入数据
297 | */
298 | fun writeText(filePath: File, content: String) {
299 | creatFile(filePath)
300 | filePath.writeText(content)
301 | }
302 |
303 | /**
304 | * 追加数据
305 | */
306 | fun appendText(filePath: File, content: String) {
307 | creatFile(filePath)
308 | filePath.appendText(content)
309 | }
310 |
311 | /**
312 | * 追加数据
313 | */
314 | fun appendBytes(filePath: File, array: ByteArray) {
315 | creatFile(filePath)
316 | filePath.appendBytes(array)
317 | }
318 |
319 | /**
320 | * 获取文件大小
321 | */
322 | fun getLeng(filePath: File): Long {
323 | return if (!filePath.exists()) {
324 | -1
325 | } else {
326 | filePath.length()
327 | }
328 | }
329 |
330 | /**
331 | * 按时间排序
332 | */
333 | fun sortByTime(filePath: File): Array? {
334 | if (!filePath.exists()) {
335 | return null
336 | }
337 | val files: Array = filePath.listFiles()
338 | if (files.isEmpty()) {
339 | return null
340 | }
341 | files.sortBy { it.lastModified() }
342 | files.reverse()
343 | return files
344 |
345 | }
346 |
347 |
348 | }
349 |
350 |
--------------------------------------------------------------------------------
/androidutilslib/src/main/java/apk98/com/androidutilslib/utils/TimeUtils.kt:
--------------------------------------------------------------------------------
1 | package apk98.com.androidutilslib.utils
2 |
3 | import java.text.SimpleDateFormat
4 | import java.util.*
5 | import java.util.regex.Pattern
6 | import java.text.ParseException
7 |
8 |
9 | /**
10 | * Created by laijian on 2017/8/14.
11 | * 时间控件
12 | */
13 | object TimeUtils {
14 |
15 | // 格式:年-月-日 小时:分钟:秒
16 | private val FORMAT_YMDHMS = "yyyy-MM-dd HH:mm:ss"
17 | // 格式:年-月-日
18 | private val FORMAT_YMD = "yyyy-MM-dd"
19 |
20 | /**
21 | * 字符串转日期类型
22 | * @param dateStr 日期字符串
23 | * @param format 转换类型
24 | */
25 | fun stringToDate(dateStr: String, format: String = FORMAT_YMDHMS): Date? {
26 |
27 | val formater: SimpleDateFormat = SimpleDateFormat(format, Locale.US)
28 | return try {
29 | formater.isLenient = false
30 | formater.parse(dateStr)
31 | } catch (e: Exception) {
32 | null
33 | }
34 | }
35 |
36 |
37 | /**
38 | * 日期转字符串
39 | */
40 | fun dateToString(date: Date, format: String = FORMAT_YMDHMS): String? {
41 | val formater = SimpleDateFormat(format, Locale.US)
42 | return try {
43 | formater.format(date)
44 | } catch (e: Exception) {
45 | null
46 | }
47 | }
48 |
49 |
50 | /**
51 | * 获取某年某月的天数
52 |
53 | * @param year
54 | * * int
55 | * *
56 | * @param month
57 | * * int 月份[1-12]
58 | * *
59 | * @return int
60 | */
61 | fun getDaysOfMonth(year: Int, month: Int): Int {
62 | val calendar = Calendar.getInstance()
63 | calendar.set(year, month - 1, 1)
64 | return calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
65 | }
66 |
67 | /**
68 | * 获得当前日期
69 |
70 | * @return int
71 | */
72 | fun getToday(): Int {
73 | val calendar = Calendar.getInstance()
74 | return calendar.get(Calendar.DATE)
75 | }
76 |
77 | /**
78 | * 获得当前月份
79 |
80 | * @return int
81 | */
82 | fun getToMonth(): Int {
83 | val calendar = Calendar.getInstance()
84 | return calendar.get(Calendar.MONTH) + 1
85 | }
86 |
87 | /**
88 | * 获得当前年份
89 |
90 | * @return int
91 | */
92 | fun getToYear(): Int {
93 | val calendar = Calendar.getInstance()
94 | return calendar.get(Calendar.YEAR)
95 | }
96 |
97 | /**
98 | * 返回日期的天
99 |
100 | * @param date
101 | * * Date
102 | * *
103 | * @return int
104 | */
105 | fun getDay(date: Date): Int {
106 | val calendar = Calendar.getInstance()
107 | calendar.time = date
108 | return calendar.get(Calendar.DATE)
109 | }
110 |
111 | /**
112 | * 返回日期的年
113 |
114 | * @param date
115 | * * Date
116 | * *
117 | * @return int
118 | */
119 | fun getYear(date: Date): Int {
120 | val calendar = Calendar.getInstance()
121 | calendar.time = date
122 | return calendar.get(Calendar.YEAR)
123 | }
124 |
125 | /**
126 | * 返回日期的月份,1-12
127 |
128 | * @param date
129 | * * Date
130 | * *
131 | * @return int
132 | */
133 | fun getMonth(date: Date): Int {
134 | val calendar = Calendar.getInstance()
135 | calendar.time = date
136 | return calendar.get(Calendar.MONTH) + 1
137 | }
138 |
139 | /**
140 | * 计算两个日期相差的天数,如果date2 > date1 返回正数,否则返回负数
141 |
142 | * @param date1
143 | * * Date
144 | * *
145 | * @param date2
146 | * * Date
147 | * *
148 | * @return long
149 | */
150 | fun dayDiff(date1: Date, date2: Date): Long {
151 | return (date2.time - date1.time) / 86400000
152 | }
153 |
154 | /**
155 | * 比较两个日期的年差
156 |
157 | * @param befor
158 | * *
159 | * @param after
160 | * *
161 | * @return
162 | */
163 | fun yearDiff(before: String, after: String): Int {
164 | val beforeDay = stringToDate(before, FORMAT_YMD)
165 | val afterDay = stringToDate(after, FORMAT_YMD)
166 | if (beforeDay != null && afterDay != null) {
167 | return (getYear(afterDay) - getYear(beforeDay))
168 | } else {
169 | return -1
170 | }
171 | }
172 |
173 | /**
174 | * 获取一天的开始时间
175 | */
176 | fun getFristDayTime(dateFormat: String = FORMAT_YMDHMS, setTime: String): String {
177 | val format = SimpleDateFormat(dateFormat, Locale.US)
178 | val c1 = GregorianCalendar()
179 | try {
180 | c1.time = format.parse(setTime)
181 | } catch (e: ParseException) {
182 | e.printStackTrace()
183 | }
184 | c1.set(Calendar.HOUR_OF_DAY, 0)
185 | c1.set(Calendar.MINUTE, 0)
186 | c1.set(Calendar.SECOND, 0)
187 | return format.format(c1.time)
188 | }
189 |
190 |
191 | /**
192 | * 获取一天的结束时间
193 | */
194 | fun getLastDayTime(dateFormat: String = FORMAT_YMDHMS, setTime: String): String {
195 | val format = SimpleDateFormat(dateFormat, Locale.US)
196 | val c1 = GregorianCalendar()
197 | try {
198 | c1.time = format.parse(setTime)
199 | } catch (e: ParseException) {
200 | e.printStackTrace()
201 | }
202 | c1.set(Calendar.HOUR_OF_DAY, 23);
203 | c1.set(Calendar.MINUTE, 59);
204 | c1.set(Calendar.SECOND, 59);
205 | return format.format(c1.time)
206 | }
207 |
208 | /**
209 | * 比较指定日期与当前日期的差
210 |
211 | * @param befor
212 | * *
213 | * @param after
214 | * *
215 | * @return
216 | */
217 | fun yearDiffCurr(after: String): Int {
218 | val beforeDay = Date()
219 | val afterDay = stringToDate(after, FORMAT_YMD)
220 | return if (afterDay != null) {
221 | (getYear(beforeDay) - getYear(afterDay))
222 | } else {
223 | -1
224 | }
225 | }
226 |
227 | /**
228 | * 获取指定日期的时间差
229 | */
230 | fun yearDiffCurr(after: String, before: String): Int {
231 | val beforeDay = stringToDate(before, FORMAT_YMD)
232 | val afterDay = stringToDate(after, FORMAT_YMD)
233 | return if (afterDay != null && beforeDay != null) {
234 | (getYear(beforeDay) - getYear(afterDay))
235 | } else {
236 | -1
237 | }
238 | }
239 |
240 |
241 | /**
242 | * 获取每月的第一周
243 | * @param year
244 | * *
245 | * @param month
246 | * *
247 | * @return
248 | * *
249 | * @author chenyz
250 | */
251 | fun getFirstWeekdayOfMonth(year: Int, month: Int): Int {
252 | val c = Calendar.getInstance()
253 | c.firstDayOfWeek = Calendar.SATURDAY // 星期天为第一天
254 | c.set(year, month - 1, 1)
255 | return c.get(Calendar.DAY_OF_WEEK)
256 | }
257 |
258 | /**
259 | * 获取每月的最后一周
260 | * @param year
261 | * *
262 | * @param month
263 | * *
264 | * @return
265 | * *
266 | * @author chenyz
267 | */
268 | fun getLastWeekdayOfMonth(year: Int, month: Int): Int {
269 | val c = Calendar.getInstance()
270 | c.firstDayOfWeek = Calendar.SATURDAY // 星期天为第一天
271 | c.set(year, month - 1, getDaysOfMonth(year, month))
272 | return c.get(Calendar.DAY_OF_WEEK)
273 | }
274 |
275 | /**
276 | * 获取本月第一天
277 |
278 | * @param format
279 | * *
280 | * @return
281 | */
282 | fun getFirstDayOfMonth(format: String): String {
283 | val cal = Calendar.getInstance()
284 | cal.set(Calendar.DATE, 1)
285 | val firstDayOfMonth = dateToString(cal.time, format)
286 | return firstDayOfMonth ?: ""
287 | }
288 |
289 | /**
290 | * 获取本月最后一天
291 |
292 | * @param format
293 | * *
294 | * @return
295 | */
296 | fun getLastDayOfMonth(format: String): String {
297 | val cal = Calendar.getInstance()
298 | cal.set(Calendar.DATE, 1)
299 | cal.add(Calendar.MONTH, 1)
300 | cal.add(Calendar.DATE, -1)
301 | val lastDayOfMonth = dateToString(cal.time, format)
302 | return lastDayOfMonth ?: ""
303 | }
304 |
305 | /**
306 | * 判断日期是否有效,包括闰年的情况
307 |
308 | * @param date
309 | * * YYYY-mm-dd
310 | * *
311 | * @return
312 | */
313 | fun isDate(date: String): Boolean {
314 | val reg = StringBuffer(
315 | "^((\\d{2}(([02468][048])|([13579][26]))-?((((0?")
316 | reg.append("[13578])|(1[02]))-?((0?[1-9])|([1-2][0-9])|(3[01])))")
317 | reg.append("|(((0?[469])|(11))-?((0?[1-9])|([1-2][0-9])|(30)))|")
318 | reg.append("(0?2-?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][12")
319 | reg.append("35679])|([13579][01345789]))-?((((0?[13578])|(1[02]))")
320 | reg.append("-?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))")
321 | reg.append("-?((0?[1-9])|([1-2][0-9])|(30)))|(0?2-?((0?[")
322 | reg.append("1-9])|(1[0-9])|(2[0-8]))))))")
323 | val p = Pattern.compile(reg.toString())
324 | return p.matcher(date).matches()
325 | }
326 |
327 | /**
328 | * 根据生日获取星座
329 |
330 | * @param birth
331 | * * YYYY-mm-dd
332 | * *
333 | * @return
334 | */
335 | fun getAstro(birth: String): String {
336 | var birth = birth
337 | if (!isDate(birth)) {
338 | birth = "2000" + birth
339 | }
340 | if (!isDate(birth)) {
341 | return ""
342 | }
343 | val month = Integer.parseInt(birth.substring(birth.indexOf("-") + 1,
344 | birth.lastIndexOf("-")))
345 | val day = Integer.parseInt(birth.substring(birth.lastIndexOf("-") + 1))
346 | val s = "魔羯水瓶双鱼牡羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯"
347 | val arr = intArrayOf(20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22)
348 | val start = month * 2 - if (day < arr[month - 1]) 2 else 0
349 | return s.substring(start, start + 2) + "座"
350 | }
351 |
352 |
353 | }
--------------------------------------------------------------------------------