Skip to content

Commit

Permalink
Decorating feature showing and updating offline snackbar (#189)
Browse files Browse the repository at this point in the history
* Add network status feature

* Add observable network status implementation

* Add proper string for offline state

* Update dependency and use default callbacks to avoid boilerplate

* Remove exra empty lines
  • Loading branch information
jraska authored Oct 12, 2019
1 parent 0da9812 commit 633f1b2
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 0 deletions.
1 change: 1 addition & 0 deletions client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ dependencies {
api project(':core-android')
api project(':lib:identity')
api project(':lib:dynamic-features')
api project(':lib:network-status')
api project(':feature:push')
api project(':feature:users')
api project(':feature:settings_entrance')
Expand Down
2 changes: 2 additions & 0 deletions client/src/main/java/com/jraska/github/client/AppComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.jraska.github.client.dynamicbase.DynamicFeaturesModule
import com.jraska.github.client.http.HttpComponent
import com.jraska.github.client.identity.IdentityModule
import com.jraska.github.client.identity.IdentityProvider
import com.jraska.github.client.networkstatus.NetworkStatusModule
import com.jraska.github.client.push.PushModule
import com.jraska.github.client.settings.entrance.SettingsEntranceModule
import com.jraska.github.client.shortcuts.ShortcutsModule
Expand All @@ -25,6 +26,7 @@ import dagger.Subcomponent
NavigationModule::class,
DynamicFeaturesModule::class,
IdentityModule::class,
NetworkStatusModule::class,
UsersModule::class,
PushModule::class,
SettingsEntranceModule::class,
Expand Down
1 change: 1 addition & 0 deletions lib/network-status/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
27 changes: 27 additions & 0 deletions lib/network-status/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 21
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
api project(':core')
api project(':core-android')

kapt 'com.google.dagger:dagger-compiler:2.24'
api 'com.google.dagger:dagger:2.24'

implementation 'com.google.android.material:material:1.1.0-beta01'

testImplementation 'junit:junit:4.12'
}
5 changes: 5 additions & 0 deletions lib/network-status/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jraska.github.client.networkstatus">

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.jraska.github.client.networkstatus

import android.app.Application
import com.jraska.github.client.core.android.OnAppCreate
import com.jraska.github.client.networkstatus.internal.NetworkStatusDisplayer
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet

@Module
object NetworkStatusModule {
@JvmStatic
@Provides
@IntoSet
internal fun addNetworkStatusDisplayer(networkStatusDisplayer: NetworkStatusDisplayer): OnAppCreate {
return object : OnAppCreate {
override fun onCreate(app: Application) {
val callbacks = NetworkStatusDisplayer.Callbacks(networkStatusDisplayer)
app.registerActivityLifecycleCallbacks(callbacks)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.jraska.github.client.networkstatus.internal

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkRequest
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
import javax.inject.Inject

internal class NetworkObservable @Inject constructor(
private val context: Context
) {
private val networkObservable: Observable<Boolean> by lazy {
setupNetworkObservable()
}

fun connectedObservable(): Observable<Boolean> {
return networkObservable
}

private val connectivityManager
get() = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

private fun setupNetworkObservable(): Observable<Boolean> {
val behaviorSubject = BehaviorSubject.createDefault(isConnected())

connectivityManager.registerNetworkCallback(allChangesRequest(), object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
behaviorSubject.onNext(isConnected())
}

override fun onLost(network: Network) {
behaviorSubject.onNext(isConnected())
}
})

return behaviorSubject.distinctUntilChanged()
}

private fun allChangesRequest() = NetworkRequest.Builder().build()

private fun isConnected(): Boolean {
val activeNetwork = connectivityManager.activeNetworkInfo
return activeNetwork != null && activeNetwork.isConnected
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.jraska.github.client.networkstatus.internal

import android.app.Activity
import android.view.View
import com.google.android.material.snackbar.Snackbar
import com.jraska.github.client.core.android.DefaultActivityCallbacks
import com.jraska.github.client.networkstatus.R
import com.jraska.github.client.rx.AppSchedulers
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject

internal class NetworkStatusDisplayer @Inject constructor(
private val networkObservable: NetworkObservable,
private val appSchedulers: AppSchedulers
) {

private val compositeDisposable = CompositeDisposable()
private var offlineSnackbar: Snackbar? = null

fun onActivityResumed(activity: Activity) {
networkObservable.connectedObservable()
.observeOn(appSchedulers.mainThread)
.subscribe { showState(activity, it) }
.also { compositeDisposable.add(it) }
}

fun onActivityPaused() {
dismissAnySnackbar()
compositeDisposable.clear()
}

private fun dismissAnySnackbar() {
offlineSnackbar?.dismiss()
offlineSnackbar = null
}

private fun showState(activity: Activity, isOnline: Boolean) {
if (isOnline) {
dismissAnySnackbar()
} else {
showOfflineSnackbar(activity)
}
}

private fun showOfflineSnackbar(activity: Activity) {
dismissAnySnackbar()

val view = activity.findViewById<View>(android.R.id.content)

val snackbar = Snackbar.make(view, R.string.status_is_offline, Snackbar.LENGTH_INDEFINITE)
snackbar.show()
offlineSnackbar = snackbar
}

internal class Callbacks(
private val displayer: NetworkStatusDisplayer
) : DefaultActivityCallbacks() {

override fun onActivityResumed(activity: Activity) {
displayer.onActivityResumed(activity)
}

override fun onActivityPaused(activity: Activity) {
displayer.onActivityPaused()
}
}
}
3 changes: 3 additions & 0 deletions lib/network-status/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<resources>
<string name="status_is_offline">You\'re offline</string>
</resources>
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include ':client',

':lib:identity',
':lib:dynamic-features',
':lib:network-status',

':feature:users',
':feature:push',
Expand Down

0 comments on commit 633f1b2

Please sign in to comment.