Skip to content

Instantly share code, notes, and snippets.

View afollestad's full-sized avatar
📱

Aidan Follestad afollestad

📱
View GitHub Profile
import android.view.View
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlin.coroutines.CoroutineContext
fun View.scopeWhileAttached(
context: CoroutineContext,
exec: CoroutineScope.() -> Unit
) {
val job = Job(context[Job])
import android.graphics.Typeface
import androidx.annotation.CheckResult
import java.lang.Exception
/** @author Aidan Follestad (@afollestad) */
object TypefaceHelper {
private val cache = hashMapOf<String, Typeface>()
/**
* Gets a typeface by its family name. Automatically statically caches it to avoid
class CommandLineParser(
private val input: String
) : Iterator<String> {
private var lastIndex: Int = 0
override fun hasNext(): Boolean = lastIndex in 0..input.length - 2
override fun next(): String {
val value = StringBuilder()
var quoteStarter: Char? = null
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@FlowPreview
@ExperimentalCoroutinesApi
fun main() {
runBlocking {
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
package com.afollestad.flow.util
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
@afollestad
afollestad / LiveDataZip.kt
Created December 7, 2018 06:55
Merge two live data into one
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Observer
typealias Zipper<T, K, R> = (T, K) -> R
/** @author Aidan Follestad (@afollestad) */
class ZipLiveData<T, K, R>(
source1: LiveData<T>,
source2: LiveData<K>,
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
fun EditText.onTextChanged(
@IntRange(from = 0, to = 10000) debounce: Int = 0,
cb: (String) -> Unit
) {
addTextChangedListener(object : TextWatcher {
val callbackRunner = Runnable {
@afollestad
afollestad / ViewExt.kt
Last active March 2, 2022 21:02
Some simple Kotlin view extensions
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import android.view.ViewTreeObserver
import android.widget.AdapterView
import android.widget.Spinner
fun View.show() {
visibility = VISIBLE
@afollestad
afollestad / .zshrc
Last active September 18, 2022 01:26
export JAVA_HOME='/Users/afollestad/Library/Java/JavaVirtualMachines/azul-11-ARM64'
export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
export PATH="$HOME/Library/Android/sdk/tools:$HOME/Library/Android/sdk/platform-tools:$PATH"
alias gw="./gradlew "
alias dev="cd ~/Development"
alias gcm="git commit -am "
alias gco="git checkout "
alias gpu="git push "
alias gpl="git pull "
@afollestad
afollestad / Debouncer.kt
Created June 12, 2019 18:08
Debounce clicks in Kotlin
import android.view.View
internal object Debouncer {
@Volatile private var enabled: Boolean = true
private val enableAgain = Runnable { enabled = true }
fun canPerform(view: View): Boolean {
if (enabled) {
enabled = false
view.post(enableAgain)