Library of Kotlin extensions and tools for Android.
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
demo
docs
gradle/wrapper
library
.gitignore
.travis.yml
LICENSE
README.md
build.gradle
gradle.properties
gradlew
gradlew.bat
settings.gradle

README.md

Build Status Sonatype Nexus (Releases) License

KotX

kotx is a growing library of Kotlin extensions and tools for Android.

Quick Start

Add dependency to app/build.gradle:

dependencies {
    implementation 'com.github.0xe1f:kotx:1.0.6'
}

Documentation

KDoc-style documentation

Examples

Starting an activity:

startActivity<FooActivity> {
	putExtra(EXTRA_TITLE, title)
}

Showing an AlertDialog:

showAlertDialog(R.string.delete_everything) {
	positiveButton(R.string.yes) { _, _ -> deleteAll() }
	negativeButton(R.string.no)
}

Showing a Snackbar:

showSnackbar(view, R.string.do_you_agree) {
	setAction(R.string.agree) { validateAndSubmit(true) }
}

Popping a Toast:

showToast(R.string.update_successful)

Inflating a View:

val layout = container.inflate(R.layout.template_row)

Writing to SharedPreferences:

defaultSharedPreferences.edit {
	put(PREFS_LAST_SYNC, System.currentTimeMillis())
}

Creating a new Bundle:

newBundle {
	put(ARG_ANSWER, 42)
}

Fragments

Many helper methods are accessible in a Fragment without a reference to the container activity, safely:

startActivity<FooActivity> {
	putExtra(EXTRA_TITLE, title)
}

If getActivity() is null, startActivity<>() is never executed.

Generally, parameters that can be determined at runtime can be skipped in the context of a Fragment. For instance, for showSnackbar(), the view parameter can be omitted, e.g.:

showSnackbar(R.string.error)

Collections

KotX includes a growing list of helpers for collections and lists that are native to Android. These methods are thread-safe where possible; e.g. LruCache.getOrPut():

holder.title!!.text = titleCache.getOrPut("title") {
	buildTitle(object)
}

Get list of values in a SparseArray:

val items: List<String> = sparseArray.values

Preferences

In addition writing to SharedPreferences via Editor directly, you can write individual values via delegate properties:

var color: String? by Preferences.sharedPrefs

// Corresponds to Preferences.sharedPrefs.getString("color", "red")
val colorWithDefault: String?
    get() = color ?: "red"

Log.v(LOG_TAG, "color: $colorWithDefault")
color = "blue"
Log.v(LOG_TAG, "color: $colorWithDefault")

// Outputs:
// color: red
// color: blue

Delegate properties return nullable types to denote missing values, even for primitive types such as Integer. Writing a null value removes the key/value pair from SharedPreferences.

Safety

Many helpers deal with nullability issues safely. For instance, to avoid errors in a Fragment that's detached from activity, you can safely get it as a nullable String:

getStringSafe(R.string.foo)

Similarly Fragment.resourcesSafe will return a null Resources reference if the activity is detached.

Tools

Use ViewParser to tag View layout ids, then assign them at runtime - even for read-only properties:

@ViewParser.Leaf(layoutId = R.id.code)
private val codeField: EditText? = null
@ViewParser.Leaf(layoutId = R.id.confirm)
private val confirmButton: Button? = null

// ... later, in Fragment.onCreateView() ...

val layout = container.inflate(R.layout.fragment_main)
ViewParser.parse(this, layout)

// ... or Activity.onCreate() ...

setContentView(R.layout.activity_main)
ViewParser.parse(this)

Use ByteArrayBuilder to build byte arrays in a manner similar to StringBuilder - without resorting to IO streams or direct copying:

val encryptedString = buildByteArray {
	append("Salted__".toByteArray())
	append(salt)
	append(cipher.doFinal(message.toByteArray()))
}.toBase64String()

Date/Time

Java's poor handling of date/time types isn't exactly news. In place of a heavy reimplementation or simple syntactic sugar, KotX introduces DateTime and MutableDateTime, which act as thin wrappers around Java's Calendar:

// Basics:
DateTime(1983, 7, 15, 8, 0, 59) // Specific instance using current time zone
DateTime(1983, 7, 15, timeZoneId = "America/Los_Angeles") // or any time zone...
DateTime.now // Get current time/date
DateTime.today // Get today's date with time components set to 0

// Simple formatting using current time zone:
time.toString("MM/dd/yyyy HH:mm:ss")
// Using a custom formatter:
time.toString("MM/dd/yyyy HH:mm:ss".toDateFormat(tzCode, Locale.US))

// Parsing in current time zone:
val parsed = DateTime.parse("7/15/1983", "MM/dd/yyyy")
val wrong = DateTime.tryParse("HELLO!", "MM/dd/yyyy") // Returns null on error

// Math using specific units:
time.addedYears(3) // Return a new DateTime instance
time.addedMonths(6)
time.addedDays(17)

In addition to compareTo and equals, DateTime includes additional helpers using Kotlin's infix syntax:

when {
	time1 on time2 -> // ...
	time1 before time2 -> // ...
	time1 after time2 -> // ...
}

on (and variants onOrBefore and onOrAfter) is particularly useful for performing time zone-agnostic equality checks, since equals itself is not time zone agnostic.

Any methods called on a DateTime instance leave the receiver unmodified - thus DateTime is immutable. KotX also includes a mutable subclass called MutableDateTime, which adds functions and properties that modify the object in-place:

val mutable = MutableDateTime.now
mutable.year = 2043 // ... set a component explicitly
mutable.addDays(17) // ... increment a particular component

Because KotX is an Android library, DateTime and MutableDateTime are both Parcelable:

// Using the usual syntax:
dest.writeParcelable(time, 0)
time = source.readParcelable(DateTime::class.java.classLoader)
// or...
mutableTime = source.readParcelable(MutableDateTime::class.java.classLoader)

// Using extension methods:
dest.writeDateTime(time)
time = source.readDateTime()
// or...
mutableTime = source.readMutableDateTime()

The hit of DateTime on the size of the parcel is minor - it stores only the number of milliseconds since epoch (Long) and a string identifying the time zone.

DateTime and MutableDateTime work well with existing types, such as Calendar and Long (milliseconds since epoch), and most methods that take DateTime will also accept the Calendar and Long.

Finally, it's important to note that unlike Calendar, the DateTime's first month is numbered 1, not zero. Any methods expecting/returning the month number will adjust accordingly.

Advanced

Sending local broadcasts:

context.broadcastLocal(AUTH_ERROR)

Accessing the TextView of a Snackbar:

showSnackbar(R.string.access_denied) {
	textView?.maxLines = 1
}

Reading style attributes:

var textColor = Color.parseColor("#fff")
view.context.withStyledAttributes(R.styleable.GaugeView, attrs) {
	textColor = getColor(R.styleable.GaugeView_textColor, textColor)
}

Etc.

Examples listed here aren't exhaustive, particularly with regard to Context.

License

Copyright (c) Akop Karapetyan

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
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.