Init commit

This commit is contained in:
世界
2022-12-02 14:17:47 +08:00
commit 7736e1e644
121 changed files with 6295 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
package io.nekohasekai.sfa.ktx
import android.content.Context
import android.net.Uri
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import com.google.android.material.elevation.SurfaceColors
fun Context.launchCustomTab(link: String) {
val color = SurfaceColors.SURFACE_2.getColor(this)
CustomTabsIntent.Builder().apply {
setColorScheme(CustomTabsIntent.COLOR_SCHEME_SYSTEM)
setColorSchemeParams(
CustomTabsIntent.COLOR_SCHEME_LIGHT,
CustomTabColorSchemeParams.Builder().apply {
setToolbarColor(color)
}.build()
)
setColorSchemeParams(
CustomTabsIntent.COLOR_SCHEME_DARK,
CustomTabColorSchemeParams.Builder().apply {
setToolbarColor(color)
}.build()
)
}.build().launchUrl(this, Uri.parse(link))
}

View File

@@ -0,0 +1,17 @@
package io.nekohasekai.sfa.ktx
import android.content.Context
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
@ColorInt
fun Context.getAttrColor(
@AttrRes attrColor: Int,
typedValue: TypedValue = TypedValue(),
resolveRefs: Boolean = true
): Int {
theme.resolveAttribute(attrColor, typedValue, resolveRefs)
return typedValue.data
}

View File

@@ -0,0 +1,18 @@
package io.nekohasekai.sfa.ktx
import kotlin.coroutines.Continuation
fun <T> Continuation<T>.tryResume(value: T) {
try {
resumeWith(Result.success(value))
} catch (ignored: IllegalStateException) {
}
}
fun <T> Continuation<T>.tryResumeWithException(exception: Throwable) {
try {
resumeWith(Result.failure(exception))
} catch (ignored: IllegalStateException) {
}
}

View File

@@ -0,0 +1,24 @@
package io.nekohasekai.sfa.ktx
import android.content.Context
import androidx.annotation.StringRes
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.nekohasekai.sfa.R
fun Context.errorDialogBuilder(@StringRes messageId: Int): MaterialAlertDialogBuilder {
return MaterialAlertDialogBuilder(this)
.setTitle(R.string.error_title)
.setMessage(messageId)
.setPositiveButton(resources.getString(android.R.string.ok), null)
}
fun Context.errorDialogBuilder(message: String): MaterialAlertDialogBuilder {
return MaterialAlertDialogBuilder(this)
.setTitle(R.string.error_title)
.setMessage(message)
.setPositiveButton(resources.getString(android.R.string.ok), null)
}
fun Context.errorDialogBuilder(exception: Throwable): MaterialAlertDialogBuilder {
return errorDialogBuilder(exception.localizedMessage ?: exception.toString())
}

View File

@@ -0,0 +1,51 @@
package io.nekohasekai.sfa.ktx
import androidx.annotation.ArrayRes
import androidx.core.widget.addTextChangedListener
import com.google.android.material.textfield.MaterialAutoCompleteTextView
import com.google.android.material.textfield.TextInputLayout
import io.nekohasekai.sfa.R
var TextInputLayout.text: String
get() = editText?.text?.toString() ?: ""
set(value) {
editText?.setText(value)
}
var TextInputLayout.error: String
get() = editText?.error?.toString() ?: ""
set(value) {
editText?.error = value
}
fun TextInputLayout.setSimpleItems(@ArrayRes redId: Int) {
(editText as? MaterialAutoCompleteTextView)?.setSimpleItems(redId)
}
fun TextInputLayout.removeErrorIfNotEmpty() {
addOnEditTextAttachedListener {
editText?.addTextChangedListener {
if (text.isNotBlank()) {
error = null
}
}
}
}
fun TextInputLayout.showErrorIfEmpty(): Boolean {
if (text.isBlank()) {
error = context.getString(R.string.profile_input_required)
return true
}
return false
}
fun TextInputLayout.addTextChangedListener(listener: (String) -> Unit) {
addOnEditTextAttachedListener {
editText?.addTextChangedListener {
listener(it?.toString() ?: "")
}
}
}

View File

@@ -0,0 +1,21 @@
package io.nekohasekai.sfa.ktx
import android.content.ActivityNotFoundException
import androidx.activity.result.ActivityResultLauncher
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.nekohasekai.sfa.R
import io.nekohasekai.sfa.ui.shared.AbstractActivity
fun AbstractActivity.startFilesForResult(
launcher: ActivityResultLauncher<String>, input: String
) {
try {
return launcher.launch(input)
} catch (_: ActivityNotFoundException) {
} catch (_: SecurityException) {
}
val builder = MaterialAlertDialogBuilder(this)
builder.setPositiveButton(resources.getString(android.R.string.ok), null)
builder.setMessage(R.string.file_manager_missing)
builder.show()
}

View File

@@ -0,0 +1,66 @@
package io.nekohasekai.sfa.ktx
import androidx.preference.PreferenceDataStore
import kotlin.reflect.KProperty
fun PreferenceDataStore.string(
name: String,
defaultValue: () -> String = { "" },
) = PreferenceProxy(name, defaultValue, ::getString, ::putString)
fun PreferenceDataStore.stringNotBlack(
name: String,
defaultValue: () -> String = { "" },
) = PreferenceProxy(name, defaultValue, { key, default ->
getString(key, default)?.takeIf { it.isNotBlank() } ?: default
}, { key, value ->
putString(key, value.takeIf { it.isNotBlank() } ?: defaultValue())
})
fun PreferenceDataStore.boolean(
name: String,
defaultValue: () -> Boolean = { false },
) = PreferenceProxy(name, defaultValue, ::getBoolean, ::putBoolean)
fun PreferenceDataStore.int(
name: String,
defaultValue: () -> Int = { 0 },
) = PreferenceProxy(name, defaultValue, ::getInt, ::putInt)
fun PreferenceDataStore.stringToInt(
name: String,
defaultValue: () -> Int = { 0 },
) = PreferenceProxy(name, defaultValue, { key, default ->
getString(key, "$default")?.toIntOrNull() ?: default
}, { key, value -> putString(key, "$value") })
fun PreferenceDataStore.stringToIntIfExists(
name: String,
defaultValue: () -> Int = { 0 },
) = PreferenceProxy(name, defaultValue, { key, default ->
getString(key, "$default")?.toIntOrNull() ?: default
}, { key, value -> putString(key, value.takeIf { it > 0 }?.toString() ?: "") })
fun PreferenceDataStore.long(
name: String,
defaultValue: () -> Long = { 0L },
) = PreferenceProxy(name, defaultValue, ::getLong, ::putLong)
fun PreferenceDataStore.stringToLong(
name: String,
defaultValue: () -> Long = { 0L },
) = PreferenceProxy(name, defaultValue, { key, default ->
getString(key, "$default")?.toLongOrNull() ?: default
}, { key, value -> putString(key, "$value") })
class PreferenceProxy<T>(
val name: String,
val defaultValue: () -> T,
val getter: (String, T) -> T?,
val setter: (String, value: T) -> Unit,
) {
operator fun setValue(thisObj: Any?, property: KProperty<*>, value: T) = setter(name, value)
operator fun getValue(thisObj: Any?, property: KProperty<*>) = getter(name, defaultValue())!!
}

View File

@@ -0,0 +1,21 @@
package io.nekohasekai.sfa.ktx
import android.os.Parcel
import android.os.Parcelable
fun Parcelable.marshall(): ByteArray {
val parcel = Parcel.obtain()
writeToParcel(parcel, 0)
val content = parcel.marshall()
parcel.recycle()
return content
}
fun <T> ByteArray.unmarshall(constructor: (Parcel) -> T): T {
val parcel = Parcel.obtain()
parcel.unmarshall(this, 0, size)
parcel.setDataPosition(0) // This is extremely important!
val result = constructor(parcel)
parcel.recycle()
return result
}