Each module is published as a library using Jitpack. Jitpack makes it easy to distribute libraries, but the way it handles versioning is not ideal for mult-module projects such as this one. Versions are defined using Git tags, which are not module-specific. This means if we create a 1.0.0
tag, Jitpack will publish a new 1.0.0
version for every module in the project. Since we want to track each module's version separately, we need to prefix our tags with the module name (e.g. lint-1.0.0
). Eventually we will want to ditch Jitpack and publish directly to Maven Central, which would allow us to specify module versions without the prefix. Publishing to Maven Central requires some additional work, such as registering an account and creating PGP keys to sign our releases.
Before using any of the dependencies below, add the Jitpack repository to your top-level build.gradle
file:
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' } <--- Add this line
}
}
The biometrics
module contains:
-
BaseBiometricsHelper
- Base class that creates ciphers, keys, and IVs, and also handles encryption, decryption, storage, and retrieval of the data. This class also has basic logging and error handling as well as some helper functions to help keep the UI logic clean and readable.
-
CredentialBiometricsHelper
- Implementation of
BaseBiometricHelper
that works with a username and password.
- Implementation of
-
TokenBiometricsHelper
- Implementation of
BaseBiometricHelper
that works with a string based token.
- Implementation of
To include these components in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:biometrics:biometrics-1.0.0'
The connectivity-notifier
module allows asynchronous and synchronous network monitoring. It is easy to set up and allows you to globally
react when the network state changes across wifi, cellular, and vpn.
To set up:
- Call registerConnectivityNetworkCallbacks() within onCreate() of your application.
override fun onCreate() {
super.onCreate()
registerConnectivityNetworkCallbacks()
}
- Subscribe to the ConnectivityNotifier in your base activity or whichever class you need this functionality. You can also set this up globally via an ApplicationLifecycleCallback, but this can get challenging depending on your use case.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
disposables.add(
ConnectivityNotifier
.isConnected
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ isConnected ->
if (isConnected) {
onConnected()
} else {
onDisconnected()
}
}, {
Timber.e(
it,
"BaseActivity.ConnectivityNotifier.isConnected error"
)
})
)
}
private fun onConnected() {
// Logic to be performed when the network is back online
}
private fun onDisconnected() {
// Logic to be performed when the network is lost. Typically showing a Snackbar/Toast or logging.
}
- (optional) Depending on your use case, you may also want to update the UI in onResume() if the state is disconnected. The observable stream will trigger any future connectivity events, but if the network is lost prior to navigation, the new screen will not show that the network is disconnected. This library allows for a synchronous way of checking for network state as well which is what we will use here.
override fun onResume() {
super.onResume()
if (!ConnectivityStateHolder.isConnected) {
onDisconnected()
}
}
private fun onDisconnected() {
// Logic to be performed when the network is lost. Typically showing a Snackbar/Toast or logging.
}
To include this module in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:connectivity-notifier:connectivity-notifier-1.0.0'
The core
module contains extension functions for Kotlin data types, as well as some helper classes around LiveData
, AndroidX Navigation, and RxJava Diposables
:
- LiveData
-
KotlinLiveData
- ALiveData
class that enforces Kotlin's nullable types -
KotlinMutableLiveData
- AMutableLiveData
class that enforces Kotlin's nullable types
-
- AndroidX Navigation
-
BottomNavigationBehavior
- This class acts as a bridge between the AndroidX Navigation library and aBottomNavigationView
. It creates a separate back stack ofFragments
for each bottom navigation tab and handles switching between these back stacks whenever the currently selected tab has changed. This allows users to switch tabs without losing their navigation state.
-
- RxJava
-
DisposableUtilsKx
- Extension functions for RxJava'sDisposable
type
-
- Various extension functions for built-in types:
BooleanUtilsKx
ByteArrayUtilsKx
CollectionUtilsKx
DateUtilsKx
DoubleUtilsKx
FloatUtilsKx
LiveDataUtilsKx
NumberUtilsKx
StringUtilsKx
ThrowableUtilsKx
UtilsKx
ZoneDateTimeUtilsKx
To include this module in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:core:core-1.0.0'
The lint
module contains a few custom lint checks:
-
CompositeDisposableClearDetector
- A
CompositeDisposable
reference is being held, but it is not being cleared.
- A
-
DisposableClearDetector
- A
Disposable
reference is being held, but it is not being disposed of.
- A
-
LiveDataDetector
-
KotlinLiveData
should be used instead ofLiveData
for null safety.
-
-
MutableLiveDataDetector
-
KotlinMutableLiveData
should be used instead ofMutableLiveData
for null safety.
-
-
NavControllerDetector
- A bug exists within
NavController.navigate()
that can sometimes cause the application to crash.NavController.navigateSafe()
should be used instead.
- A bug exists within
To include these lint checks in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:lint:lint-1.0.0'
The network
module contains two network interceptors relating to API authentication, as well as a RFC-6265 compliant cookie implementation:
-
AuthInterceptor
- An OkHttp Interceptor that retrieves an authentication token using
TokenDataSource.currentTokens()
, and attaches it to outgoing requests using theAuthorization
header.
- An OkHttp Interceptor that retrieves an authentication token using
-
RefreshInterceptor
- An OkHttp Interceptor that attempts to refresh expired authentication tokens. Upon receiving a 401 error, the interceptor calls
TokenApi.refreshToken()
, stores the result usingTokenDataSource.insertTokens()
, and then retries the original request using the new token.
- An OkHttp Interceptor that attempts to refresh expired authentication tokens. Upon receiving a 401 error, the interceptor calls
-
PersistentCookieJar
- An implementation of OkHttp's CookieJar. This implementation stores session and persistent cookies using the
SessionCookieCache
andPersistentCookieCache
interfaces. An implementation of each are included:-
InMemoryCookieCache
: Stores session cookies using aHashSet
-
RoomCookieCache
: A Dao for the AndroidX Room library that stores persistent cookies in a SQLite database
-
- An implementation of OkHttp's CookieJar. This implementation stores session and persistent cookies using the
-
is401Unauthorized()
- Any easy way to check if a Throwable is a Retrofit HttpException with a 401 code.
-
is400BadRequest()
- Any easy way to check if a Throwable is a Retrofit HttpException with a 400 code.
To include these components in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:network:network-1.0.0'
The push-notifications
module allows you to locally schedule push notifications. The implementation used keeps all of your push notification logic
in one place and also survives device reboots.
To set up:
- Create a class that extends
BasePushNotificationHelper
and implementcreatePushNotification
andcreateNotificationChannel
.
class PushNotificationHelper(private val app: Application) : BasePushNotificationHelper(app) {
override fun createPushNotification(
title: String,
body: String,
intentData: Map<String, String>?
): Notification {
// Your logic here
}
override fun createNotificationChannel() {
// Your logic here
}
}
- Within your Application, implement
PushNotificationHelperProvider
. You will most likely want to implement this using lazy loading as well as in a Singleton manner. Below we are initializing the helper the first time it is accessed and then returning that single instance with any future calls to get().
class CovidApplication : MultiDexApplication(), PushNotificationHelperProvider {
private var pushNotificationHelper: PushNotificationHelper? = null
override fun get(): PushNotificationHelper? {
if (pushNotificationHelper == null) {
pushNotificationHelper = PushNotificationHelper(this)
}
return pushNotificationHelper
}
}
- Create a list of ScheduledNotifications and call
pushNotificationHelper.schedulePushNotifications(notifications)
. This will handle scheduling the notifications using AlarmManager. These scheduled notifications will also survive device reboots.
To include this module in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:push-notifications:push-notifications-1.0.0'
The test-utils
module contains the following to help make testing quicker and more standardized:
- Utils to generate random non-null primitive data types:
-
randomInt()
- Generates a random integer and allows you to specify a range. Default range is any positive integer. -
randomDouble()
- Generates a random double and allows you to specify a range. Default range is any positive double. -
randomLong()
- Generates a random long and allows you to specify a range. Default range is any positive long. -
randomFloat()
- Generates a random float and allows you to specify a range. Default range is any positive float. -
randomCharacter()
- Generates a random character in the set (a-z, A-Z, 0-9). -
randomString()
- Generates a random string of the character set (a-z, A-Z, 0-9). The length of the string can be set via a min and max length. Default is a random string between 5 and 30 characters. -
randomBoolean()
- Generates a random boolean.
-
- Custom Espresso actions, assertions, and matchers to make testing more efficient, concise, and readable. This library utilizes the Barista library. Please make sure that any custom Espresso actions, assertions, and matchers you add to this library don't already exist within Barista. This is meant to be an extension of that library.
- Custom extension functions built around
ActivityTestRule
:-
rotateLandscape()
- Easily rotate the screen into the landscape orientation. -
rotatePortrait()
- Easily rotate the screen into the portrait orientation. -
launchFragment()
- Easily launch a fragment into a dummy activity to test it in isolation.
-
-
replaceNavControllerWithMock()
- Extension function built aroundFragment
to easily swap theNavController
with a mock. -
runOnMainThread()
- Utility function to easily run a function on the main thread. -
RxIdlerTestRunner
- A fully configured test runner that utilizes RxIdler to help cut down on flakiness when testing code that contains asynchronous RxJava operations. -
TestActivity
- A dummy activity used to test a Fragment in isolation.
To use this module in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:test-utils:test-utils-1.0.0'
The ui
module contains the following:
- Custom BindingAdapters:
-
ImageView.showNetworkImage()
- Bind to a URL to be able to load images into the ImageView via Glide. This custom adapter also allows you to specify a placeholder and an error Drawable. -
View.setVisible()
- Bind to a boolean to be able to set the visibility of the view. -
setSelected()
- Binds a boolean to a View'sisSelected
property
-
- Various extension functions for the following:
- Context
- Activity
- Fragment
- RecyclerView
- TabLayout
- View
- ViewGroup
- Various Utils
-
AnimationEndListener
- This is an interface that cuts down on some boilerplate code when trying to implement an AnimationListener when all you care about is wanting to know when the animation ends. -
HtmlListTagHandler
- Custom tag handler that can be passed in when converting from Html to a Spannable. This tag handler properly numbers ordered lists as well as properly indents both ordered and unordered Html lists. -
LineDividerDecoration
- This decorator can be added to a RecyclerView to draw a separator between the items. It can take in a custom drawable, work with vertical or horizontal orientations, and can hide the separator on the last item if desired. -
SnapHelperOneByOne
- This can be attached to a RecyclerView to allow the items to snap into place in the center one by one. This is typically used for a gallery/carousel type implementation. -
ViewModelFactoryUtil
- This utility function cuts down on some of the boilerplate code around creating a ViewModelProvider factory. -
BlurExtensions
- Contains helper functions for blurringViewGroups
-
- Reusable Views
-
BaseFragment
- A headless view class that is an extension of Fragment with a few helpers tacked on. This class makes setting up a toolbar, getting a reference to an AppCompatActivity, and getting a reference to the ActionBar a bit easier. -
BinaryViewPager
- This is a custom ViewPager that lazily loads Fragments and does not support swiping. Currently, it only supports a max of two Fragments. -
IconToggleButton
- This is a custom toggle button that allows you to have a checked and an unchecked state with a icon in the center.
-
To use this module in your project, add the following dependency:
implementation 'com.github.1904labs.android-utils:ui:ui-1.0.0'