Skip to content

Instantly share code, notes, and snippets.

@rharter
rharter / make_launcher.sh
Created Sep 23, 2020
Using this script you can create a tiny macOS app that will always launch the latest version of any JetBrains Toolbox installed IDE. Simply pass the target IDE application as the first argument and the script will create a launcher that you can add to your dock that will always point to the latest installed version of the IDE.
View make_launcher.sh
#!/bin/bash
APP_DIR=$1
APP_SCRIPT_NAME="$(ls "${APP_DIR}/Contents/MacOS/")"
APP_FILE_NAME="$(basename "${APP_DIR}")"
CHANNEL_DIR="$(dirname "$(dirname "$1")")"
CHANNEL_NAME="$(basename "$CHANNEL_DIR")"
TOOLBOX_APP_NAME="$(basename "$(dirname "$CHANNEL_DIR")")"
@rharter
rharter / StoreRepository.kt
Created Aug 19, 2020
Extension function to convert a store, which emits a stream of **events**, to a repository which emits a stream of **states**. This hides implementation details, like which datasource is providing the data, from consumers.
View StoreRepository.kt
sealed class Resource<T> {
data class Loading<T>(val data: T? = null, val message: Int? = null) : Resource<T>()
data class Success<T>(val data: T) : Resource<T>()
data class Error<T>(val throwable: Throwable?, val data: T? = null) : Resource<T>()
}
fun <T> Resource<T>.dataOrNull(): T? = when (this) {
is Resource.Loading -> data
is Resource.Success -> data
is Resource.Error -> data
@rharter
rharter / Scoped.kt
Last active Mar 16, 2021
A kotlin property delegate to scope any object to an android lifecycle. Blog post at https://ryanharter.com/blog/easy-android-scopes/
View Scoped.kt
import androidx.lifecycle.get
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
/**
* Returns a property delegate to access the wrapped value, which will be retained for the
* duration of the lifecycle of this [ViewModelStoreOwner].
*
View Notes.md

Dagger/Android with Internal Dependencies

I’ve figured out how to do what I wanted, though without some generation I’m not actually sure it’s worth it. Here’s a bunch more code with the solution I’ve figured out, in case anyone knows how to auto-generate this stuff, or if there’s a better way to do this.

The Goal: Allow use of Dagger-android AndroidInjectors without needing to make all dependencies of the target public.

The Scenario: I have a modular app where each feature is a separate module (among others). I have always hated making the ViewModels/ViewStates/ViewActions and other internal parts of the feature public, but that’s required with the current Dagger-android subcomponent approach.

Here’s my module setup:

@rharter
rharter / InjectableActivityScenario.kt
Created Jul 19, 2019
An ActivityScenario that allows you to use Dagger Android's automatic, lifecycle based injection without making your Application class `open`, or overriding it in tests.
View InjectableActivityScenario.kt
package com.pixite.pigment.testing
import android.app.Activity
import android.app.Instrumentation
import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
@rharter
rharter / InjectableActivityTestRule.kt
Last active Jan 30, 2021
Test Rule that allows you to use Dagger Android's automatic lifecycle based injection without making your Application class `open`, or overriding it in tests.
View InjectableActivityTestRule.kt
package com.pixite.pigment.testing
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
@rharter
rharter / config.yaml
Created May 24, 2018
Example Circle CI config file
View config.yaml
version: 2.0
defaults: &defaults
docker:
- image: menny/android_ndk:latest
working_directory: ~/pigment
environment:
_JAVA_OPTIONS: "-Xmx1400m -XX:ParallelGCThreads=2 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=2 -Djava.util.concurrent.ForkJoinPool.common.parallelism=2"
TERM: dumb
@rharter
rharter / LiveEvent.kt
Last active Apr 8, 2020
LiveData that only delivers new events to observers.
View LiveEvent.kt
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MediatorLiveData
import android.arch.lifecycle.Observer
fun <T> LiveData<T>.toLiveEvent(): LiveEvent<T> {
return when (this) {
is LiveEvent -> this
else -> LiveEvent<T>().apply {
@rharter
rharter / ViewCompatExt.kt
Last active Feb 4, 2018
Extension methods to easily provide all functionality in ViewCompat. This makes it super easy to see when there is a compat method available in code completion right alongside the framework methods.
View ViewCompatExt.kt
import android.content.ClipData
import android.graphics.Paint
import android.os.Build
import android.os.Bundle
import android.support.v4.view.*
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat
import android.view.*
/**
@rharter
rharter / SharedPreferenceLiveData.kt
Last active Mar 23, 2021
Creates LiveData objects that observe a value in SharedPreferences while they have active listeners.
View SharedPreferenceLiveData.kt
import android.arch.lifecycle.LiveData
import android.content.SharedPreferences
abstract class SharedPreferenceLiveData<T>(val sharedPrefs: SharedPreferences,
val key: String,
val defValue: T) : LiveData<T>() {
private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
if (key == this.key) {
value = getValueFromPreferences(key, defValue)