Skip to content

Instantly share code, notes, and snippets.

View NikolaDespotoski's full-sized avatar

Nikola Despotoski NikolaDespotoski

View GitHub Profile
@NikolaDespotoski
NikolaDespotoski / RegisterForActivityResultDetector.kt
Last active November 2, 2020 15:00
RegisterForActivityResultDetector LINT detector that warns on incorrect usage of registerForActivityResult
/*
* Copyright (C) 2020 Nikola Despotoski
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
@NikolaDespotoski
NikolaDespotoski / FlowIterable.kt
Last active October 5, 2020 23:04
Filter items from Iterable with Flow
suspend fun <T, R : Iterable<T>> Flow<Iterable<T>>.filterIterable(predicate: suspend (T) -> Boolean): Flow<R> =
transform {
flow {
val emitted = arrayListOf<T>()
for (item in it) {
if (predicate(item)) {
emitted.add(item)
}
}
emit(emitted)
@NikolaDespotoski
NikolaDespotoski / TaskKt.kt
Created September 8, 2020 21:09
Await Google Play Services Task in coroutine
suspend fun <TResult> Task<TResult>.awaitTask() =
suspendCancellableCoroutine<Task<TResult>> { continuation ->
addOnCompleteListener {
continuation.resume(it)
}
}
suspend fun <TResult> Task<TResult>.awaitTaskResult() =
suspendCancellableCoroutine<TResult> { continuation ->
addOnCanceledListener {
@NikolaDespotoski
NikolaDespotoski / AnimationKtx.kt
Last active September 8, 2020 12:11
Extensions for Android Animation package.
data class AnimationListenerChain(val animation: Animation, val listener: Animation.AnimationListener = object : Animation.AnimationListener() {
override fun onAnimationStart(animation: Animation?) {}
override fun onAnimationEnd(animation: Animation?) {}
override fun onAnimationRepeat(animation: Animation?) {}
})
inline fun Animation.doOnStart(crossinline onStart: () -> Unit): AnimationListenerChain {
val chain = AnimationListenerChain(this)
@NikolaDespotoski
NikolaDespotoski / ChainActivityResultCallback.kt
Created July 22, 2020 18:06
Chain activity result callbacks using plus operator
operator fun <O> ActivityResultCallback<O>.plus(another: ActivityResultCallback<O>) = ActivityResultCallback<O> {
[email protected](it)
another.onActivityResult(it)
}
suspend fun <F : Fragment> FragmentManager.awaitFragmentById(idRes: Int) =
suspendCancellableCoroutine<F> {
val backStackListener = object : FragmentManager.OnBackStackChangedListener {
override fun onBackStackChanged() {
val f = findFragmentById(idRes) as F?
if (f != null) {
removeOnBackStackChangedListener(this)
it.resume(f)
}
}
object SuspendingBleScanner {
suspend fun BluetoothLeScanner.startScan(timeout: Long, scanFilters: List<ScanFilter>? = null, settings: ScanSettings? = null): List<ScanResult> {
check(timeout <= 0L) { "BLE scan timeout must be > 0" }
var scanCallback: ScanCallback? = null
val accumulator = mutableListOf<ScanResult>()
return try {
withTimeout(timeout) {
suspendCancellableCoroutine<List<ScanResult>> { continuation ->
scanCallback = object : ScanCallback() {
fun <I, O> prepareCall(activityResultContractor: ActivityResultContractor<I, O>,activityResultCallback: ActivityResultCallback<O>)
: ActivityResultLauncher<I> = prepareCall(object : ActivityResultContract<I, O>() {
override fun createIntent(input: I): Intent = activityResultContractor.createIntent(input)
override fun parseResult(resultCode: Int, intent: Intent?): O = activityResultContractor.parseResult(resultCode, intent)
}, activityResultCallback)
}
class ProductsViewModel @Inject constructor(
 private val activityResultContractor: ActivityResultContractor<Nothing, String>,
  private val coroutineProvider: CoroutineProvider,
 private val productInteractor: ProductInteractor
) : ViewModel(), ActivityResultCallback<String>,
 ActivityResultContractor<Unit, String> by activityResultContractor{
override fun onActivityResult(result: String?) {
 // do something with the result
 }
@FragmentScope
class ChooseFilterContractor @Inject constructor(private val context: Context) : ActivityResultContractor<Unit, String> {
  override fun createIntent(input: Unit): Intent = Intent(context, ChooseFilterActivity::class.java)
 
override fun parseResult(resultCode: Int, intent: Intent?): String = if (resultCode == Activity.RESULT_OK) {
 intent?.getStringExtra(KEY_FILTER)?: error("Dispatched RESULT_OK, but no payload)
 } else {
 ""
 }
}