Skip to content

Commit

Permalink
Added network connection verification
Browse files Browse the repository at this point in the history
Added message when there is no network connection
and user tries to use the app.
  • Loading branch information
Vitor Poncell committed Dec 17, 2019
1 parent d5aaee8 commit 3adb5b8
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Comparator;
import java.util.List;

import br.org.cesar.discordtime.stickysessions.data.remote.wrapper.INetworkWrapper;
import br.org.cesar.discordtime.stickysessions.domain.model.Meeting;
import br.org.cesar.discordtime.stickysessions.executor.IObservableUseCase;
import br.org.cesar.discordtime.stickysessions.navigation.router.IRouter;
Expand All @@ -18,8 +19,8 @@ public class MeetingPresenterModule {
@Provides
public MeetingContract.Presenter providesMeetingPresenter(
IObservableUseCase<Comparator<Meeting>, List<Meeting>> listMeetings,
IRouter router, IBundleFactory bundleFactory
IRouter router, INetworkWrapper networkWrapper, IBundleFactory bundleFactory
) {
return new MeetingPresenter(listMeetings, router, bundleFactory);
return new MeetingPresenter(listMeetings, router, networkWrapper, bundleFactory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface MeetingContract {
fun detachView()
fun onResume()
fun enterOnMeeting(meetingItem: MeetingItem)
fun onRetryNetworkClick()
}

interface View {
Expand All @@ -21,5 +22,9 @@ interface MeetingContract {
fun getName(): String
@Throws(InvalidViewNameException::class)
fun goNext(route: Route, bundle: IBundle)
fun isMeetingsEmpty(): Boolean
fun showNetworkError()
fun hideNetworkError()
fun showNetworkErrorIcon()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package br.org.cesar.discordtime.stickysessions.presentation.meeting

import br.org.cesar.discordtime.stickysessions.data.remote.wrapper.INetworkWrapper
import br.org.cesar.discordtime.stickysessions.domain.model.Meeting
import br.org.cesar.discordtime.stickysessions.executor.IObservableUseCase
import br.org.cesar.discordtime.stickysessions.navigation.router.IRouter
Expand All @@ -12,6 +13,7 @@ import java.util.*
class MeetingPresenter(
private val listMeetings: IObservableUseCase<Comparator<Meeting>, MutableList<Meeting>>,
private val router: IRouter,
private val networkWrapper: INetworkWrapper,
private val bundleFactory: IBundleFactory)
: MeetingContract.Presenter {

Expand All @@ -20,17 +22,14 @@ class MeetingPresenter(
override fun attachView(view: MeetingContract.View) {
mView = view
}

override fun detachView() {
mView = null
listMeetings.dispose()
}

override fun onResume() {
mView?.let { view ->
view.startLoadingMeetings()
listMeetings.execute(
MeetingsObserver(),
MeetingsComparator())
}
loadMeetings()
}

override fun enterOnMeeting(meetingItem: MeetingItem) {
Expand All @@ -42,6 +41,10 @@ class MeetingPresenter(
}
}

override fun onRetryNetworkClick() {
loadMeetings()
}

private inner class MeetingsComparator: Comparator<Meeting> {
// descending order m2 > m1
override fun compare(m1: Meeting, m2: Meeting): Int {
Expand Down Expand Up @@ -91,4 +94,19 @@ class MeetingPresenter(
numOfParticipants = meeting.participants.size
)
}

private fun loadMeetings() {
when {
networkWrapper.isConnected -> mView?.let { view ->
view.hideNetworkError()
view.startLoadingMeetings()
listMeetings.execute(
MeetingsObserver(),
MeetingsComparator())
}
mView?.isMeetingsEmpty()!! -> mView?.showNetworkError()
else -> mView?.showNetworkErrorIcon()
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
Expand All @@ -30,13 +31,15 @@ class MeetingActivity : AppCompatActivity(), MeetingContract.View {
lateinit var mViewStarter: IViewStarter
lateinit var mProgressBar: ProgressBar
lateinit var mAdapter: MeetingItemAdapter
lateinit var mNoNetworkContainer: View

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_meeting)
(application as StickySessionApplication).inject(this)

mProgressBar = findViewById(R.id.progressbar)
mNoNetworkContainer = findViewById(R.id.no_network_container)
configureToolbar()
configureRecycleView()
}
Expand All @@ -47,6 +50,9 @@ class MeetingActivity : AppCompatActivity(), MeetingContract.View {

val toolbarTitle = findViewById<TextView>(R.id.toolbar_title)
toolbarTitle.setText(R.string.toolbar_title_meetings)

val imageView = toolbar.findViewById<ImageView>(R.id.iv_no_network)
imageView.setOnClickListener { view -> onRetryClick(view) }
}

@SuppressLint("CheckResult")
Expand Down Expand Up @@ -103,6 +109,32 @@ class MeetingActivity : AppCompatActivity(), MeetingContract.View {
mViewStarter.goNext(this, route.to, route.shouldClearStack, bundle)
}

override fun isMeetingsEmpty(): Boolean {
return mAdapter.meetingItems.isEmpty()
}

override fun showNetworkError() {
mNoNetworkContainer.visibility = View.VISIBLE
}

override fun showNetworkErrorIcon() {
val toolbar = findViewById<Toolbar>(R.id.toolbar)
val imageView = toolbar.findViewById<ImageView>(R.id.iv_no_network)
imageView.visibility = View.VISIBLE
}

override fun hideNetworkError() {
mNoNetworkContainer.visibility = View.GONE

val toolbar = findViewById<Toolbar>(R.id.toolbar)
val imageView = toolbar.findViewById<ImageView>(R.id.iv_no_network)
imageView.visibility = View.GONE
}

fun onRetryClick(view: View) {
mPresenter.onRetryNetworkClick()
}

companion object {
const val TAG = "MeetingActivity"
}
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_cloud_off_black_24dp.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z"/>
</vector>
50 changes: 50 additions & 0 deletions app/src/main/res/layout/activity_meeting.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,54 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/no_network_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar">

<ImageView
android:id="@+id/iv_no_network"
android:layout_width="@dimen/ic_no_network_size"
android:layout_height="@dimen/ic_no_network_size"
android:scaleType="fitXY"
android:src="@drawable/ic_cloud_off_black_24dp"
android:tint="@color/warm_grey"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tv_no_network"
app:layout_constraintHorizontal_chainStyle="packed"
android:contentDescription="@string/no_network_content_description"/>

<TextView
android:id="@+id/tv_no_network"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/network_error_description"
android:textColor="@color/warm_grey"
android:textSize="@dimen/no_network_text_size"
android:layout_marginStart="@dimen/no_network_margin"
app:layout_constraintBottom_toBottomOf="@id/iv_no_network"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@id/iv_no_network"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintVertical_chainStyle="packed" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/no_network_margin"
android:onClick="onRetryClick"
android:text="@string/retry"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/iv_no_network" />

</androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
13 changes: 13 additions & 0 deletions app/src/main/res/layout/toolbar.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,18 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="Toolbar" />

<ImageView
android:id="@+id/iv_no_network"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginEnd="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:src="@drawable/ic_cloud_off_black_24dp"
android:tint="@color/warm_grey"
android:visibility="gone"
android:contentDescription="@string/no_network_content_description" />

</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.Toolbar>
3 changes: 3 additions & 0 deletions app/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="card_session_height">147dp</dimen>
<dimen name="ic_no_network_size">50sp</dimen>
<dimen name="no_network_text_size">20sp</dimen>
<dimen name="no_network_margin">20sp</dimen>
</resources>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@
<string name="text_meeting_date_format">%s %s</string>
<string name="text_meeting_recents_section">Recents</string>
<string name="text_meeting_older_section">Older</string>
<string name="network_error_description">No network connection</string>
<string name="no_network_content_description">There is no network connection</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package br.org.cesar.discordtime.stickysessions.injectors.components

import br.org.cesar.discordtime.stickysessions.injectors.modules.MeetingPresenterModule
import br.org.cesar.discordtime.stickysessions.injectors.modules.TestHttpModule
import br.org.cesar.discordtime.stickysessions.injectors.modules.TestMeetingModule
import br.org.cesar.discordtime.stickysessions.injectors.modules.TestNavigationModule
import br.org.cesar.discordtime.stickysessions.injectors.modules.TestServerModule
import br.org.cesar.discordtime.stickysessions.injectors.modules.TestSessionModule
import br.org.cesar.discordtime.stickysessions.injectors.modules.*
import br.org.cesar.discordtime.stickysessions.presentation.meeting.MeetingPresenterTest
import dagger.Component
import javax.inject.Singleton

@Singleton
@Component(modules = [
TestMeetingModule::class,
MeetingPresenterModule::class,
TestSessionModule::class,
TestServerModule::class,
TestNavigationModule::class,
TestHttpModule::class
TestHttpModule::class,
TestNetworkModule::class
])
interface TestMeetingComponent {
fun inject(integrationTest: MeetingPresenterTest)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package br.org.cesar.discordtime.stickysessions.injectors.modules

import br.org.cesar.discordtime.stickysessions.data.remote.wrapper.INetworkWrapper
import com.nhaarman.mockito_kotlin.mock
import dagger.Module
import dagger.Provides
import javax.inject.Singleton

@Module
class TestNetworkModule {

@Provides
@Singleton
fun provideNetworkWrapper(): INetworkWrapper {
return mock()
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package br.org.cesar.discordtime.stickysessions.presentation.meeting

import br.org.cesar.discordtime.stickysessions.TestServerDispatcher
import br.org.cesar.discordtime.stickysessions.data.remote.wrapper.INetworkWrapper
import br.org.cesar.discordtime.stickysessions.factory.MeetingItemFactory
import br.org.cesar.discordtime.stickysessions.injectors.components.DaggerTestMeetingComponent
import br.org.cesar.discordtime.stickysessions.injectors.components.TestMeetingComponent
Expand All @@ -20,6 +21,9 @@ class MeetingPresenterTest {
@Inject
lateinit var meetingPresenter: MeetingContract.Presenter

@Inject
lateinit var networkWrapper: INetworkWrapper

private lateinit var mockView: MeetingContract.View
private lateinit var webServer: MockWebServer

Expand All @@ -36,6 +40,8 @@ class MeetingPresenterTest {
webServer.start(8080)

captor = argumentCaptor()

whenever(networkWrapper.isConnected).thenReturn(true)
}

@After
Expand All @@ -60,6 +66,36 @@ class MeetingPresenterTest {
assertEquals(8, captor.firstValue.size)
}

@Test
fun `onResume with network`() {
webServer.setDispatcher(TestServerDispatcher().RequestDispatcher())
meetingPresenter.attachView(mockView)
meetingPresenter.onResume()
verify(mockView, times(1)).hideNetworkError()
verify(mockView, times(1)).startLoadingMeetings()
}

@Test
fun `onResume without network and without meetings`() {
whenever(networkWrapper.isConnected).thenReturn(false)
whenever(mockView.isMeetingsEmpty()).thenReturn(true)
webServer.setDispatcher(TestServerDispatcher().RequestDispatcher())
meetingPresenter.attachView(mockView)
meetingPresenter.onResume()
verify(mockView, times(1)).showNetworkError()
}

@Test
fun `onResume without network and with meeting`() {
whenever(networkWrapper.isConnected).thenReturn(false)
whenever(mockView.isMeetingsEmpty()).thenReturn(false)
webServer.setDispatcher(TestServerDispatcher().RequestDispatcher())
meetingPresenter.attachView(mockView)
meetingPresenter.onResume()
verify(mockView, times(1)).showNetworkErrorIcon()
}


@Test
fun `onEnterMeeting should call goNext`(){
webServer.setDispatcher(TestServerDispatcher().RequestDispatcher())
Expand Down

0 comments on commit 3adb5b8

Please sign in to comment.