Collection of utility classes that are common for multiple android projects.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
.idea
SecretSauce
ci
fastlane
gradle
sampleapplication
waitlayout
.gitignore
.travis.yml
CHANGELOG.md
CustomViewUse.md
Gemfile
LICENSE
NetworkChangeReceiverUsing.md
README.md
RoundedDrawableUsing.md
ViewUtilsUsing.md
WaitLayoutUsing.md
WebViewActivityUsing.md
build.gradle
circle.yml
findbugsExcludeFilter.xml
gradlew
gradlew.bat
settings.gradle

README.md

SecretSauce

Maven Central Maven Central master: Build Status develop: Build Status

Collection of utility classes that are common for multiple android projects.

Contains:

  • Activity Extension functions for hiding and showing keyboards, showing fragments with animations, default date picker, and more
  • WebViewActivity (for displaying basic full screen webview, that accepts URL and data to post)
  • WaitLayout - Displays spinner on any item, can synchronize with CachedFields and CachedEndpoints. This part is available in separate artifact
  • PicassoScrollListener (that stops loading images during fast scroll)
  • LogUtils (for situation when Timber is not appropriate)
  • NetworkChangeReceiver - wrapper from BroadcastReceiver that sets current state to Android ObservableBoolean
  • AnimationRunnable (helps with custom view transitions between two states)
  • Checkable views
  • LinkTextView (TextView where URLs are clickable)
  • RoundedDrawable, RoundedImageView
  • ViewUtils (set styled text fragments, convert dp to px, convert dp to pixel, and more)
  • Toast Context extension functions
  • JdkBasedTimeZoneProvider (for faster loading of JodaTime)
  • BindingAdapters - Common Android DataBinding adapters
    • onClick that does not pass view to listener (to avoid spill Android dependencies do viewModel)
    • showView (setVisibility VISIBLE/GONE)
    • error (editText.setError)
    • active (setActivated)
  • RxLifecycle extension functions - allows invoking certain action on Fragment/Activity pause (commonly used to detach view from viewModel, or similar cleanups)
  • Android Dagger Lifecycle common classes like [ViewModelFactory] and extension functions

initialization

Some feature are more convenient to use if you declare some parameters up front.

SecretSauceSettings.set(debug = BuildConfig.DEBUG,
                containerViewId = R.id.container,
                bindingViewModelId = BR.viewModel,
                viewModelFactoryProvider = { viewModelFactory })

Settings any of those values is optional. If you don't use given feature you can skip given parameter. However if you attempt to use given feature later you will either have to pass optional param to function or the exception will be thrown. debug is used by logs and Toasts containerViewId used by showFragment methods bindingViewModelId and viewModelFactoryProvider use by viewModel extension functions.

Do you create new instances of objects inside your fragments/activities by hand, because they require scoped context? Dagger Android support can help you!

Dagger works best if it can create all the injected objects

Lets say that we have successfully configured Dagger and we inject our non-Android dependencies.
Our activity may look something like that:

class MyActivity : AppCompatActivity() {

    @Inject
    lateinit var dependencyA: DependencyA
    @Inject
    lateinit var dependencyB: DependencyB
    val colorMixer by lazy { ColorMixer(this, dependencyA, dependencyB) }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityContextDependencies2Binding = bindContentView(R.layout.myactivity)
        DaggerUtil.inject(this)
        binding.textView.setBackgroundColor(colorMixer.color)
    }
}

It is not that bad, but there are several problems:

  • We create ColorMixer inside the activity. This tight coupling makes it easy to pass local context, but unfortunately we lose some of advantages of using Inversion of control. For example it becomes harder to switch ColorMixer implementation.

  • We do inject DependencyA and DependencyB that we do not need just so we can create ColorMixer instance. That introduces boilerplate and namespace pollution.

  • If ColorMixer had two arguments of the same type we risk of passing them in wrong order causing (sometimes) subtle bugs.

You may ask - what if I use application context everywhere instead? In some cases that may work but I would discourage it. You will get exception if you try to get layout inflater, and you may get wrong answer if you ask for color (if you use themed activities).

So what can we do? We can use dagger.android

Do you create new instances of objects inside your fragments/activities by hand, because they require scoped context? Dagger Android support can help you!

Dagger works best if it can create all the injected objects

Lets say that we have successfully configured Dagger and we inject our non-Android dependencies.
Our activity may look something like that:

class MyActivity : AppCompatActivity() {

    @Inject
    lateinit var dependencyA: DependencyA
    @Inject
    lateinit var dependencyB: DependencyB
    val colorMixer by lazy { ColorMixer(this, dependencyA, dependencyB) }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityContextDependencies2Binding = bindContentView(R.layout.myactivity)
        DaggerUtil.inject(this)
        binding.textView.setBackgroundColor(colorMixer.color)
    }
}

It is not that bad, but there are several problems:

  • We create ColorMixer inside the activity. This tight coupling makes it easy to pass local context, but unfortunately we lose some of advantages of using Inversion of control. For example it becomes harder to switch ColorMixer implementation.

  • We do inject DependencyA and DependencyB that we do not need just so we can create ColorMixer instance. That introduces boilerplate and namespace pollution.

  • If ColorMixer had two arguments of the same type we risk of passing them in wrong order causing (sometimes) subtle bugs.

You may ask - what if I use application context everywhere instead? In some cases that may work but I would discourage it. You will get exception if you try to get layout inflater, and you may get wrong answer if you ask for color (if you use themed activities).

So what can we do? We can use dagger.android