Skip to content

skoumalcz/grimoire-wand

Repository files navigation

GrimoireWand

Why Wand?

  • Consistency

    Adds consistency to your code when working with your app's resources or common APIs.

  • Ease of Use

    Many utilities in Wand serve as an extension of system APIs and improving upon them. No long setups or boilerplate needed.

ColorWand + extensions

ColorWand resolves colors only after calling .getColor(context). In some cases it's only used as a pass-through to resolve colors immediately. For instance when calling color extension methods directly on context, it will use ColorWand however immediately resolves the value.

// requires app context, optionally works with themed context
context.colorStateList(R.color.primary_color) // returns entire state list
context.color(R.color.primary_color) // returns default color

// requires themed context
context.attribute(R.attr.colorPrimary) // returns default color for attribute

In cases in which you need to save the value and resolve later, you can use as methods.

var wand: ColorWand

wand = asColor(0x000000)
wand = asColorRes(R.color.primary_color)
wand = asColorStateList(R.color.primary_color)
wand = asColorAttr(R.attr.colorPrimary)

// … some time later

wand.getColor(context)

TextWand + extensions

TextWand too works as a static holder up until the point you want to resolve the values

context.text(R.string.my_string, username)
context.quantityText(R.plurals.days, dayCount, dayCount)

Or save and resolve later

var wand: TextWand

wand = asText("My string")
wand = asText(R.string.my_string)
wand = asText(R.plurals.my_string, dayCount, dayCount)

InsetsWand

Working with insets was never easy on Android, however if you do figure them out, apps look infinitely better and more modern. Why not giving it a try?

In Activity

override fun onCreate(/**/) {
    val wand = InsetsWand(this)
    lifecycleScope.launch {
        wand.collect { //it: InsetsWand
            // todo save / apply insets
        }
    }
}

In Fragment

override fun onViewCreated(/**/) {
    val wand = InsetsWand(this)
    lifecycleScope.launch {
        wand.collect { //it: InsetsWand
            // todo save / apply insets
        }
    }
}

In View

override fun onAttachedToWindow() {
    val wand = InsetsWand(this)
    viewScope.launch {
        wand.collect { //it: InsetsWand
            // todo save / apply insets
        }
    }
}

WindowAppearance

Unfortunately Android framework doesn't provide clear and concise way to automatically adjust status and navigation bar colors based on system version and theme colors. One simple extension method comes to the rescue. It works wonderfully with InsetsWand!

In Activity

override fun onCreate(/**/) {
    applyWindowAppearance()
}

This declaration alone will not do any wonders. You need to implement themes, which might be somewhat difficult too, so here's a helpful guide.

values/styles.xml

<style name="Foundation" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="android:windowTranslucentStatus">@bool/windowTranslucentStatus</item>
    <item name="android:windowTranslucentNavigation">@bool/windowTranslucentNavigation</item>
    <item name="android:navigationBarColor">@color/colorNavigationBar</item>
    <item name="android:statusBarColor">@color/colorStatusBar</item>
</style>

<style name="Foundation.Extras" />

<style name="AppTheme" parent="Foundation.Extras">
    <!-- This is really important as it sets the window background without which the WindowAppearance tool won't work -->
    <item name="colorSurface">@color/colorSurface</item>
    <!-- Declare other colors here -->
</style>

values-v23/styles.xml

<style name="Foundation.Extras">
    <item name="android:windowLightStatusBar">@bool/light_mode</item>
</style>

values-v27/styles.xml

<style name="Foundation.Extras">
    <item name="android:windowLightStatusBar">@bool/light_mode</item>
    <item name="android:windowLightNavigationBar">@bool/light_mode</item>
</style>

Extensions

You might find a healthy amount of helpful extensions here:

Recycler View

Often than not we all find ourselves using RecyclerViews with the pesky adapters and view holders. We aim to solve this with some easy boilerplate reducers.

AsyncBindingAdapter

It merges principles of AdapterListDiffer and BindingViewHolder. Only thing you really need to do is provide layout resources through getItemViewType. Fetching specific items can be done through getItemAt(position).

class MyAdapter(
    differ: DiffUtil.ItemCallback<MyData>,
    extras: ExtrasBinder? = null
) : AsyncBindingAdapter<MyData>(differ, extras) {

    override fun getItemViewType(position: Int) {
        val item = getItemAt(position)
        return when(item) {
            null -> R.layout.placeholder
            else -> R.layout.item_my_data
        }
    }

}

You can also bind extras (additional data, callbacks, etc…) to your items through ExtrasBinder

Logo by smalllikeart

About

Every magician needs a wand, how else would you cast spells from grimoire? -|- Wand is a collection of helpful utilities for managing Android screens.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages