Skip to content
This repository was archived by the owner on Dec 14, 2021. It is now read-only.

Commit ef44428

Browse files
authored
[WIP] Pull to refresh - Manual sync (#310)
* Pull to refresh - Manual sync * Added changes after code review
1 parent 55acd9b commit ef44428

9 files changed

Lines changed: 79 additions & 8 deletions

File tree

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ dependencies {
7373
implementation "com.jakewharton.rxbinding2:rxbinding-design-kotlin:${rxbinding_version}"
7474
implementation "com.jakewharton.rxbinding2:rxbinding-appcompat-v7-kotlin:${rxbinding_version}"
7575
implementation "com.jakewharton.rxbinding2:rxbinding-recyclerview-v7-kotlin:${rxbinding_version}"
76+
implementation "com.jakewharton.rxbinding2:rxbinding-support-v4-kotlin:${rxbinding_version}"
7677
implementation 'com.jakewharton.rxrelay2:rxrelay:2.0.0'
7778
implementation 'com.f2prateek.rx.preferences2:rx-preferences:2.0.0'
7879
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'

app/src/androidTest/java/mozilla/lockbox/ItemListTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,11 @@ open class ItemListTest {
4444
itemList { selectFirstItemInSortMenu() }
4545
itemList { spinnerDisplaysFirstItemSelection() }
4646
}
47+
48+
@Test
49+
fun testPullToRefresh() {
50+
navigator.gotoItemList(false)
51+
itemList { pullToRefresh() }
52+
navigator.checkAtItemList()
53+
}
4754
}

app/src/androidTest/java/mozilla/lockbox/robots/BaseTestRobot.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import android.support.test.espresso.NoActivityResumedException
1212
import android.support.test.espresso.ViewInteraction
1313
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
1414
import android.support.test.espresso.action.ViewActions.replaceText
15+
import android.support.test.espresso.action.ViewActions.swipeDown
1516
import android.support.test.espresso.assertion.ViewAssertions.matches
1617
import android.support.test.espresso.matcher.ViewMatchers.withId
1718
import android.support.test.espresso.matcher.ViewMatchers.withText
@@ -37,6 +38,9 @@ interface BaseTestRobot {
3738
}
3839
}
3940

41+
fun swipeDown(resId: Int): ViewInteraction =
42+
onView(withId(resId)).perform(swipeDown())
43+
4044
fun back(remainInApplication: Boolean = true) {
4145
Espresso.closeSoftKeyboard()
4246
try {

app/src/androidTest/java/mozilla/lockbox/robots/ItemListRobot.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import android.support.test.espresso.contrib.NavigationViewActions.navigateTo
1313
import android.support.test.espresso.matcher.ViewMatchers.withId
1414
import br.com.concretesolutions.kappuccino.actions.ClickActions.click
1515
import br.com.concretesolutions.kappuccino.assertions.VisibilityAssertions.displayed
16+
import kotlinx.android.synthetic.main.fragment_item_list.view.*
1617
import mozilla.lockbox.R
1718

1819
// ItemList
@@ -61,6 +62,8 @@ class ItemListRobot : BaseTestRobot {
6162
fun tapAccountSetting() = menuOption(R.id.account_setting_menu_item)
6263

6364
fun selectItem(position: Int = 0) = clickListItem(R.id.entriesView, position)
65+
66+
fun pullToRefresh() = swipeDown(R.id.entriesView)
6467
}
6568

6669
fun itemList(f: ItemListRobot.() -> Unit) = ItemListRobot().apply(f)

app/src/main/java/mozilla/lockbox/presenter/ItemListPresenter.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ interface ItemListView {
4040
fun updateAccountProfile(profile: AccountViewModel)
4141
fun updateItemListSort(sort: Setting.ItemListSort)
4242
fun loading(isLoading: Boolean)
43+
val refreshItemList: Observable<Unit>
44+
val isRefreshing: Boolean
45+
fun stopRefreshing()
4346
}
4447

4548
@ExperimentalCoroutinesApi
@@ -62,7 +65,14 @@ class ItemListPresenter(
6265
DataStore.SyncState.NotSyncing -> false
6366
}
6467
}
65-
.subscribe(view::loading)
68+
.subscribe { syncing ->
69+
when (view.isRefreshing) {
70+
true -> if (!syncing) {
71+
view.stopRefreshing()
72+
}
73+
false -> view.loading(syncing)
74+
}
75+
}
6676
.addTo(compositeDisposable)
6777

6878
Observables.combineLatest(dataStore.list, settingStore.itemListSortOrder)
@@ -115,6 +125,11 @@ class ItemListPresenter(
115125
dispatcher.dispatch(SettingAction.ItemListSortOrder(sortBy))
116126
}.addTo(compositeDisposable)
117127

128+
view.refreshItemList
129+
.doOnDispose { view.stopRefreshing() }
130+
.subscribe { dispatcher.dispatch(DataStoreAction.Sync) }
131+
.addTo(compositeDisposable)
132+
118133
accountStore.profile
119134
.filterNotNull()
120135
.map {

app/src/main/java/mozilla/lockbox/view/ItemListFragment.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import android.widget.AdapterView
2121
import androidx.navigation.NavController
2222
import androidx.navigation.findNavController
2323
import androidx.navigation.ui.setupWithNavController
24-
import com.jakewharton.rxbinding2.support.design.widget.itemSelections
2524
import android.widget.Spinner
25+
import com.jakewharton.rxbinding2.support.design.widget.itemSelections
26+
import com.jakewharton.rxbinding2.support.v4.widget.refreshes
2627
import com.jakewharton.rxbinding2.support.v7.widget.navigationClicks
2728
import com.jakewharton.rxbinding2.view.clicks
2829
import com.squareup.picasso.Picasso
@@ -71,6 +72,7 @@ class ItemListFragment : CommonFragment(), ItemListView {
7172
setupNavigationView(navController, view.navView)
7273
setupListView(view.entriesView)
7374
setupSpinner(view)
75+
view.refreshContainer.setColorSchemeResources(R.color.refresh_blue)
7476
super.onViewCreated(view, savedInstanceState)
7577
}
7678

@@ -197,4 +199,10 @@ class ItemListFragment : CommonFragment(), ItemListView {
197199
view!!.filterButton.isEnabled = !isLoading
198200
view!!.sortButton.isClickable = !isLoading
199201
}
200-
}
202+
203+
override val refreshItemList: Observable<Unit> get() = view!!.refreshContainer.refreshes()
204+
override val isRefreshing: Boolean get() = view!!.refreshContainer.isRefreshing
205+
override fun stopRefreshing() {
206+
view!!.refreshContainer.isRefreshing = false
207+
}
208+
}

app/src/main/res/layout/fragment_item_list.xml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<?xml version="1.0" encoding="utf-8"?><!--
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
23
~ This Source Code Form is subject to the terms of the Mozilla Public
34
~ License, v. 2.0. If a copy of the MPL was not distributed with this
45
~ file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -55,10 +56,17 @@
5556
</LinearLayout>
5657
</android.support.v7.widget.Toolbar>
5758

58-
<android.support.v7.widget.RecyclerView
59-
android:id="@+id/entriesView"
59+
<android.support.v4.widget.SwipeRefreshLayout
60+
xmlns:android="http://schemas.android.com/apk/res/android"
61+
android:id="@+id/refreshContainer"
6062
android:layout_width="match_parent"
61-
android:layout_height="match_parent" />
63+
android:layout_height="match_parent">
64+
65+
<android.support.v7.widget.RecyclerView
66+
android:id="@+id/entriesView"
67+
android:layout_width="match_parent"
68+
android:layout_height="match_parent" />
69+
</android.support.v4.widget.SwipeRefreshLayout>
6270

6371
<RelativeLayout
6472
android:id="@+id/loadingView"

app/src/main/res/values/colors.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@
2727
<color name="green">#12bc00</color>
2828
<color name="dark_grey">#4a4a4f</color>
2929
<color name="selection">#14000000</color>
30+
<color name="refresh_blue">#0e89c9</color>
3031
</resources>

app/src/test/java/mozilla/lockbox/presenter/ItemListPresenterTest.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import org.junit.Before
3535
import org.junit.Test
3636
import org.junit.runner.RunWith
3737
import org.mockito.Mock
38+
import org.mockito.Mockito.spy
39+
import org.mockito.Mockito.verify
3840
import org.powermock.api.mockito.PowerMockito
3941
import org.powermock.core.classloader.annotations.PrepareForTest
4042
import org.robolectric.RobolectricTestRunner
@@ -94,6 +96,7 @@ open class ItemListPresenterTest {
9496
val sortItemSelectionStub = PublishSubject.create<Setting.ItemListSort>()
9597
val disclaimerActionStub = PublishSubject.create<AlertState>()
9698
val lockNowSelectionStub = PublishSubject.create<Unit>()
99+
val refreshItemListStub = PublishSubject.create<Unit>()
97100

98101
override val itemSelection: Observable<ItemViewModel>
99102
get() = itemSelectedStub
@@ -129,6 +132,14 @@ open class ItemListPresenterTest {
129132
override fun loading(isLoading: Boolean) {
130133
this.isLoading = isLoading
131134
}
135+
136+
override val refreshItemList: Observable<Unit>
137+
get() = refreshItemListStub
138+
139+
override val isRefreshing: Boolean = false
140+
141+
override fun stopRefreshing() {
142+
}
132143
}
133144

134145
class FakeDataStore : DataStore() {
@@ -150,7 +161,7 @@ open class ItemListPresenterTest {
150161
private val settingStore = FakeSettingStore()
151162
private val profileStub = PublishSubject.create<Optional<Profile>>()
152163

153-
val view = FakeView()
164+
val view: FakeView = spy(FakeView())
154165
val dispatcher = Dispatcher()
155166
val subject = ItemListPresenter(view, dispatcher, dataStore, settingStore, fingerprintStore, accountStore)
156167

@@ -281,4 +292,17 @@ open class ItemListPresenterTest {
281292
dataStore.syncStateStub.onNext(DataStore.SyncState.NotSyncing)
282293
Assert.assertEquals(false, view.isLoading)
283294
}
295+
296+
@Test
297+
fun `stop refreshing when stop syncing after pull to refresh`() {
298+
whenCalled(view.isRefreshing).thenReturn(true)
299+
dataStore.syncStateStub.onNext(DataStore.SyncState.NotSyncing)
300+
verify(view).stopRefreshing()
301+
}
302+
303+
@Test
304+
fun `swipe down calls sync`() {
305+
view.refreshItemListStub.onNext(Unit)
306+
dispatcherObserver.assertLastValue(DataStoreAction.Sync)
307+
}
284308
}

0 commit comments

Comments
 (0)