Skip to content

Instantly share code, notes, and snippets.

View realdadfish's full-sized avatar

Thomas Keller realdadfish

View GitHub Profile
@realdadfish
realdadfish / ScopeCancel.kt
Created February 7, 2023 11:53
Why does my shared flow subscription not complete after the scope it's shared in is canceled?
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.shareIn
@realdadfish
realdadfish / AndroidManifest.xml
Last active September 14, 2022 08:25
Enable / disable buttons in list
...
<application>
<activity
android:name="androidx.activity.ComponentActivity"
android:exported="false"/>
</application>
...
class HiltFragmentScenario<F : Fragment> private constructor(
private val fragmentClass: Class<F>,
val activityScenario: ActivityScenario<TestHiltActivity>
) {
@Suppress("UNCHECKED_CAST")
val fragment: F?
get() = activityScenario.getActivity()?.supportFragmentManager?.findFragmentByTag(FRAGMENT_TAG) as? F?
/**
* Moves Fragment state to a new state.
@realdadfish
realdadfish / ProgressIterator.kt
Created November 11, 2021 11:58
A trial to mimic "Type-driven design in Rust" (https://www.youtube.com/watch?v=bnnacleqg6k) with Kotlin
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
interface ExactSizeIterator<T> : Iterator<T> {
val length: UInt
}
fun <T> Collection<T>.exactSizeIterator() = object : ExactSizeIterator<T> {
private val inner = iterator()
override val length: UInt = size.toUInt()
override fun hasNext(): Boolean = inner.hasNext()
@realdadfish
realdadfish / LicenseReportPlugin.kt
Created January 28, 2021 15:35
Leveraging Gradle's ArtifactView
package some.package
import nl.javadude.gradle.plugins.license.DownloadLicensesExtension
import nl.javadude.gradle.plugins.license.DownloadLicensesReportExtension
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeCompatibilityRule
@realdadfish
realdadfish / gradle-4.7-with-remote-buildcache.log
Last active November 4, 2020 15:51
Slow buildSrc compilation Gradle 4.7 vs 4.8-milestone-1
[15:46:41] Welcome to Gradle 6.7!
[15:46:41]
[15:46:41] Here are the highlights of this release:
[15:46:41] - File system watching is ready for production use
[15:46:41] - Declare the version of Java your build requires
[15:46:41] - Java 15 support
[15:46:41]
[15:46:41] For more details see https://docs.gradle.org/6.7/release-notes.html
[15:46:41]
[15:46:41] To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/6.7/userguide/gradle_daemon.html.
@realdadfish
realdadfish / RetrofitBug.kt
Created August 24, 2020 10:45
Retrofit Bug
import io.reactivex.rxjava3.core.Completable
import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.mockwebserver.MockWebServer
import org.junit.Rule
import org.junit.Test
import retrofit2.Retrofit
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory
import retrofit2.http.POST
@realdadfish
realdadfish / build.gradle.kts
Last active February 27, 2023 07:22
Unmatched attributes issue and solution
val configuration = project.configurations.getByName("prodConsumerReleaseRuntimeClasspath")
val artifactType = Attribute.of("artifactType", String::class.java)
// Some plugin can only work with configurations, while the Android Gradle Plugin (AGP) has the newer "artifact view"
// paradigm implemented. This makes it impossible to resolve most of the created, variant-aware
// configurations from AGP "by hand" without getting unmatched attribute exceptions.
// We now pick one artifact that holds our dependencies and add a custom compatibility rule
// for it which basically accepts all incoming compatibility issues as long as the produced value
// on "the other side" is a JAR or AAR artifact.
configuration.attributes {
attribute(artifactType, "android-classes-directory")
@realdadfish
realdadfish / CpdPlugin.kt
Last active July 18, 2020 21:55
PMD CPD Kotlin Plugin
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.internal.tasks.factory.dependsOn
import de.aaschmid.gradle.plugins.cpd.Cpd
import de.aaschmid.gradle.plugins.cpd.CpdExtension
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.internal.HasConvention
import org.gradle.api.plugins.JavaPluginConvention
@realdadfish
realdadfish / MyCodemarkerPlugin.kt
Last active May 29, 2020 21:53
Kotlin Gradle Plugin that shows how the generation and consumption of artifacts between different Gradle projects works
open class MyCodemarkerPlugin : Plugin<Project> {
override fun apply(project: Project) {
val androidExtension = project.extensions.findByType(BaseExtension::class.java)
if (androidExtension != null) {
project.createCodemarkerTask {
sources.from(project.files(androidExtension.sourceSets.map {
it.java.srcDirs + it.assets.srcDirs + it.res.srcDirs + it.manifest.srcFile
}))
}
return