Helper extension functions
// allows to switch image loading library in one place
fun ImageView.setImage(path: String?) {
Picasso.get()
.load(path)
.into(this)
}
// obtain activity from context
private fun Context.getActivity(): Activity? {
var c = this
while (c is ContextWrapper) {
if (c is Activity) return c
c = c.baseContext
}
return null
}
// works from everywhere
fun Context.hideKeyboardFrom(view: View) {
val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
fun Context.showAlert(title: Int, message: Int = -1, okAction: (() -> Unit)? = null, cancelAction: ((DialogInterface, Int) -> Unit)? = null) {
AlertDialog.Builder(this)
.setTitle(title)
.apply{ if (message >= 0) setMessage(message ) }
.setPositiveButton(R.string.btn_ok) { _, _ -> okAction?.invoke() }
.apply { cancelAction?.let { setNegativeButton(R.string.btn_ok, it) } }
.create()
.show()
}
fun Context.getMoneySum(sum: String?) = sum?.let { getString(R.string.format_price_baht, it) }
// only works when called from Activity
fun Activity.hideKeyboard() {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
//Find the currently focused view, so we can grab the correct window token from it.
var view = currentFocus
//If no view currently has focus, create a new one, just so we can grab a window token from it
if (view == null) {
view = View(this)
}
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
fun Activity.showSoftKeyboard(view: View) {
if (view.requestFocus()) {
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val isShowing = inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
if (!isShowing) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
}
}
}
inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
beginTransaction().func().commit()
}
// Allows to omit smth like (activity as? AppCompatActivity)?.setSupportActionbar(toolbar)
// instead appCompatActivity { setSupportActionBar(toolbar) }
inline fun Fragment.appCompatActivity(body: AppCompatActivity.() -> Unit) {
(activity as? AppCompatActivity)?.body()
}
/**
* Created by Admin on 18/2/18.
* Parcel extensions
*/
fun Parcel.readStringMap(): Map<String, String> {
val size = readInt()
val map = mutableMapOf<String, String>()
for (i in 0 until size) {
map[readString()] = readString()
}
return map
}
/**
* pictures
* parcel.writeInt(pictures.size)
* pictures.forEach { parcel.writeString(it)
* */
fun Parcel.readStringArray(): ArrayList<String> {
val size = readInt()
val array = arrayListOf<String>()
for (i in 0 until size) {
array.add(readString())
}
return array
}
/**
* Created by Admin on 15/2/18.
* Bitmap extensions
*/
fun Bitmap.scaleDown(maxImageSize: Float, filter: Boolean): Bitmap {
val ratio = Math.min(
maxImageSize / width,
maxImageSize / height)
Log.d("OfferCategory", "scaleDown() before, width: $width, height: $height")
val width = Math.round(ratio * width)
val height = Math.round(ratio * height)
Log.d("OfferCategory", "scaleDown() after, width: $width, height: $height")
return Bitmap.createScaledBitmap(this, width, height, filter)
}
fun ByteArray.toBitmap(): Bitmap =
BitmapFactory.decodeStream(ByteArrayInputStream(this))
fun Bitmap.compress(quality: Int = 100): ByteArray {
Log.d("OfferCategory", "compress() before, size: $byteCount")
val out = ByteArrayOutputStream()
compress(Bitmap.CompressFormat.JPEG, quality, out)
Log.d("OfferCategory", "compress() after, size: ${out.toByteArray().size}")
return out.toByteArray()
}
fun Bitmap.modifyOrientation(contentResolver: ContentResolver, uri: Uri): Bitmap {
try {
contentResolver.openInputStream(uri)
.use { inputStream ->
val exif = ExifInterface(inputStream)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
return when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> rotate(90f)
ExifInterface.ORIENTATION_ROTATE_180 -> rotate(180f)
ExifInterface.ORIENTATION_ROTATE_270 -> rotate(270f)
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> flip(true, false)
ExifInterface.ORIENTATION_FLIP_VERTICAL -> flip(false, true)
else -> this
}
}
} catch (e: IOException) {
e.printStackTrace()
}
return this
}
private fun Bitmap.rotate(degrees: Float): Bitmap {
val matrix = Matrix()
matrix.postRotate(degrees)
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
}
private fun Bitmap.flip(horizontal: Boolean, vertical: Boolean): Bitmap {
val matrix = Matrix()
matrix.preScale(if (horizontal) -1f else 1f, if (vertical) -1f else 1f)
return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
}
fun <T> Array<out T>.indexOfOrNull(element: T): Int? {
return indexOf(element).let {
if (it < 0) null else it
}
}
/**
* Created by Admin on 2/2/18.
* Firebase extensions
*/
interface SimpleValueEventListener: ValueEventListener {
override fun onCancelled(snap: DatabaseError?) { }
}
inline fun Query.addValueEventListener(crossinline onValue: (DataSnapshot) -> Unit): ValueEventListener {
return addValueEventListener(object: SimpleValueEventListener {
override fun onDataChange(snap: DataSnapshot) = onValue(snap)
})
}
inline fun Query.addListenerForSingleValueEvent(crossinline onValue: (DataSnapshot) -> Unit) {
addListenerForSingleValueEvent(object: SimpleValueEventListener {
override fun onDataChange(snap: DataSnapshot) = onValue(snap)
})
}
interface KeyAble {
var key: String
}
inline fun <reified K: KeyAble> getKeyableList(path: String, crossinline onSuccess: (List<K>) -> Unit) {
Log.d("FirebaseExtensions", "getKeyableList(), path: $path")
val rootRef = FirebaseDatabase.getInstance().reference
rootRef.child(path)
.addListenerForSingleValueEvent { snap ->
val list = snap.children.mapNotNull {
it.getValue(K::class.java)
?.apply { key = it.key }
}
onSuccess(list)
}
}
inline fun <reified K: KeyAble> getKeyable(path: String, crossinline onSuccess: (K?) -> Unit) {
Log.d("FirebaseExtensions", "getKeyableList(), path: $path")
val rootRef = FirebaseDatabase.getInstance().reference
rootRef.child(path)
.addListenerForSingleValueEvent { snap ->
val keyAble = snap.getValue(K::class.java)
?.apply { key = snap.key }
onSuccess(keyAble)
}
}
fun JsonObject.getAsJsonObjectOrNull(fieldName: String): JsonObject? {
return get(fieldName)
?.getAsJsonObjectOrNull(fieldName)
}
fun JsonElement.getAsJsonObjectOrNull(fieldName: String = "JsonElement"): JsonObject? {
return run {
if (isJsonObject) asJsonObject
else {
android.util.Log.e("ParseJson", "$fieldName is not a JsonObject")
null
}
}
}
fun JsonObject.getAsJsonArrayOrNull(fieldName: String): JsonArray? {
return get(fieldName)?.run {
if (isJsonArray) asJsonArray
else {
android.util.Log.e("ParseJson", "$fieldName is not a JsonArray")
null
}
}
}
fun JsonObject.getAsStringOrNull(fieldName: String): String? = get(fieldName)?.getAsStringOrNull()
fun JsonElement.getAsStringOrNull(): String? {
return try {
asString
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun JsonObject.getAsIntOrNull(fieldName: String): Int? {
return try {
get(fieldName).asInt
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun String.transliterate(): String {
val abcCyr = 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', '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')
val abcLat = arrayOf(" ", "a", "b", "v", "g", "d", "e", "e", "zh", "z", "i", "y", "k", "l", "m", "n", "o", "p", "r", "s", "t", "u", "f", "h", "ts", "ch", "sh", "sch", "", "i", "", "e", "ju", "ja", "A", "B", "V", "G", "D", "E", "E", "Zh", "Z", "I", "Y", "K", "L", "M", "N", "O", "P", "R", "S", "T", "U", "F", "H", "Ts", "Ch", "Sh", "Sch", "", "I", "", "E", "Ju", "Ja", "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", "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")
val builder = StringBuilder()
for (i in 0 until this.length) {
for (x in abcCyr.indices) {
if (this[i] == abcCyr[x]) {
builder.append(abcLat[x])
}
}
}
return builder.toString()
}
fun <T> Iterable<T>.joinToSpannedString(separator: CharSequence = ", ", prefix: CharSequence = "", postfix: CharSequence = "", limit: Int = -1, truncated: CharSequence = "...", transform: ((T) -> CharSequence)? = null): SpannedString {
return joinTo(SpannableStringBuilder(), separator, prefix, postfix, limit, truncated, transform)
.let { SpannedString(it) }
}
fun Uri.getFileName(context: Context): String? {
if (scheme == "content") {
val path = getPath(context)
Log.d("Uri.getFileName", "onActivityResult, path: $path")
return File(path).name
}
return null
}
inline fun CharSequence.ifNotEmpty(block: (CharSequence) -> Unit) {
if (this.isNotEmpty()) block(this)
}
fun getVisibility(isVisible: Boolean?) = if (isVisible == true) View.VISIBLE else View.GONE
fun getVisibility(string: String?) = getVisibility(!string.isNullOrEmpty())
infix fun <S: CharSequence> S.ifNullOrEmpty(alternative: S?): S? = if (isNullOrEmpty()) alternative else this
/** Path */
fun Uri.getPath(context: Context): String? {
var cursor: Cursor? = null
try {
val proj = arrayOf(MediaStore.Images.Media.DATA)
cursor = context.contentResolver.query(this, proj, null, null, null)
val columnIndex = cursor?.getColumnIndex(MediaStore.Images.Media.DATA)
cursor.moveToFirst()
return columnIndex?.let { if (columnIndex != -1) cursor?.getString(columnIndex) else null }
} finally {
cursor?.close()
}
}
// Makes color darker (factor < 1) or lighter (caftor > 1)
private fun manipulateColor(color: Int, factor: Float): Int {
val a = Color.alpha(color)
val r = Math.round(Color.red(color) * factor)
val g = Math.round(Color.green(color) * factor)
val b = Math.round(Color.blue(color) * factor)
return Color.argb(a,
Math.min(r, 255),
Math.min(g, 255),
Math.min(b, 255))
}
// set statusbar color
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = colorDark
}
// add alhpa (opacity) to int color
val colorAlpha = ColorUtils.setAlphaComponent(color, 225)
fun Int.random(): Int = Random().nextInt(this)
/**
* Transforms sp value to pixels
*/
fun View.sp(sp: Float) = Math.round(sp * resources.displayMetrics.scaledDensity).toFloat()
fun View.sp(sp: Int) = Math.round(sp * resources.displayMetrics.scaledDensity)
/**
* Transforms dip value to pixels
*/
fun View.dp(sp: Float) = Math.round(sp * resources.displayMetrics.density).toFloat()
fun View.dp(sp: Int) = Math.round(sp * resources.displayMetrics.density)
/**
* Handy method to obtain color
*/
fun View.getColor(colorId: Int) = ContextCompat.getColor(context, colorId)
/**
* Allows to retrieve values from styled Attributes without worrying about
* recycling it afterwards
*/
inline fun View.styledAttributesTransaction(attrs: AttributeSet?, styleables: IntArray, block: TypedArray.() -> Unit) {
context.obtainStyledAttributes(attrs, styleables).apply(block).recycle()
}