├── .gitignore ├── DETAIL.md ├── LICENSE ├── README.md ├── build.gradle ├── ext ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── pers │ └── victor │ └── ext │ ├── ActivityExt.kt │ ├── ActivityMgr.kt │ ├── ApiExt.kt │ ├── BitmapExt.kt │ ├── ByteArrayExt.kt │ ├── CommonExt.kt │ ├── DateTimeExt.kt │ ├── DisplayExt.kt │ ├── Ext.kt │ ├── FileExt.kt │ ├── FragmentExt.kt │ ├── IntentData.kt │ ├── ListenerExt.kt │ ├── ManagerExt.kt │ ├── PreferencesExt.kt │ ├── SizeConversionExt.kt │ ├── StringExt.kt │ ├── ToastExt.kt │ ├── ViewExt.kt │ └── Wrappers.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | /local.properties 4 | .DS_Store 5 | build 6 | /captures 7 | .externalNativeBuild 8 | *.iml 9 | /app -------------------------------------------------------------------------------- /DETAIL.md: -------------------------------------------------------------------------------- 1 | ### Usage 2 | 3 | ```kotlin 4 | //初始化 5 | Ext.with(application) 6 | ``` 7 | 8 | ### ViewExt 9 | 10 | ```kotlin 11 | //设置padding 12 | fun View.setPaddingLeft(value: Int) 13 | fun View.setPaddingRight(value: Int) 14 | fun View.setPaddingTop(value: Int) 15 | fun View.setPaddingBottom(value: Int) 16 | fun View.setPaddingStart(value: Int) 17 | fun View.setPaddingEnd(value: Int) 18 | fun View.setPaddingHorizontal(value: Int) 19 | fun View.setPaddingVertical(value: Int) 20 | 21 | //设置宽高 22 | fun View.setWidth(value: Int) 23 | fun View.setHeight(value: Int) 24 | fun View.resize(width: Int, height: Int) 25 | 26 | //设置宽高(动画) 27 | fun View.animateWidth(toValue: Int, duration: Long, interpolator: Interpolator) 28 | fun View.animateWidthBy(toValue: Int, duration: Long, interpolator: Interpolator) 29 | fun View.animateHeight(toValue: Int, duration: Long, interpolator: Interpolator) 30 | fun View.animateHeightBy(toValue: Int, duration: Long, interpolator: Interpolator) 31 | 32 | //移动 33 | fun View.animateX(toValue: Int, duration: Long, interpolator: Interpolator) 34 | fun View.animateXBy(toValue: Int, duration: Long, interpolator: Interpolator) 35 | fun View.animateY(toValue: Int, duration: Long, interpolator: Interpolator) 36 | fun View.animateYBy(toValue: Int, duration: Long, interpolator: Interpolator) 37 | 38 | //ViewGroup子View 39 | val ViewGroup.children: List 40 | 41 | //ViewGroup子View.getChildAt(index) 42 | //val v = ViewGroup[index] 43 | operator fun ViewGroup.get(index: Int) 44 | 45 | 46 | //下划线 47 | fun TextView.underLine() 48 | //中划线 49 | fun TextView.deleteLine() 50 | //加粗 51 | fun TextView.bold(b: Boolean) 52 | 53 | //获取或设置RadioGroup选中的RadioButton的index 54 | var RadioGroup.checkedIndex 55 | 56 | //setText getText 57 | var EditText.value: String 58 | //所有字母大写 59 | fun EditText.uppercase() 60 | //所有字母小写 61 | fun EditText.lowercase() 62 | //显示、隐藏密码 63 | fun EditText.passwordToggledVisible() 64 | 65 | //点击, 调用时lambda里的it为具体View类型 66 | fun T.click(block: (T) -> Unit) 67 | fun T.longClick(block: (T) -> Boolean) 68 | 69 | //显示 70 | fun View.visiable() 71 | fun View.invisiable() 72 | fun View.gone() 73 | 74 | fun View.isVisible(): Boolean 75 | fun View.isInvisible(): Boolean 76 | fun View.isGone(): Boolean 77 | 78 | fun View.visiableIf(block: () -> Boolean) 79 | fun View.invisiableIf(block: () -> Boolean) 80 | fun View.goneIf(block: () -> Boolean) 81 | 82 | //获取view的bitmap 83 | fun View.getBitmap(): Bitmap 84 | ``` 85 | 86 | ### CommonExt 87 | 88 | ```kotlin 89 | val app: Application 90 | val currentTimeMillis: Long 91 | 92 | //强行三目运算符 yes no 93 | val value = bool.yes { "true value" }.no { "false value" } 94 | val value = bool yes { "true value" } no { "false value" } 95 | 96 | //内部使用ContextCompat 97 | fun findColor(@ColorRes resId: Int) 98 | fun findDrawable(@DrawableRes resId: Int) 99 | fun findColorStateList(@ColorRes resId: Int) 100 | 101 | fun inflate(@LayoutRes layoutId: Int, parent: ViewGroup?, attachToRoot: Boolean = false) 102 | fun inflate(@LayoutRes layoutId: Int) 103 | 104 | //跳转到拨号界面 105 | fun Context.dial(tel: String?) 106 | //跳转到短信界面 107 | fun Context.sms(phone: String?, body: String) 108 | //是否在主线程 109 | fun isMainThread() 110 | //是否连接网络 111 | fun isNetworkConnected(): Boolean 112 | 113 | fun Int.isOdd(): Boolean 114 | fun Int.isEven(): Boolean 115 | ``` 116 | 117 | ### DisplayExt 118 | 119 | ```kotlin 120 | //屏幕尺寸、密度 121 | val screenWidth: Int 122 | val screenHeight: Int 123 | val screenDensity: Float 124 | val scaledDensity: Float 125 | 126 | fun dp2px(dp: Number) 127 | fun sp2px(sp: Number) 128 | fun px2dp(px: Number) 129 | fun px2sp(px: Number) 130 | ``` 131 | 132 | ### DateTimeExt 133 | 134 | ```kotlin 135 | //格式化日期 136 | fun Long.date(pattern: String = "yyyy-MM-dd HH:mm:ss") 137 | fun Long.year() 138 | fun Long.month() 139 | fun Long.day() 140 | fun Long.hour() 141 | fun Long.minute() 142 | fun Long.second() 143 | fun Long.week() 144 | fun Long.dateOnly(split: String) 145 | fun Long.timeOnly(split: String) 146 | 147 | //一年第几天 148 | fun Long.dayOfYear() 149 | //一年第几周 150 | fun Long.weekOfYear() 151 | //获取星座 152 | fun Long.constellation() 153 | //是否闰年 154 | fun Int.isLeapYear() 155 | ``` 156 | 157 | ### StringExt 158 | 159 | ```kotlin 160 | fun String.toast() 161 | fun String.md5() 162 | fun String.sha1() 163 | 164 | //是否是身份证 165 | fun String.isIdcard() 166 | //是否是手机号 167 | fun String.isPhone() 168 | //是否是邮箱 169 | fun String.isEmail() 170 | //是否是纯数字 171 | fun String.isNumeric() 172 | //不考虑大小写比较 173 | fun String.equalsIgnoreCase(other: String) 174 | ``` 175 | 176 | ### ListenerExt 177 | 178 | ```kotlin 179 | fun Animator.addListener { 180 | onStart { } 181 | onCancel { } 182 | onEnd { } 183 | onRepeat { } 184 | } 185 | 186 | fun Animator.addPauseListener { 187 | onPause { } 188 | onResume { } 189 | } 190 | 191 | fun EditText.addTextChangedListener { 192 | before { s, start, count, after -> } 193 | on { s, start, before, count -> } 194 | after { } 195 | } 196 | 197 | fun ViewPager.addOnPageChangeListener { 198 | onPageScrollStateChanged { } 199 | onPageSelected { } 200 | onPageScrolled { position, positionOffset, positionOffsetPixels -> } 201 | } 202 | ``` 203 | 204 | ### ManagerExt 205 | 206 | ```kotlin 207 | //直接调用 208 | val connectivityManager 209 | val alarmManager 210 | val telephonyManager 211 | val activityManager 212 | val notificationManager 213 | val appWidgetManager 214 | val inputMethodManager 215 | val clipboardManager 216 | val bluetoothManager 217 | val audioManager 218 | val batteryManager 219 | val cameraManager 220 | val vibrator 221 | ``` 222 | 223 | ### SharedPreferencesExt 224 | 225 | ```kotlin 226 | fun spSetInt(key: String, value: Int) 227 | fun spGetInt(key: String, defaultValue: Int = 0) 228 | fun spSetLong(key: String, value: Long) 229 | fun spGetLong(key: String, defaultValue: Long = 0L) 230 | fun spSetFloat(key: String, value: Float) 231 | fun spGetFloat(key: String, defaultValue: Float = 0f) 232 | fun spSetBoolean(key: String, value: Boolean) 233 | fun spGetBoolean(key: String, defaultValue: Boolean = false) 234 | fun spSetString(key: String, value: String) 235 | fun spGetString(key: String, defaultValue: String = "") 236 | fun spRemove(key: String) 237 | fun spClearAll() 238 | ``` 239 | 240 | ### BitmapExt 241 | 242 | ```kotlin 243 | //bitmap转base64 244 | fun Bitmap.toBase64(): String 245 | //bitmap调整大小 246 | fun Bitmap.resize(w: Number, h: Number): Bitmap 247 | //保存bitmap到文件 248 | fun Bitmap.saveFile(path: String) 249 | //bitmap转byte[] 250 | fun Bitmap.toByteArray(): ByteArray 251 | ``` 252 | 253 | ### FileExt 254 | 255 | ```kotlin 256 | //复制文件 257 | fun File.copy(dest: File) 258 | //移动文件 259 | fun File.move(dest: File) 260 | //复制文件夹 261 | fun File.copyDirectory(dest: File) 262 | //移动文件夹 263 | fun File.moveDirectory(dest: File) 264 | //删除文件夹及其下所有文件 265 | fun File.deleteAll() 266 | 267 | fun File.md5() 268 | fun File.sha1() 269 | 270 | //File转byte[] 271 | fun File.toByteArray(): ByteArray 272 | ``` 273 | 274 | ### ToastExt 275 | 276 | ```kotlin 277 | fun toast(msg: Any, isShortToast: Boolean = true) 278 | ``` 279 | 280 | ### ActivityExt 281 | 282 | ```kotlin 283 | fun Activity.goActivity() 284 | fun Activity.goActivity(requestCode: Int) 285 | 286 | fun Activity.goService() 287 | fun Activity.goService(sc: ServiceConnection, flags: Int) 288 | 289 | fun Activity.hideInputMethod() 290 | fun Activity.showInputMethod(v: EditText) 291 | 292 | //清除window背景 293 | fun Activity.clearWindowBackground() 294 | fun Activity.steepStatusBar() 295 | 296 | fun AppCompatActivity.addFragments(fragments: List, containerId: Int) 297 | fun AppCompatActivity.showFragment(fragment: Fragment) 298 | fun AppCompatActivity.hideFragment(fragment: Fragment) 299 | ``` 300 | 301 | ### FragmentExt (support.v4) 302 | 303 | ```kotlin 304 | fun Fragment.goActivity() 305 | fun Fragment.goActivity(requestCode: Int) 306 | 307 | fun Fragment.goService() 308 | fun Fragment.goService(sc: ServiceConnection, flags: Int) 309 | 310 | fun Fragment.hideInputMethod() 311 | fun Fragment.showInputMethod(v: EditText) 312 | 313 | //finish所在的Activity 314 | fun Fragment.finish() 315 | ``` 316 | 317 | ### ByteArrayExt 318 | 319 | ```kotlin 320 | //保存byte[]到文件 321 | fun ByteArray.saveFile(path: String) 322 | //byte[]转Bitmap 323 | fun ByteArray.toBitmap(opts: BitmapFactory.Options): Bitmap 324 | ``` 325 | 326 | ### ActivityMgr 327 | 328 | ```kotlin 329 | //建议放在BaseActivity里,onCreate()里ActivityMgr.add,onDestroy()里ActivityMgr.remove 330 | fun add(activity: Activity) 331 | fun remove(activity: Activity) 332 | fun removeAll() 333 | fun current(): Activity? 334 | fun findFirst(clazz: KClass): T? 335 | fun findLast(clazz: KClass): T? 336 | fun find(clazz: KClass): List 337 | fun finishFirst(clazz: KClass<*>) 338 | fun finishLast(clazz: KClass<*>) 339 | fun finish(clazz: KClass<*>) 340 | fun finishExcept(vararg clazz: KClass<*>) 341 | fun finishAll() 342 | ``` 343 | 344 | ### ApiExt 345 | 346 | ```kotlin 347 | // aboveApi(20) { view.elevation = 10f } 348 | fun aboveApi(api: Int, included: Boolean = false, block: () -> Unit) 349 | fun belowApi(api: Int, included: Boolean = false, block: () -> Unit) 350 | ``` 351 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 VictorChow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KotlinAndroidLib [![](https://jitpack.io/v/VictorChow/kotlin-android-lib.svg)](https://jitpack.io/#VictorChow/kotlin-android-lib) 2 | 3 | 一些Android开发的扩展。 4 | 5 | [[点这查看所有支持的扩展API(readmore)]](./DETAIL.md) 6 | 7 | ### 2.x - AndroidX 8 | ### 1.x - Android Support 9 | 10 | ## Usage 11 | 12 | ```kotlin 13 | //初始化 14 | Ext.with(application) 15 | ``` 16 | 17 | ## Gradle 18 | 19 | ```groovy 20 | allprojects { 21 | repositories { 22 | ... 23 | maven { url 'https://jitpack.io' } 24 | } 25 | } 26 | ``` 27 | 28 | ```groovy 29 | //依赖项 30 | //org.jetbrains.kotlin:kotlin-stdlib-jdk7 31 | 32 | 2.x 33 | dependencies { 34 | implementation 'com.github.VictorChow:kotlin-android-lib:2.0.0' 35 | } 36 | 37 | 1.x 38 | dependencies { 39 | implementation 'com.github.VictorChow:kotlin-android-lib:1.2.4' 40 | } 41 | ``` 42 | 43 | ## Brief 44 | 45 | ### ViewExt 46 | 47 | ```kotlin 48 | //设置宽高 49 | fun View.setWidth(value: Int) 50 | fun View.setHeight(value: Int) 51 | fun View.resize(width: Int, height: Int) 52 | ... 53 | ``` 54 | 55 | ### CommonExt 56 | 57 | ```kotlin 58 | fun findColor(@ColorRes resId: Int) 59 | fun findDrawable(@DrawableRes resId: Int) 60 | fun findColorStateList(@ColorRes resId: Int) 61 | ... 62 | ``` 63 | 64 | ### DisplayExt 65 | 66 | ```kotlin 67 | val screenWidth: Int 68 | val screenHeight: Int 69 | val screenDensity: Float 70 | ... 71 | ``` 72 | 73 | ### DateTimeExt 74 | 75 | ```kotlin 76 | fun Long.year() 77 | fun Long.month() 78 | fun Long.day() 79 | ... 80 | ``` 81 | 82 | ### StringExt 83 | 84 | ```kotlin 85 | fun String.toast() 86 | fun String.md5() 87 | fun String.sha1() 88 | ... 89 | ``` 90 | 91 | ### ListenerExt 92 | 93 | ```kotlin 94 | fun Animator.addListener { 95 | onStart { } 96 | onCancel { } 97 | onEnd { } 98 | onRepeat { } 99 | } 100 | 101 | fun Animator.addPauseListener { 102 | onPause { } 103 | onResume { } 104 | } 105 | ... 106 | ``` 107 | 108 | ### ManagerExt 109 | 110 | ```kotlin 111 | val connectivityManager 112 | val alarmManager 113 | val telephonyManager 114 | val activityManager 115 | ... 116 | ``` 117 | 118 | ### SharedPreferencesExt 119 | 120 | ```kotlin 121 | fun spSetInt(key: String, value: Int) 122 | fun spGetInt(key: String, defaultValue: Int = 0) 123 | ... 124 | ``` 125 | 126 | ### BitmapExt 127 | 128 | ```kotlin 129 | fun Bitmap.toBase64(): String 130 | fun Bitmap.resize(w: Number, h: Number): Bitmap 131 | ... 132 | ``` 133 | 134 | ### FileExt 135 | 136 | ```kotlin 137 | fun File.copy(dest: File) 138 | fun File.copyDirectory(dest: File) 139 | ... 140 | ``` 141 | 142 | ### ToastExt 143 | 144 | ```kotlin 145 | fun toast(msg: Any, isShort: Boolean = true) 146 | ``` 147 | 148 | ### ActivityExt 149 | 150 | ```kotlin 151 | fun Activity.goActivity() 152 | fun Activity.goActivity(requestCode: Int) 153 | ... 154 | ``` 155 | 156 | ### FragmentExt (support.v4) 157 | 158 | ```kotlin 159 | fun Fragment.goActivity() 160 | fun Fragment.goActivity(requestCode: Int) 161 | ... 162 | ``` 163 | 164 | ### ActivityMgr 165 | 166 | ```kotlin 167 | fun add(activity: Activity) 168 | fun remove(activity: Activity) 169 | fun removeAll() 170 | ... 171 | ``` 172 | 173 | ### ApiExt 174 | 175 | ```kotlin 176 | fun aboveApi(api: Int, included: Boolean = false, block: () -> Unit) 177 | fun belowApi(api: Int, included: Boolean = false, block: () -> Unit) 178 | ``` 179 | 180 | [[点这查看所有支持的扩展API(readmore)]](./DETAIL.md) 181 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.5.0' 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /ext/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ext/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | group = 'com.github.VictorChow' 5 | 6 | android { 7 | compileSdkVersion 28 8 | buildToolsVersion "28.0.3" 9 | 10 | defaultConfig { 11 | minSdkVersion 17 12 | targetSdkVersion 28 13 | } 14 | 15 | sourceSets { 16 | main.java.srcDirs += 'src/main/kotlin' 17 | } 18 | } 19 | 20 | dependencies { 21 | implementation fileTree(include: ['*.jar'], dir: 'libs') 22 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 23 | implementation 'androidx.appcompat:appcompat:1.0.2' 24 | } 25 | -------------------------------------------------------------------------------- /ext/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ActivityExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.app.Activity 4 | import android.app.Service 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.ServiceConnection 8 | import android.graphics.Color 9 | import android.graphics.drawable.ColorDrawable 10 | import android.os.Build 11 | import android.view.WindowManager 12 | import android.view.inputmethod.InputMethodManager 13 | import android.widget.EditText 14 | import androidx.appcompat.app.AppCompatActivity 15 | import androidx.fragment.app.Fragment 16 | 17 | /** 18 | * Created by Victor on 2017/9/12. (ง •̀_•́)ง 19 | */ 20 | 21 | inline fun Activity.goActivity() = startActivity(Intent(this, T::class.java)) 22 | 23 | inline fun Activity.goActivity(requestCode: Int) = startActivityForResult(Intent(this, T::class.java), requestCode) 24 | 25 | inline fun Activity.goActivity(vararg extras: Any?) { 26 | val intent = Intent(this, T::class.java) 27 | if (extras.size % 2 == 0) { 28 | (0 until extras.size step 2) 29 | .filter { extras[it + 1] != null } 30 | .forEach { IntentData.putExtra(intent, extras[it].toString(), extras[it + 1]!!) } 31 | } 32 | startActivity(intent) 33 | } 34 | 35 | inline fun Activity.goService() = startService(Intent(this, T::class.java)) 36 | 37 | inline fun Activity.goService(sc: ServiceConnection, flags: Int = Context.BIND_AUTO_CREATE) = bindService(Intent(this, T::class.java), sc, flags) 38 | 39 | fun Activity.hideInputMethod() = window.peekDecorView()?.let { inputMethodManager.hideSoftInputFromWindow(window.peekDecorView().windowToken, 0) } 40 | 41 | fun Activity.showInputMethod(v: EditText) { 42 | v.requestFocus() 43 | inputMethodManager.showSoftInput(v, InputMethodManager.SHOW_FORCED) 44 | } 45 | 46 | fun Activity.clearWindowBackground() = window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) 47 | 48 | fun Activity.steepStatusBar() { 49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 50 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 51 | window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) 52 | } 53 | } 54 | 55 | fun AppCompatActivity.addFragments(fragments: List, containerId: Int) { 56 | fragments.forEach { 57 | val ft = supportFragmentManager.beginTransaction() 58 | ft.add(containerId, it) 59 | ft.commitAllowingStateLoss() 60 | } 61 | } 62 | 63 | fun AppCompatActivity.showFragment(fragment: Fragment) { 64 | val ft = supportFragmentManager.beginTransaction() 65 | ft.show(fragment) 66 | ft.commitAllowingStateLoss() 67 | } 68 | 69 | fun AppCompatActivity.hideFragment(fragment: Fragment) { 70 | val ft = supportFragmentManager.beginTransaction() 71 | ft.hide(fragment) 72 | ft.commitAllowingStateLoss() 73 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ActivityMgr.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.app.Activity 4 | import kotlin.reflect.KClass 5 | 6 | /** 7 | * Created by victor on 16-6-14. (ง •̀_•́)ง 8 | */ 9 | 10 | object ActivityMgr { 11 | private val container = arrayListOf() 12 | 13 | @Synchronized 14 | fun add(activity: Activity) = container.add(activity) 15 | 16 | @Synchronized 17 | fun current(): Activity? = if (container.isEmpty()) null else container[container.lastIndex] 18 | 19 | @Synchronized 20 | fun findFirst(clazz: KClass): T? = container.firstOrNull { it::class == clazz } as? T 21 | 22 | @Synchronized 23 | fun findLast(clazz: KClass): T? = container.lastOrNull { it::class == clazz } as? T 24 | 25 | @Synchronized 26 | fun find(clazz: KClass): List = container.filter { it::class == clazz }.map { it as T } 27 | 28 | @Synchronized 29 | fun remove(activity: Activity) = container.remove(activity) 30 | 31 | @Synchronized 32 | fun removeAll() = container.clear() 33 | 34 | @Synchronized 35 | fun finishFirst(clazz: KClass<*>) = container.firstOrNull { it::class == clazz }?.let { finish(it) } 36 | 37 | @Synchronized 38 | fun finishLast(clazz: KClass<*>) = container.lastOrNull { it::class == clazz }?.let { finish(it) } 39 | 40 | @Synchronized 41 | fun finish(clazz: KClass<*>) = container 42 | .filter { it::class == clazz } 43 | .forEach { finish(it) } 44 | 45 | @Synchronized 46 | fun finishExcept(vararg clazz: KClass<*>) { 47 | if (clazz.isEmpty()) { 48 | finishAll() 49 | return 50 | } 51 | for (i in container.indices.reversed()) { 52 | val cur = container[i] 53 | val b = clazz.any { it == cur::class } 54 | if (!b) finish(cur) 55 | } 56 | } 57 | 58 | @Synchronized 59 | fun finishAll() { 60 | container.forEach { it.finish() } 61 | container.clear() 62 | } 63 | 64 | @Synchronized 65 | private fun finish(activity: Activity) { 66 | container.remove(activity) 67 | activity.finish() 68 | } 69 | 70 | @Synchronized 71 | fun activities(): List = container 72 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ApiExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.os.Build 4 | 5 | /** 6 | * Created by Victor on 2017/8/23. (ง •̀_•́)ง 7 | */ 8 | 9 | inline fun aboveApi(api: Int, included: Boolean = false, block: () -> Unit) { 10 | if (Build.VERSION.SDK_INT > if (included) api - 1 else api) { 11 | block() 12 | } 13 | } 14 | 15 | inline fun belowApi(api: Int, included: Boolean = false, block: () -> Unit) { 16 | if (Build.VERSION.SDK_INT < if (included) api + 1 else api) { 17 | block() 18 | } 19 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/BitmapExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Matrix 5 | import android.util.Base64 6 | import java.io.ByteArrayOutputStream 7 | import java.io.File 8 | import java.io.FileOutputStream 9 | 10 | 11 | /** 12 | * Created by Victor on 2017/8/29. (ง •̀_•́)ง 13 | */ 14 | 15 | fun Bitmap.toBase64(compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG): String { 16 | val result: String 17 | val baos = ByteArrayOutputStream() 18 | compress(compressFormat, 100, baos) 19 | baos.flush() 20 | baos.close() 21 | val bitmapBytes = baos.toByteArray() 22 | result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT) 23 | baos.flush() 24 | baos.close() 25 | return result 26 | } 27 | 28 | fun Bitmap.resize(w: Number, h: Number): Bitmap { 29 | val width = width 30 | val height = height 31 | val scaleWidth = w.toFloat() / width 32 | val scaleHeight = h.toFloat() / height 33 | val matrix = Matrix() 34 | matrix.postScale(scaleWidth, scaleHeight) 35 | if (width > 0 && height > 0) { 36 | return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) 37 | } 38 | return this 39 | } 40 | 41 | fun Bitmap.saveFile(path: String, compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG) { 42 | val f = File(path) 43 | if (!f.exists()) { 44 | f.createNewFile() 45 | } 46 | val stream = FileOutputStream(f) 47 | compress(compressFormat, 100, stream) 48 | stream.flush() 49 | stream.close() 50 | } 51 | 52 | fun Bitmap.toByteArray(compressFormat: Bitmap.CompressFormat = Bitmap.CompressFormat.PNG): ByteArray { 53 | val stream = ByteArrayOutputStream() 54 | compress(compressFormat, 100, stream) 55 | return stream.toByteArray() 56 | } 57 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ByteArrayExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.graphics.BitmapFactory 4 | import java.io.File 5 | import java.io.FileOutputStream 6 | 7 | /** 8 | * Created by Victor on 2017/10/11. (ง •̀_•́)ง 9 | */ 10 | 11 | fun ByteArray.saveFile(path: String) { 12 | val file = File(path) 13 | if (!file.exists()) { 14 | file.createNewFile() 15 | } 16 | val output = FileOutputStream(file) 17 | output.write(this) 18 | output.flush() 19 | output.close() 20 | } 21 | 22 | fun ByteArray.toBitmap(opts: BitmapFactory.Options? = null) = BitmapFactory.decodeByteArray(this, 0, size, opts) -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/CommonExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.res.ColorStateList 7 | import android.graphics.drawable.Drawable 8 | import android.net.Uri 9 | import android.os.Looper 10 | import androidx.core.content.ContextCompat 11 | import android.view.LayoutInflater 12 | import android.view.ViewGroup 13 | import androidx.annotation.ColorRes 14 | import androidx.annotation.DrawableRes 15 | import androidx.annotation.LayoutRes 16 | 17 | /** 18 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 19 | */ 20 | 21 | inline val app: Application 22 | get() = Ext.ctx 23 | 24 | inline val currentTimeMillis: Long 25 | get() = System.currentTimeMillis() 26 | 27 | fun findColor(@ColorRes resId: Int) = ContextCompat.getColor(app, resId) 28 | 29 | fun findDrawable(@DrawableRes resId: Int): Drawable? = ContextCompat.getDrawable(app, resId) 30 | 31 | fun findColorStateList(@ColorRes resId: Int): ColorStateList? = ContextCompat.getColorStateList(app, resId) 32 | 33 | fun inflate(@LayoutRes layoutId: Int, parent: ViewGroup?, attachToRoot: Boolean = false) = LayoutInflater.from(app).inflate(layoutId, parent, attachToRoot)!! 34 | 35 | fun inflate(@LayoutRes layoutId: Int) = inflate(layoutId, null) 36 | 37 | fun Context.dial(tel: String?) = startActivity(Intent(Intent.ACTION_DIAL, Uri.parse("tel:$tel"))) 38 | 39 | fun Context.sms(phone: String?, body: String = "") { 40 | val smsToUri = Uri.parse("smsto:$phone") 41 | val intent = Intent(Intent.ACTION_SENDTO, smsToUri) 42 | intent.putExtra("sms_body", body) 43 | startActivity(intent) 44 | } 45 | 46 | fun isMainThread(): Boolean = Looper.myLooper() == Looper.getMainLooper() 47 | 48 | infix fun Boolean.yes(trueValue: () -> T) = TernaryOperator(trueValue, this) 49 | 50 | class TernaryOperator(val trueValue: () -> T, val bool: Boolean) 51 | 52 | inline infix fun TernaryOperator.no(falseValue: () -> T) = if (bool) trueValue() else falseValue() 53 | 54 | fun Int.isOdd() = this and 0x1 == 1 55 | 56 | fun Int.isEven() = !this.isOdd() -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/DateTimeExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.* 5 | 6 | /** 7 | * Created by Victor on 2017/8/21. (ง •̀_•́)ง 8 | */ 9 | 10 | private enum class DateExpr { 11 | YEAR, MONTH, DAY, 12 | HOUR, MINUTE, SECOND, 13 | WEEK, DAY_YEAR, WEEK_YEAR, 14 | CONSTELLATION 15 | } 16 | 17 | fun Long.date(pattern: String = "yyyy-MM-dd HH:mm:ss"): String? = SimpleDateFormat(pattern, Locale.CHINA).format(this) 18 | 19 | fun Long.year() = getData(this, DateExpr.YEAR) 20 | 21 | fun Long.month() = getData(this, DateExpr.MONTH) 22 | 23 | fun Long.day() = getData(this, DateExpr.DAY) 24 | 25 | fun Long.week() = getData(this, DateExpr.WEEK) 26 | 27 | fun Long.hour() = getData(this, DateExpr.HOUR) 28 | 29 | fun Long.minute() = getData(this, DateExpr.MINUTE) 30 | 31 | fun Long.second() = getData(this, DateExpr.SECOND) 32 | 33 | fun Long.dayOfYear() = getData(this, DateExpr.DAY_YEAR) 34 | 35 | fun Long.weekOfYear() = getData(this, DateExpr.WEEK_YEAR) 36 | 37 | fun Long.constellation() = getData(this, DateExpr.CONSTELLATION) 38 | 39 | fun Long.dateOnly(split: String = "-") = "${year()}$split${month()}$split${day()}" 40 | 41 | fun Long.timeOnly(split: String = ":") = "${hour()}$split${minute()}$split${second()}" 42 | 43 | fun Int.isLeapYear() = (this % 4 == 0) && (this % 100 != 0) || (this % 400 == 0) 44 | 45 | private fun getData(timeMillis: Long, expr: DateExpr): String { 46 | val cal = Calendar.getInstance() 47 | cal.time = Date(timeMillis) 48 | return when (expr) { 49 | DateExpr.YEAR -> cal[Calendar.YEAR].toString() 50 | DateExpr.MONTH -> (cal[Calendar.MONTH] + 1).toString().prefix0() 51 | DateExpr.DAY -> cal[Calendar.DAY_OF_MONTH].toString().prefix0() 52 | DateExpr.WEEK -> { 53 | val week = arrayOf("未知", "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六") 54 | week[cal.get(Calendar.DAY_OF_WEEK)] 55 | } 56 | DateExpr.HOUR -> cal[Calendar.HOUR_OF_DAY].toString().prefix0() 57 | DateExpr.MINUTE -> cal[Calendar.MINUTE].toString().prefix0() 58 | DateExpr.SECOND -> cal[Calendar.SECOND].toString().prefix0() 59 | DateExpr.DAY_YEAR -> cal[Calendar.DAY_OF_YEAR].toString() 60 | DateExpr.WEEK_YEAR -> cal[Calendar.WEEK_OF_YEAR].toString() 61 | DateExpr.CONSTELLATION -> getConstellations(timeMillis.month().toInt(), timeMillis.day().toInt()) 62 | } 63 | } 64 | 65 | private fun getConstellations(month: Int, day: Int): String { 66 | val constellations = arrayOf(arrayOf("摩羯座", "水瓶座"), arrayOf("水瓶座", "双鱼座"), arrayOf("双鱼座", "白羊座"), arrayOf("白羊座", "金牛座"), arrayOf("金牛座", "双子座"), arrayOf("双子座", "巨蟹座"), arrayOf("巨蟹座", "狮子座"), arrayOf("狮子座", "处女座"), arrayOf("处女座", "天秤座"), arrayOf("天秤座", "天蝎座"), arrayOf("天蝎座", "射手座"), arrayOf("射手座", "摩羯座")) 67 | val dateSplit = intArrayOf(20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22) 68 | val split = dateSplit[month - 1] 69 | val constellation = constellations[month - 1] 70 | return if (day >= split) constellation[1] else constellation[0] 71 | } 72 | 73 | private fun String.prefix0() = if (length == 1) "0$this" else this -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/DisplayExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.content.Context 4 | import android.graphics.Point 5 | import android.util.DisplayMetrics 6 | import android.view.WindowManager 7 | import kotlin.DeprecationLevel.WARNING 8 | 9 | 10 | /** 11 | * Created by Victor on 2017/8/21. (ง •̀_•́)ง 12 | */ 13 | 14 | val displayMetrics = app.resources.displayMetrics!! 15 | 16 | inline val screenWidth: Int 17 | get() = app.resources.displayMetrics.widthPixels 18 | 19 | inline val screenHeight: Int 20 | get() = app.resources.displayMetrics.heightPixels 21 | 22 | inline val screenDensity: Float 23 | get() = app.resources.displayMetrics.density 24 | 25 | inline val scaledDensity: Float 26 | get() = app.resources.displayMetrics.scaledDensity 27 | 28 | fun getStatusBarHeight(): Int { 29 | var result = 0 30 | val resourceId = app.resources.getIdentifier("status_bar_height", "dimen", "android") 31 | if (resourceId > 0) { 32 | result = app.resources.getDimensionPixelSize(resourceId) 33 | } 34 | return result 35 | } 36 | 37 | fun getVirNavBarHeight(): Int { 38 | var height: Int 39 | val wm = (app.getSystemService(Context.WINDOW_SERVICE) as WindowManager) 40 | val display = wm.defaultDisplay 41 | val p = Point() 42 | display.getSize(p) 43 | val screenHeight = Math.max(p.y, p.x) 44 | val dm = DisplayMetrics() 45 | val c: Class<*> = Class.forName("android.view.Display") 46 | val method = c.getMethod("getRealMetrics", DisplayMetrics::class.java) 47 | method.invoke(display, dm) 48 | //横屏在右|竖屏在底 49 | height = Math.max(dm.heightPixels, dm.widthPixels) - screenHeight 50 | //横屏在底|竖屏在底 51 | if (height == 0) { 52 | height = Math.min(dm.heightPixels, dm.widthPixels) - Math.min(p.y, p.x) 53 | } 54 | return height 55 | } 56 | 57 | //ReplaceWith()目前不支持将方法转换字段,所以需要手动修改 58 | @Deprecated("使用扩展属性更加直观, dp(360) -> 360.dp",level = WARNING) 59 | fun dp2px(dp: Number) = (dp.toFloat() * displayMetrics.density + 0.5f).toInt() 60 | 61 | @Deprecated("使用扩展属性更加直观, sp(360) -> 360.sp",level = WARNING) 62 | fun sp2px(sp: Number) = (sp.toFloat() * displayMetrics.scaledDensity + 0.5f).toInt() 63 | 64 | @Deprecated("使用扩展属性更加直观, px2dp(360) -> 360.px2dp",level = WARNING) 65 | fun px2dp(px: Number) = (px.toFloat() / displayMetrics.density + 0.5f).toInt() 66 | 67 | @Deprecated("使用扩展属性更加直观, px2sp(360) -> 360.px2sp",level = WARNING) 68 | fun px2sp(px: Number) = (px.toFloat() / displayMetrics.scaledDensity + 0.5f).toInt() 69 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/Ext.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.app.Application 4 | 5 | /** 6 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 7 | */ 8 | object Ext { 9 | lateinit var ctx: Application 10 | 11 | fun with(app: Application) { 12 | this.ctx = app 13 | } 14 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/FileExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import java.io.ByteArrayOutputStream 4 | import java.io.File 5 | import java.io.FileInputStream 6 | import java.io.FileOutputStream 7 | import java.nio.channels.FileChannel 8 | import java.security.MessageDigest 9 | 10 | 11 | /** 12 | * Created by Victor on 2017/9/21. (ง •̀_•́)ง 13 | */ 14 | 15 | fun File.copy(dest: File) { 16 | var fi: FileInputStream? = null 17 | var fo: FileOutputStream? = null 18 | var ic: FileChannel? = null 19 | var oc: FileChannel? = null 20 | try { 21 | if (!dest.exists()) { 22 | dest.createNewFile() 23 | } 24 | fi = FileInputStream(this) 25 | fo = FileOutputStream(dest) 26 | ic = fi.channel 27 | oc = fo.channel 28 | ic.transferTo(0, ic.size(), oc) 29 | } catch (e: Exception) { 30 | e.printStackTrace() 31 | } finally { 32 | fi?.close() 33 | fo?.close() 34 | ic?.close() 35 | oc?.close() 36 | } 37 | } 38 | 39 | fun File.move(dest: File) { 40 | copy(dest) 41 | delete() 42 | } 43 | 44 | fun File.copyDirectory(dest: File) { 45 | if (!dest.exists()) { 46 | dest.mkdirs() 47 | } 48 | val files = listFiles() 49 | files?.forEach { 50 | if (it.isFile) { 51 | it.copy(File("${dest.absolutePath}/${it.name}")) 52 | } 53 | if (it.isDirectory) { 54 | val dirSrc = File("$absolutePath/${it.name}") 55 | val dirDest = File("${dest.absolutePath}/${it.name}") 56 | dirSrc.copyDirectory(dirDest) 57 | } 58 | } 59 | } 60 | 61 | fun File.moveDirectory(dest: File) { 62 | copyDirectory(dest) 63 | deleteAll() 64 | } 65 | 66 | fun File.deleteAll() { 67 | if (isFile && exists()) { 68 | delete() 69 | return 70 | } 71 | if (isDirectory) { 72 | val files = listFiles() 73 | if (files == null || files.isEmpty()) { 74 | delete() 75 | return 76 | } 77 | files.forEach { it.deleteAll() } 78 | delete() 79 | } 80 | } 81 | 82 | fun File.md5(): String? { 83 | if (!this.isFile) { 84 | return null 85 | } 86 | return encryptFile(this, "MD5") 87 | } 88 | 89 | fun File.sha1(): String? { 90 | if (!this.isFile) { 91 | return null 92 | } 93 | return encryptFile(this, "SHA-1") 94 | } 95 | 96 | private fun encryptFile(file: File, type: String): String { 97 | val digest: MessageDigest = MessageDigest.getInstance(type) 98 | val input = FileInputStream(file) 99 | val buffer = ByteArray(1024) 100 | var len = input.read(buffer, 0, 1024) 101 | while (len != -1) { 102 | digest.update(buffer, 0, len) 103 | len = input.read(buffer, 0, 1024) 104 | } 105 | input.close() 106 | return bytes2Hex(digest.digest()) 107 | } 108 | 109 | fun File.toByteArray(): ByteArray { 110 | val bos = ByteArrayOutputStream(this.length().toInt()) 111 | val input = FileInputStream(this) 112 | val size = 1024 113 | val buffer = ByteArray(size) 114 | var len = input.read(buffer, 0, size) 115 | while (len != -1) { 116 | bos.write(buffer, 0, len) 117 | len = input.read(buffer, 0, size) 118 | } 119 | input.close() 120 | bos.close() 121 | return bos.toByteArray() 122 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/FragmentExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.app.Activity 4 | import android.app.Service 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.ServiceConnection 8 | import android.view.inputmethod.InputMethodManager 9 | import android.widget.EditText 10 | import androidx.fragment.app.Fragment 11 | 12 | /** 13 | * Created by Victor on 2017/9/12. (ง •̀_•́)ง 14 | */ 15 | 16 | inline fun Fragment.goActivity() = startActivity(Intent(activity, T::class.java)) 17 | 18 | inline fun Fragment.goActivity(requestCode: Int) = startActivityForResult(Intent(activity, T::class.java), requestCode) 19 | 20 | inline fun Fragment.goService() = activity?.startService(Intent(activity, T::class.java)) 21 | 22 | inline fun Fragment.goService(sc: ServiceConnection, flags: Int = Context.BIND_AUTO_CREATE) = activity?.bindService(Intent(activity, T::class.java), sc, flags) 23 | 24 | fun Fragment.hideInputMethod() { 25 | if (activity != null && activity!!.window.peekDecorView() != null) { 26 | inputMethodManager.hideSoftInputFromWindow(activity!!.window.peekDecorView().windowToken, 0) 27 | } 28 | } 29 | 30 | fun Fragment.showInputMethod(v: EditText) { 31 | v.requestFocus() 32 | inputMethodManager.showSoftInput(v, InputMethodManager.SHOW_FORCED) 33 | } 34 | 35 | fun Fragment.finish() = activity?.finish() 36 | 37 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/IntentData.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.os.Parcelable 6 | import java.io.Serializable 7 | 8 | /** 9 | * Created by victor on 16-8-12. (ง •̀_•́)ง 10 | */ 11 | @Suppress("UNCHECKED_CAST") 12 | object IntentData { 13 | fun putExtra(intent: Intent, key: String, value: Any) { 14 | when (value) { 15 | is Boolean -> intent.putExtra(key, value) 16 | is Byte -> intent.putExtra(key, value) 17 | is Char -> intent.putExtra(key, value) 18 | is Short -> intent.putExtra(key, value) 19 | is Int -> intent.putExtra(key, value) 20 | is Long -> intent.putExtra(key, value) 21 | is Float -> intent.putExtra(key, value) 22 | is Double -> intent.putExtra(key, value) 23 | is String -> intent.putExtra(key, value) 24 | is CharSequence -> intent.putExtra(key, value) 25 | is Parcelable -> intent.putExtra(key, value) 26 | is Serializable -> intent.putExtra(key, value) 27 | is BooleanArray -> intent.putExtra(key, value) 28 | is ByteArray -> intent.putExtra(key, value) 29 | is ShortArray -> intent.putExtra(key, value) 30 | is CharArray -> intent.putExtra(key, value) 31 | is LongArray -> intent.putExtra(key, value) 32 | is DoubleArray -> intent.putExtra(key, value) 33 | is FloatArray -> intent.putExtra(key, value) 34 | is IntArray -> intent.putExtra(key, value) 35 | is Bundle -> intent.putExtra(key, value) 36 | is Array<*> -> { 37 | if (value.isNotEmpty() && value[0] != null) { 38 | when (value[0]) { 39 | is Parcelable -> intent.putExtra(key, value as Array) 40 | is CharSequence -> intent.putExtra(key, value as Array) 41 | is String -> intent.putExtra(key, value as Array) 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ListenerExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.animation.Animator 4 | import android.os.Build 5 | import android.text.Editable 6 | import android.text.TextWatcher 7 | import android.widget.TextView 8 | import androidx.viewpager.widget.ViewPager 9 | 10 | /** 11 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 12 | */ 13 | 14 | fun Animator.addListener(init: AnimatorListenerWrapper.() -> Unit) { 15 | val wrapper = AnimatorListenerWrapper().apply { init() } 16 | addListener(object : Animator.AnimatorListener { 17 | override fun onAnimationRepeat(p0: Animator?) { 18 | wrapper.onRepeat?.invoke() 19 | } 20 | 21 | override fun onAnimationEnd(p0: Animator?) { 22 | wrapper.onEnd?.invoke() 23 | } 24 | 25 | override fun onAnimationCancel(p0: Animator?) { 26 | wrapper.onCancel?.invoke() 27 | } 28 | 29 | override fun onAnimationStart(p0: Animator?) { 30 | wrapper.onStart?.invoke() 31 | } 32 | 33 | }) 34 | } 35 | 36 | fun Animator.addPauseListener(init: AnimatorPauseListenerWrapper.() -> Unit) { 37 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 38 | val wrapper = AnimatorPauseListenerWrapper().apply { init() } 39 | addPauseListener(object : Animator.AnimatorPauseListener { 40 | override fun onAnimationPause(p0: Animator?) { 41 | wrapper.onPause?.invoke() 42 | } 43 | 44 | override fun onAnimationResume(p0: Animator?) { 45 | wrapper.onResume?.invoke() 46 | } 47 | }) 48 | } 49 | } 50 | 51 | fun TextView.addTextChangedListener(init: TextWatcherWrapper.() -> Unit) { 52 | val wrapper = TextWatcherWrapper().apply { init() } 53 | addTextChangedListener(object : TextWatcher { 54 | override fun afterTextChanged(p0: Editable) { 55 | wrapper.after?.invoke(p0) 56 | } 57 | 58 | override fun beforeTextChanged(p0: CharSequence, p1: Int, p2: Int, p3: Int) { 59 | wrapper.before?.invoke(p0, p1, p2, p3) 60 | } 61 | 62 | override fun onTextChanged(p0: CharSequence, p1: Int, p2: Int, p3: Int) { 63 | wrapper.on?.invoke(p0, p1, p2, p3) 64 | } 65 | 66 | }) 67 | } 68 | 69 | fun ViewPager.addOnPageChangeListener(init: OnPageChangeListenerWrapper.() -> Unit) { 70 | val wrapper = OnPageChangeListenerWrapper().apply { init() } 71 | addOnPageChangeListener(object : ViewPager.OnPageChangeListener { 72 | override fun onPageScrollStateChanged(state: Int) { 73 | wrapper.onPageScrollStateChanged?.invoke(state) 74 | } 75 | 76 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { 77 | wrapper.onPageScrolled?.invoke(position, positionOffset, positionOffsetPixels) 78 | } 79 | 80 | override fun onPageSelected(position: Int) { 81 | wrapper.onPageSelected?.invoke(position) 82 | } 83 | 84 | }) 85 | } -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ManagerExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.app.ActivityManager 4 | import android.app.AlarmManager 5 | import android.app.NotificationManager 6 | import android.appwidget.AppWidgetManager 7 | import android.bluetooth.BluetoothManager 8 | import android.content.ClipboardManager 9 | import android.content.Context 10 | import android.hardware.camera2.CameraManager 11 | import android.media.AudioManager 12 | import android.net.ConnectivityManager 13 | import android.os.BatteryManager 14 | import android.os.Build 15 | import android.os.Vibrator 16 | import android.telephony.TelephonyManager 17 | import android.view.inputmethod.InputMethodManager 18 | import androidx.annotation.RequiresApi 19 | 20 | /** 21 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 22 | */ 23 | 24 | inline val connectivityManager: ConnectivityManager 25 | get() = app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 26 | 27 | inline val alarmManager: AlarmManager 28 | get() = app.getSystemService(Context.ALARM_SERVICE) as AlarmManager 29 | 30 | inline val telephonyManager: TelephonyManager 31 | get() = app.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager 32 | 33 | inline val activityManager: ActivityManager 34 | get() = app.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager 35 | 36 | inline val notificationManager: NotificationManager 37 | get() = app.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 38 | 39 | inline val appWidgetManager 40 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 41 | get() = app.getSystemService(Context.APPWIDGET_SERVICE) as AppWidgetManager 42 | 43 | inline val inputMethodManager: InputMethodManager 44 | get() = app.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 45 | 46 | inline val clipboardManager 47 | get() = app.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 48 | 49 | inline val bluetoothManager: BluetoothManager 50 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2) 51 | get() = app.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager 52 | 53 | inline val audioManager 54 | get() = app.getSystemService(Context.AUDIO_SERVICE) as AudioManager 55 | 56 | inline val batteryManager 57 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 58 | get() = app.getSystemService(Context.BATTERY_SERVICE) as BatteryManager 59 | 60 | inline val cameraManager 61 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 62 | get() = app.getSystemService(Context.CAMERA_SERVICE) as CameraManager 63 | 64 | inline val vibrator 65 | get() = app.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/PreferencesExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.content.SharedPreferences 4 | import android.preference.PreferenceManager 5 | 6 | /** 7 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 8 | */ 9 | 10 | private inline val sp: SharedPreferences 11 | get() = PreferenceManager.getDefaultSharedPreferences(app) 12 | 13 | fun spSetInt(key: String, value: Int) = sp.edit().putInt(key, value).apply() 14 | 15 | fun spGetInt(key: String, defaultValue: Int = 0) = sp.getInt(key, defaultValue) 16 | 17 | fun spSetLong(key: String, value: Long) = sp.edit().putLong(key, value).apply() 18 | 19 | fun spGetLong(key: String, defaultValue: Long = 0L) = sp.getLong(key, defaultValue) 20 | 21 | fun spSetFloat(key: String, value: Float) = sp.edit().putFloat(key, value).apply() 22 | 23 | fun spGetFloat(key: String, defaultValue: Float = 0f) = sp.getFloat(key, defaultValue) 24 | 25 | fun spSetBoolean(key: String, value: Boolean) = sp.edit().putBoolean(key, value).apply() 26 | 27 | fun spGetBoolean(key: String, defaultValue: Boolean = false) = sp.getBoolean(key, defaultValue) 28 | 29 | fun spSetString(key: String, value: String) = sp.edit().putString(key, value).apply() 30 | 31 | fun spGetString(key: String, defaultValue: String = "") = sp.getString(key, defaultValue)!! 32 | 33 | fun spRemove(key: String) = sp.edit().remove(key).apply() 34 | 35 | fun spClearAll() = sp.edit().clear().apply() -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/SizeConversionExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.content.res.Resources 4 | import android.util.TypedValue 5 | import android.util.TypedValue.COMPLEX_UNIT_DIP 6 | import android.util.TypedValue.COMPLEX_UNIT_SP 7 | 8 | private val metrics = Resources.getSystem().displayMetrics 9 | 10 | /** 11 | * 正常编码中一般只会用到 [dp]/[sp] 和 [px] ; 12 | * 其中[dp]/[sp] 会根据系统分辨率将输入的dp/sp值转换为对应的px 13 | * 而[px]只是返回自身,目的是表明自己是px值 14 | */ 15 | val Float.dp: Float // [xxhdpi](360 -> 1080) 16 | get() = TypedValue.applyDimension(COMPLEX_UNIT_DIP, this, metrics) 17 | 18 | val Int.dp: Int // [xxhdpi](360 -> 1080) 19 | get() = TypedValue.applyDimension(COMPLEX_UNIT_DIP, this.toFloat(), metrics).toInt() 20 | 21 | val Float.sp: Float // [xxhdpi](360 -> 1080) 22 | get() = TypedValue.applyDimension(COMPLEX_UNIT_SP, this, metrics) 23 | 24 | val Int.sp: Int // [xxhdpi](360 -> 1080) 25 | get() = TypedValue.applyDimension(COMPLEX_UNIT_SP, this.toFloat(), metrics).toInt() 26 | 27 | val Number.px: Number // [xxhdpi](360 -> 360) 28 | get() = this 29 | 30 | /** 31 | * 在(可能存在的?)某些特殊情况会需要将px值转换为对应的dp/sp 32 | * 对应方法[Number.px2dp]/[Number.px2sp] 33 | */ 34 | val Number.px2dp: Int // [xxhdpi](360 -> 120) 35 | get() = (this.toFloat() / metrics.density).toInt() 36 | 37 | val Number.px2sp: Int // [xxhdpi](360 -> 120) 38 | get() = (this.toFloat() / metrics.scaledDensity).toInt() -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/StringExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import java.security.MessageDigest 4 | 5 | 6 | /** 7 | * Created by Victor on 2017/8/21. (ง •̀_•́)ง 8 | */ 9 | 10 | fun String.toast(isShortToast: Boolean = true) = toast(this, isShortToast) 11 | 12 | fun String.md5() = encrypt(this, "MD5") 13 | 14 | fun String.sha1() = encrypt(this, "SHA-1") 15 | 16 | fun String.isIdcard(): Boolean { 17 | val p18 = "^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]\$".toRegex() 18 | val p15 = "^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{2}[0-9Xx]\$".toRegex() 19 | return matches(p18) || matches(p15) 20 | } 21 | 22 | fun String.isPhone(): Boolean { 23 | val p = "^1([34578])\\d{9}\$".toRegex() 24 | return matches(p) 25 | } 26 | 27 | fun String.isEmail(): Boolean { 28 | val p = "^(\\w)+(\\.\\w+)*@(\\w)+((\\.\\w+)+)\$".toRegex() 29 | return matches(p) 30 | } 31 | 32 | fun String.isNumeric(): Boolean { 33 | val p = "^[0-9]+$".toRegex() 34 | return matches(p) 35 | } 36 | 37 | fun String.equalsIgnoreCase(other: String) = this.toLowerCase().contentEquals(other.toLowerCase()) 38 | 39 | private fun encrypt(string: String?, type: String): String { 40 | val bytes = MessageDigest.getInstance(type).digest(string!!.toByteArray()) 41 | return bytes2Hex(bytes) 42 | } 43 | 44 | internal fun bytes2Hex(bts: ByteArray): String { 45 | var des = "" 46 | var tmp: String 47 | for (i in bts.indices) { 48 | tmp = Integer.toHexString(bts[i].toInt() and 0xFF) 49 | if (tmp.length == 1) { 50 | des += "0" 51 | } 52 | des += tmp 53 | } 54 | return des 55 | } 56 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ToastExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.annotation.SuppressLint 4 | import android.widget.Toast 5 | 6 | /** 7 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 8 | */ 9 | 10 | private var toast: Toast? = null 11 | 12 | @SuppressLint("ShowToast") 13 | fun toast(msg: Any?, isShort: Boolean = true) { 14 | msg?.let { 15 | if (toast == null) { 16 | toast = Toast.makeText(app, msg.toString(), Toast.LENGTH_SHORT) 17 | } else { 18 | toast!!.setText(msg.toString()) 19 | } 20 | toast!!.duration = if (isShort) Toast.LENGTH_SHORT else Toast.LENGTH_LONG 21 | toast!!.show() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/ViewExt.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.animation.ValueAnimator 4 | import android.graphics.Bitmap 5 | import android.graphics.Canvas 6 | import android.graphics.Paint 7 | import android.text.method.PasswordTransformationMethod 8 | import android.text.method.ReplacementTransformationMethod 9 | import android.view.View 10 | import android.view.ViewGroup 11 | import android.view.animation.AccelerateDecelerateInterpolator 12 | import android.view.animation.Interpolator 13 | import android.widget.EditText 14 | import android.widget.RadioButton 15 | import android.widget.RadioGroup 16 | import android.widget.TextView 17 | 18 | 19 | /** 20 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 21 | */ 22 | 23 | private const val duration = 750L 24 | private inline val interpolator: Interpolator 25 | get() = AccelerateDecelerateInterpolator() 26 | 27 | fun View.setPaddingLeft(value: Int) = setPadding(value, paddingTop, paddingRight, paddingBottom) 28 | 29 | fun View.setPaddingRight(value: Int) = setPadding(paddingLeft, paddingTop, value, paddingBottom) 30 | 31 | fun View.setPaddingTop(value: Int) = setPaddingRelative(paddingStart, value, paddingEnd, paddingBottom) 32 | 33 | fun View.setPaddingBottom(value: Int) = setPaddingRelative(paddingStart, paddingTop, paddingEnd, value) 34 | 35 | fun View.setPaddingStart(value: Int) = setPaddingRelative(value, paddingTop, paddingEnd, paddingBottom) 36 | 37 | fun View.setPaddingEnd(value: Int) = setPaddingRelative(paddingStart, paddingTop, value, paddingBottom) 38 | 39 | fun View.setPaddingHorizontal(value: Int) = setPaddingRelative(value, paddingTop, value, paddingBottom) 40 | 41 | fun View.setPaddingVertical(value: Int) = setPaddingRelative(paddingStart, value, paddingEnd, value) 42 | 43 | fun View.setHeight(value: Int) { 44 | val lp = layoutParams 45 | lp?.let { 46 | lp.height = value 47 | layoutParams = lp 48 | } 49 | } 50 | 51 | fun View.setWidth(value: Int) { 52 | val lp = layoutParams 53 | lp?.let { 54 | lp.width = value 55 | layoutParams = lp 56 | } 57 | } 58 | 59 | fun View.resize(width: Int, height: Int) { 60 | val lp = layoutParams 61 | lp?.let { 62 | lp.width = width 63 | lp.height = height 64 | layoutParams = lp 65 | } 66 | } 67 | 68 | inline val ViewGroup.children: List 69 | get() = (0 until childCount).map { getChildAt(it) } 70 | 71 | operator fun ViewGroup.get(index: Int): View { 72 | return getChildAt(index) 73 | } 74 | 75 | fun View.animateWidth(toValue: Int, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator): AnimatePropsWrapper { 76 | if (toValue == width || layoutParams == null) { 77 | return AnimatePropsWrapper(null) 78 | } 79 | return AnimatePropsWrapper(ValueAnimator().apply { 80 | setIntValues(width, toValue) 81 | setDuration(duration) 82 | setInterpolator(interpolator) 83 | addUpdateListener { 84 | val lp = layoutParams 85 | lp.width = it.animatedValue as Int 86 | layoutParams = lp 87 | } 88 | start() 89 | }) 90 | } 91 | 92 | fun View.animateWidthBy(byValue: Int, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator) = animateWidth(width + byValue, duration, interpolator) 93 | 94 | fun View.animateHeight(toValue: Int, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator): AnimatePropsWrapper { 95 | if (toValue == height || layoutParams == null) { 96 | return AnimatePropsWrapper(null) 97 | } 98 | return AnimatePropsWrapper(ValueAnimator().apply { 99 | setIntValues(height, toValue) 100 | setDuration(duration) 101 | setInterpolator(interpolator) 102 | addUpdateListener { 103 | val lp = layoutParams 104 | lp.height = it.animatedValue as Int 105 | layoutParams = lp 106 | } 107 | start() 108 | }) 109 | } 110 | 111 | fun View.animateHeightBy(byValue: Int, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator) = animateHeight(height + byValue, duration, interpolator) 112 | 113 | fun TextView.underLine() { 114 | paint.flags = paint.flags or Paint.UNDERLINE_TEXT_FLAG 115 | paint.isAntiAlias = true 116 | } 117 | 118 | fun TextView.deleteLine() { 119 | paint.flags = paint.flags or Paint.STRIKE_THRU_TEXT_FLAG 120 | paint.isAntiAlias = true 121 | } 122 | 123 | fun TextView.bold(isBold: Boolean = true) { 124 | paint.isFakeBoldText = isBold 125 | paint.isAntiAlias = true 126 | } 127 | 128 | fun T.click(block: (T) -> Unit) = setOnClickListener { block(it as T) } 129 | 130 | fun T.longClick(block: (T) -> Boolean) = setOnLongClickListener { block(it as T) } 131 | 132 | fun View.visiable() { 133 | if (visibility != View.VISIBLE) { 134 | visibility = View.VISIBLE 135 | } 136 | } 137 | 138 | inline fun View.visiableIf(block: () -> Boolean) { 139 | if (visibility != View.VISIBLE && block()) { 140 | visibility = View.VISIBLE 141 | } 142 | } 143 | 144 | fun View.invisiable() { 145 | if (visibility != View.INVISIBLE) { 146 | visibility = View.INVISIBLE 147 | } 148 | } 149 | 150 | inline fun View.invisiableIf(block: () -> Boolean) { 151 | if (visibility != View.INVISIBLE && block()) { 152 | visibility = View.INVISIBLE 153 | } 154 | } 155 | 156 | fun View.gone() { 157 | if (visibility != View.GONE) { 158 | visibility = View.GONE 159 | } 160 | } 161 | 162 | inline fun View.goneIf(block: () -> Boolean) { 163 | if (visibility != View.GONE && block()) { 164 | visibility = View.GONE 165 | } 166 | } 167 | 168 | fun View.animateX(toValue: Float, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator): AnimatePropsWrapper { 169 | if (toValue == translationX) { 170 | return AnimatePropsWrapper(null) 171 | } 172 | return AnimatePropsWrapper(ValueAnimator().apply { 173 | setFloatValues(translationX, toValue) 174 | setDuration(duration) 175 | setInterpolator(interpolator) 176 | addUpdateListener { this@animateX.translationX = it.animatedValue as Float } 177 | start() 178 | }) 179 | } 180 | 181 | fun View.animateXBy(toValue: Float, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator) = animateX(translationX + toValue, duration, interpolator) 182 | 183 | fun View.animateY(toValue: Float, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator): AnimatePropsWrapper { 184 | if (toValue == translationY) { 185 | return AnimatePropsWrapper(null) 186 | } 187 | return AnimatePropsWrapper(ValueAnimator().apply { 188 | setFloatValues(translationY, toValue) 189 | setDuration(duration) 190 | setInterpolator(interpolator) 191 | addUpdateListener { this@animateY.translationY = it.animatedValue as Float } 192 | start() 193 | }) 194 | } 195 | 196 | fun View.animateYBy(toValue: Float, duration: Long = pers.victor.ext.duration, interpolator: Interpolator = pers.victor.ext.interpolator) = animateY(translationY + toValue, duration, interpolator) 197 | 198 | var EditText.value: String 199 | get() = text.toString() 200 | set(value) = setText(value) 201 | 202 | fun View.getBitmap(): Bitmap { 203 | val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) 204 | val canvas = Canvas(bmp) 205 | draw(canvas) 206 | canvas.save() 207 | return bmp 208 | } 209 | 210 | fun EditText.uppercase() { 211 | transformationMethod = object : ReplacementTransformationMethod() { 212 | private val lower = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') 213 | private val upper = charArrayOf('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z') 214 | 215 | override fun getOriginal() = lower 216 | 217 | override fun getReplacement() = upper 218 | } 219 | } 220 | 221 | fun EditText.lowercase() { 222 | transformationMethod = object : ReplacementTransformationMethod() { 223 | private val lower = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') 224 | private val upper = charArrayOf('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z') 225 | 226 | override fun getOriginal() = upper 227 | 228 | override fun getReplacement() = lower 229 | } 230 | } 231 | 232 | fun EditText.passwordToggledVisible() { 233 | val selection = selectionStart 234 | transformationMethod = if (transformationMethod == null) PasswordTransformationMethod() else null 235 | setSelection(selection) 236 | } 237 | 238 | fun View.isVisible() = visibility == View.VISIBLE 239 | 240 | fun View.isGone() = visibility == View.GONE 241 | 242 | fun View.isInvisible() = visibility == View.INVISIBLE 243 | 244 | var RadioGroup.checkedIndex: Int 245 | get() = (0 until childCount).firstOrNull { (getChildAt(it) as RadioButton).isChecked } ?: -1 246 | set(value) { 247 | if (value !in 0 until childCount) { 248 | children.map { it as RadioButton }.filter { it.isChecked }.forEach { it.isChecked = false } 249 | } else { 250 | (getChildAt(value) as RadioButton).isChecked = true 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /ext/src/main/kotlin/pers/victor/ext/Wrappers.kt: -------------------------------------------------------------------------------- 1 | package pers.victor.ext 2 | 3 | import android.animation.Animator 4 | import android.text.Editable 5 | 6 | /** 7 | * Created by Victor on 2017/8/18. (ง •̀_•́)ง 8 | */ 9 | 10 | class AnimatorListenerWrapper { 11 | internal var onStart: (() -> Unit)? = null 12 | internal var onEnd: (() -> Unit)? = null 13 | internal var onCancel: (() -> Unit)? = null 14 | internal var onRepeat: (() -> Unit)? = null 15 | 16 | fun onStart(block: (() -> Unit)) { 17 | onStart = block 18 | } 19 | 20 | fun onEnd(block: (() -> Unit)) { 21 | onEnd = block 22 | } 23 | 24 | fun onCancel(block: (() -> Unit)) { 25 | onCancel = block 26 | } 27 | 28 | fun onRepeat(block: (() -> Unit)) { 29 | onRepeat = block 30 | } 31 | } 32 | 33 | class AnimatorPauseListenerWrapper { 34 | internal var onPause: (() -> Unit)? = null 35 | internal var onResume: (() -> Unit)? = null 36 | 37 | fun onPause(block: (() -> Unit)) { 38 | onPause = block 39 | } 40 | 41 | fun onResume(block: (() -> Unit)) { 42 | onResume = block 43 | } 44 | } 45 | 46 | class TextWatcherWrapper { 47 | internal var after: ((Editable) -> Unit)? = null 48 | internal var before: ((CharSequence, Int, Int, Int) -> Unit)? = null 49 | internal var on: ((CharSequence, Int, Int, Int) -> Unit)? = null 50 | 51 | fun after(block: (Editable) -> Unit) { 52 | after = block 53 | } 54 | 55 | fun before(block: (s: CharSequence, start: Int, count: Int, after: Int) -> Unit) { 56 | before = block 57 | } 58 | 59 | fun on(block: (s: CharSequence, start: Int, before: Int, count: Int) -> Unit) { 60 | on = block 61 | } 62 | } 63 | 64 | class OnPageChangeListenerWrapper { 65 | internal var onPageScrolled: ((Int, Float, Int) -> Unit)? = null 66 | internal var onPageSelected: ((Int) -> Unit)? = null 67 | internal var onPageScrollStateChanged: ((Int) -> Unit)? = null 68 | 69 | fun onPageScrolled(block: (position: Int, positionOffset: Float, positionOffsetPixels: Int) -> Unit) { 70 | onPageScrolled = block 71 | } 72 | 73 | fun onPageSelected(block: (Int) -> Unit) { 74 | onPageSelected = block 75 | } 76 | 77 | fun onPageScrollStateChanged(block: (Int) -> Unit) { 78 | onPageScrollStateChanged = block 79 | } 80 | } 81 | 82 | class AnimatePropsWrapper(private val animator: Animator?) { 83 | 84 | fun onEnd(block: () -> Unit) { 85 | if (animator == null) { 86 | block() 87 | } else { 88 | animator.addListener { onEnd { block() } } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /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 | 13 | # When configured, Gradle will run in incubating parallel mode. 14 | # This option should only be used with decoupled projects. More details, visit 15 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 16 | # org.gradle.parallel=true 17 | android.useAndroidX=true 18 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VictorChow/kotlin-android-lib/01e56eb107f0eac6880420dc6377c6dfdf367b72/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Aug 17 16:58:17 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':ext' 2 | --------------------------------------------------------------------------------