oligazar
10/20/2017 - 2:18 PM

Utils

Helper extension functions

  1. ViewUtils
  2. ColorUtils
  3. StringUtils
  4. ContextUtils
  5. GsonParsingUtils
  6. FirebaseExtensions
  7. CollectionUtils
  8. BitmapUtils
  9. ParcelExtensions
// 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()
}