191 lines
5.1 KiB
Kotlin
191 lines
5.1 KiB
Kotlin
package com.gh.common.util
|
||
|
||
import android.content.ClipboardManager
|
||
import android.content.Context
|
||
import android.text.Html
|
||
import android.text.Spanned
|
||
import android.view.View
|
||
import androidx.fragment.app.Fragment
|
||
import androidx.fragment.app.FragmentActivity
|
||
import androidx.lifecycle.*
|
||
import androidx.viewpager.widget.ViewPager
|
||
import com.google.gson.reflect.TypeToken
|
||
import com.halo.assistant.HaloApp
|
||
import com.lightgame.utils.Utils
|
||
import okhttp3.MediaType
|
||
import okhttp3.RequestBody
|
||
import java.net.URI
|
||
|
||
/**
|
||
* 创建以 activity 为观察者上下文的 viewModel
|
||
*/
|
||
inline fun <reified VM : ViewModel> FragmentActivity.viewModelProvider(
|
||
provider: ViewModelProvider.Factory? = null
|
||
) =
|
||
ViewModelProviders.of(this, provider).get(VM::class.java)
|
||
|
||
/**
|
||
* 创建以 activity 为观察者上下文的 viewModel
|
||
*/
|
||
inline fun <reified VM : ViewModel> Fragment.viewModelProviderFromParent(
|
||
provider: ViewModelProvider.Factory? = null
|
||
) =
|
||
ViewModelProviders.of(requireActivity(), provider).get(VM::class.java)
|
||
|
||
/**
|
||
* 创建以 fragment 为观察者上下文的 viewModel
|
||
*/
|
||
inline fun <reified VM : ViewModel> Fragment.viewModelProvider(
|
||
provider: ViewModelProvider.Factory? = null
|
||
) =
|
||
ViewModelProviders.of(this, provider).get(VM::class.java)
|
||
|
||
/**
|
||
*
|
||
* ViewPager Extensions
|
||
*
|
||
*/
|
||
fun ViewPager.doOnPageSelected(action: (position: Int) -> Unit) = addOnPageChangeListener(onSelected = action)
|
||
|
||
fun ViewPager.addOnPageChangeListener(onSelected: ((position: Int) -> Unit)? = null) {
|
||
val listener = object : ViewPager.OnPageChangeListener {
|
||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||
// Do nothing.
|
||
}
|
||
|
||
override fun onPageSelected(position: Int) {
|
||
onSelected?.invoke(position)
|
||
}
|
||
|
||
override fun onPageScrollStateChanged(state: Int) {
|
||
// Do nothing.
|
||
}
|
||
}
|
||
addOnPageChangeListener(listener)
|
||
}
|
||
|
||
/**
|
||
* LiveData Extensions
|
||
*/
|
||
fun <T> LiveData<T?>.observeNonNull(owner: LifecycleOwner, callback: (T) -> Unit) {
|
||
observe(owner, Observer { value ->
|
||
if (value != null) {
|
||
callback(value)
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* Login related extensions
|
||
*/
|
||
fun Fragment.ifLogin(entrance: String, action: (() -> Unit)? = null) {
|
||
requireContext().ifLogin(entrance, action)
|
||
}
|
||
|
||
fun Context.ifLogin(entrance: String, action: (() -> Unit)? = null) {
|
||
CheckLoginUtils.checkLogin(this, entrance, action)
|
||
}
|
||
|
||
|
||
/**
|
||
* Gson related extensions.
|
||
*/
|
||
inline fun <reified T : Any> String.toObject(): T? {
|
||
return try {
|
||
GsonUtils.gson.fromJson(this, object : TypeToken<T>() {}.type)
|
||
} catch (e: Exception) {
|
||
e.printStackTrace()
|
||
null
|
||
}
|
||
}
|
||
|
||
inline fun <reified T : Any> T.toJson(): String {
|
||
return GsonUtils.toJson(this)
|
||
}
|
||
|
||
/**
|
||
* 在限定 interval 里只触发一次 action
|
||
*/
|
||
fun debounceActionWithInterval(id: Int, interval: Long = 300, action: (() -> Unit)? = null) {
|
||
if (!ClickUtils.isFastDoubleClick(id, interval)) {
|
||
action?.invoke()
|
||
}
|
||
}
|
||
|
||
fun View.debounceActionWithInterval(interval: Long = 300, action: (() -> Unit)? = null) {
|
||
debounceActionWithInterval(this.id, interval, action)
|
||
}
|
||
|
||
/**
|
||
* 告诉需要返回 true or false 的外层这个事件已经被消费(即返回 true)
|
||
*/
|
||
inline fun consume(f: () -> Unit): Boolean {
|
||
f()
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* 简化那些不得不写 try catch 的代码块
|
||
*/
|
||
inline fun tryWithDefaultCatch(action: (() -> Unit)) {
|
||
try {
|
||
action.invoke()
|
||
} catch (e: Throwable) {
|
||
e.printStackTrace()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* String related
|
||
*/
|
||
fun String.fromHtml(): Spanned {
|
||
return Html.fromHtml(this)
|
||
}
|
||
|
||
// 去掉文章/答案的插入内容
|
||
fun String.removeInsertedContent(): String {
|
||
val textRegex = "(?s)<div class=\"gh-internal-content content-right\".*?</div>"
|
||
|
||
return this.replace(textRegex.toRegex(), "")
|
||
}
|
||
|
||
// 完全地清除所有 Html 格式
|
||
fun String.clearHtmlFormatCompletely(): String {
|
||
return Html.fromHtml(this).toString().replace('\n', 32.toChar())
|
||
.replace(160.toChar(), 32.toChar()).replace(65532.toChar(), 32.toChar()).trim { it <= ' ' }
|
||
}
|
||
|
||
// 如果该字符串长度超过固定长度的话,从头开始截取固定长度并返回
|
||
fun String.subStringIfPossible(length: Int): String {
|
||
return if (this.length > length) {
|
||
this.substring(0, length)
|
||
} else {
|
||
this
|
||
}
|
||
}
|
||
|
||
fun String.copyTextAndToast(toastText: String = "复制成功") {
|
||
val application = HaloApp.getInstance().application
|
||
val cmb = application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||
cmb.text = this
|
||
Utils.toast(application, toastText)
|
||
}
|
||
|
||
fun Map<String, String>.createRequestBody(): RequestBody {
|
||
val json = GsonUtils.toJson(this)
|
||
return RequestBody.create(MediaType.parse("application/json"), json)
|
||
}
|
||
|
||
// 对在浏览器(WebView)显示的路径进行转码
|
||
fun String.decodeURI(): String {
|
||
return URI(null, null, this, null).rawPath
|
||
}
|
||
|
||
/**
|
||
* 根据手机的分辨率从 dip(像素) 的单位 转成为 px
|
||
*/
|
||
fun Float.dip2px(): Int {
|
||
val scale = HaloApp.getInstance().application.resources.displayMetrics.density
|
||
return (this * scale + 0.5f).toInt()
|
||
}
|