├── 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 | } --------------------------------------------------------------------------------