Skip to content

Commit 1ea8834

Browse files
committed
Expose Loading State
Change-Id: Idaa7d98c4c4712354ff6a4ed10883f8dc78fa898
1 parent 754f945 commit 1ea8834

File tree

6 files changed

+167
-33
lines changed

6 files changed

+167
-33
lines changed

app/src/main/java/com/firebase/uidemo/database/firestore/FirestorePagingActivity.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.firebase.ui.firestore.paging.FirestorePagingAdapter;
2020
import com.firebase.ui.firestore.paging.FirestorePagingOptions;
21+
import com.firebase.ui.firestore.paging.LoadingState;
2122
import com.firebase.uidemo.R;
2223
import com.google.android.gms.tasks.OnCompleteListener;
2324
import com.google.android.gms.tasks.Task;
@@ -66,6 +67,7 @@ private void setUpAdapter() {
6667
.build();
6768

6869
FirestorePagingOptions<Item> options = new FirestorePagingOptions.Builder<Item>()
70+
.setOwner(this)
6971
.setQuery(baseQuery, config, Item.class)
7072
.build();
7173

@@ -86,9 +88,23 @@ protected void onBindViewHolder(@NonNull ItemViewHolder holder,
8688
Item model) {
8789
holder.bind(model);
8890
}
89-
};
9091

91-
// TODO: Expose loading state in the adapter
92+
@Override
93+
protected void onLoadingStateChanged(@NonNull LoadingState state) {
94+
switch (state) {
95+
case LOADING_INITIAL:
96+
case LOADING_MORE:
97+
mProgressBar.setVisibility(View.VISIBLE);
98+
break;
99+
case LOADED:
100+
mProgressBar.setVisibility(View.GONE);
101+
break;
102+
case ERROR:
103+
showToast("An error occurred.");
104+
break;
105+
}
106+
}
107+
};
92108

93109
mRecycler.setLayoutManager(new LinearLayoutManager(this));
94110
mRecycler.setAdapter(adapter);

firestore/src/main/java/com/firebase/ui/firestore/paging/FirestoreDataSource.java

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.firebase.ui.firestore.paging;
22

3+
import android.arch.lifecycle.LiveData;
4+
import android.arch.lifecycle.MutableLiveData;
35
import android.arch.paging.DataSource;
46
import android.arch.paging.PageKeyedDataSource;
57
import android.support.annotation.NonNull;
@@ -24,27 +26,43 @@ public class FirestoreDataSource extends PageKeyedDataSource<PageKey, DocumentSn
2426

2527
private static final String TAG = "FirestoreDataSource";
2628

29+
public static class Factory extends DataSource.Factory<PageKey, DocumentSnapshot> {
30+
31+
private final Query mQuery;
32+
private final MutableLiveData<FirestoreDataSource> mDataSource = new MutableLiveData<>();
33+
34+
public Factory(Query query) {
35+
mQuery = query;
36+
}
37+
38+
@Override
39+
public DataSource<PageKey, DocumentSnapshot> create() {
40+
FirestoreDataSource source = new FirestoreDataSource(mQuery);
41+
mDataSource.postValue(source);
42+
return source;
43+
}
44+
45+
public LiveData<FirestoreDataSource> getDataSource() {
46+
return mDataSource;
47+
}
48+
}
49+
50+
private final MutableLiveData<LoadingState> mLoadingState = new MutableLiveData<>();
51+
2752
private final Query mBaseQuery;
2853

2954
public FirestoreDataSource(Query baseQuery) {
3055
mBaseQuery = baseQuery;
3156
}
3257

33-
@NonNull
34-
public static DataSource.Factory<PageKey, DocumentSnapshot> newFactory(final Query query) {
35-
return new DataSource.Factory<PageKey, DocumentSnapshot>() {
36-
@Override
37-
public DataSource<PageKey, DocumentSnapshot> create() {
38-
return new FirestoreDataSource(query);
39-
}
40-
};
41-
}
42-
4358
@Override
4459
public void loadInitial(@NonNull LoadInitialParams<PageKey> params,
4560
@NonNull final LoadInitialCallback<PageKey, DocumentSnapshot> callback) {
46-
4761
Log.d(TAG, "loadInitial: " + params.requestedLoadSize);
62+
63+
// Set initial loading state
64+
mLoadingState.postValue(LoadingState.LOADING_INITIAL);
65+
4866
mBaseQuery.limit(params.requestedLoadSize)
4967
.get()
5068
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@@ -55,6 +73,8 @@ public void onSuccess(QuerySnapshot snapshots) {
5573

5674
PageKey nextPage = new PageKey(last, null);
5775
callback.onResult(data, null, nextPage);
76+
77+
mLoadingState.postValue(LoadingState.LOADED);
5878
}
5979
})
6080
.addOnFailureListener(new OnFailureListener() {
@@ -67,6 +87,8 @@ public void onFailure(@NonNull Exception e) {
6787
PageKey nextPage = new PageKey(null, null);
6888
callback.onResult(Collections.<DocumentSnapshot>emptyList(),
6989
null, nextPage);
90+
91+
mLoadingState.postValue(LoadingState.ERROR);
7092
}
7193
});
7294

@@ -75,9 +97,11 @@ public void onFailure(@NonNull Exception e) {
7597
@Override
7698
public void loadBefore(@NonNull LoadParams<PageKey> params,
7799
@NonNull LoadCallback<PageKey, DocumentSnapshot> callback) {
78-
PageKey key = params.key;
79-
Log.d(TAG, "loadBefore: " + key + ", " + params.requestedLoadSize);
80-
// TODO: Do I need the reverse query here?
100+
// Ignored for now, since we only ever append to the initial load.
101+
// Future work:
102+
// * Could we dynamically unload past pages?
103+
// * Could we ask the developer for both a forward and reverse base query
104+
// so that we can load backwards easily?
81105
}
82106

83107
@Override
@@ -86,6 +110,9 @@ public void loadAfter(@NonNull LoadParams<PageKey> params,
86110
final PageKey key = params.key;
87111
Log.d(TAG, "loadAfter: " + key + ", " + params.requestedLoadSize);
88112

113+
// Set loading state
114+
mLoadingState.postValue(LoadingState.LOADING_MORE);
115+
89116
key.getPageQuery(mBaseQuery, params.requestedLoadSize)
90117
.get()
91118
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
@@ -96,6 +123,8 @@ public void onSuccess(QuerySnapshot snapshots) {
96123

97124
PageKey nextPage = new PageKey(last, null);
98125
callback.onResult(data, nextPage);
126+
127+
mLoadingState.postValue(LoadingState.LOADED);
99128
}
100129
})
101130
.addOnFailureListener(new OnFailureListener() {
@@ -106,11 +135,17 @@ public void onFailure(@NonNull Exception e) {
106135
// On error, return an empty page with the next page key being basically
107136
// equal to the initial query.
108137
callback.onResult(Collections.<DocumentSnapshot>emptyList(), key);
138+
139+
mLoadingState.postValue(LoadingState.ERROR);
109140
}
110141
});
111142

112143
}
113144

145+
public LiveData<LoadingState> getLoadingState() {
146+
return mLoadingState;
147+
}
148+
114149
@Nullable
115150
private DocumentSnapshot getLast(List<DocumentSnapshot> data) {
116151
if (data == null || data.isEmpty()) {

firestore/src/main/java/com/firebase/ui/firestore/paging/FirestorePagingAdapter.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.arch.lifecycle.Lifecycle;
44
import android.arch.lifecycle.LifecycleObserver;
5-
import android.arch.lifecycle.LiveData;
65
import android.arch.lifecycle.Observer;
76
import android.arch.lifecycle.OnLifecycleEvent;
87
import android.arch.paging.PagedList;
@@ -26,9 +25,21 @@ public abstract class FirestorePagingAdapter<T, VH extends RecyclerView.ViewHold
2625
implements LifecycleObserver {
2726

2827
private final SnapshotParser<T> mParser;
29-
private final LiveData<PagedList<DocumentSnapshot>> mData;
28+
private final PagingData mData;
3029

31-
private final Observer<PagedList<DocumentSnapshot>> mObserver =
30+
private final Observer<LoadingState> mStateObserver =
31+
new Observer<LoadingState>() {
32+
@Override
33+
public void onChanged(@Nullable LoadingState state) {
34+
if (state == null) {
35+
return;
36+
}
37+
38+
onLoadingStateChanged(state);
39+
}
40+
};
41+
42+
private final Observer<PagedList<DocumentSnapshot>> mDataObserver =
3243
new Observer<PagedList<DocumentSnapshot>>() {
3344
@Override
3445
public void onChanged(@Nullable PagedList<DocumentSnapshot> snapshots) {
@@ -75,12 +86,14 @@ public FirestorePagingAdapter(@NonNull FirestorePagingOptions<T> options) {
7586

7687
@OnLifecycleEvent(Lifecycle.Event.ON_START)
7788
public void startListening() {
78-
mData.observeForever(mObserver);
89+
mData.getSnapshots().observeForever(mDataObserver);
90+
mData.getLoadingState().observeForever(mStateObserver);
7991
}
8092

8193
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
8294
public void stopListening() {
83-
mData.removeObserver(mObserver);
95+
mData.getSnapshots().removeObserver(mDataObserver);
96+
mData.getLoadingState().removeObserver(mStateObserver);
8497
}
8598

8699
@NonNull
@@ -95,6 +108,10 @@ public void onBindViewHolder(@NonNull VH holder, int position) {
95108

96109
protected abstract void onBindViewHolder(@NonNull VH holder, int position, T model);
97110

111+
protected void onLoadingStateChanged(@NonNull LoadingState state) {
112+
// For overriding
113+
}
114+
98115
private void onListChanged(@NonNull PagedList<DocumentSnapshot> snapshots) {
99116
submitList(snapshots);
100117
}

firestore/src/main/java/com/firebase/ui/firestore/paging/FirestorePagingOptions.java

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,33 @@
11
package com.firebase.ui.firestore.paging;
22

33
import android.arch.lifecycle.LifecycleOwner;
4-
import android.arch.lifecycle.LiveData;
5-
import android.arch.paging.LivePagedListBuilder;
64
import android.arch.paging.PagedList;
75
import android.support.annotation.NonNull;
86
import android.support.annotation.Nullable;
97

108
import com.firebase.ui.firestore.ClassSnapshotParser;
119
import com.firebase.ui.firestore.SnapshotParser;
12-
import com.google.firebase.firestore.DocumentSnapshot;
1310
import com.google.firebase.firestore.Query;
1411

1512
/**
1613
* Options to conifigure an {@link FirestorePagingAdapter}.
1714
*/
1815
public class FirestorePagingOptions<T> {
1916

20-
private final LiveData<PagedList<DocumentSnapshot>> mData;
17+
private final PagingData mData;
2118
private final SnapshotParser<T> mParser;
2219
private final LifecycleOwner mOwner;
2320

24-
private FirestorePagingOptions(@NonNull LiveData<PagedList<DocumentSnapshot>> data,
25-
@NonNull SnapshotParser<T> parser,
26-
@Nullable LifecycleOwner owner) {
21+
private FirestorePagingOptions(@NonNull PagingData data,
22+
@NonNull SnapshotParser<T> parser,
23+
@Nullable LifecycleOwner owner) {
2724
mData = data;
2825
mParser = parser;
2926
mOwner = owner;
3027
}
3128

3229
@NonNull
33-
public LiveData<PagedList<DocumentSnapshot>> getData() {
30+
public PagingData getData() {
3431
return mData;
3532
}
3633

@@ -46,7 +43,7 @@ public LifecycleOwner getOwner() {
4643

4744
public static class Builder<T> {
4845

49-
private LiveData<PagedList<DocumentSnapshot>> mData;
46+
private PagingData mData;
5047
private SnapshotParser<T> mParser;
5148
private LifecycleOwner mOwner;
5249

@@ -62,10 +59,10 @@ public Builder<T> setQuery(@NonNull Query query,
6259
@NonNull PagedList.Config config,
6360
@NonNull SnapshotParser<T> parser) {
6461

62+
6563
// Build paged list
66-
mData = new LivePagedListBuilder<>(
67-
FirestoreDataSource.newFactory(query),
68-
config).build();
64+
FirestoreDataSource.Factory factory = new FirestoreDataSource.Factory(query);
65+
mData = new PagingData(factory, config);
6966

7067
mParser = parser;
7168
return this;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.firebase.ui.firestore.paging;
2+
3+
/**
4+
* Loading state exposed by {@link FirestorePagingAdapter}.
5+
*/
6+
public enum LoadingState {
7+
/**
8+
* Loading initial data.Pag
9+
*/
10+
LOADING_INITIAL,
11+
12+
/**
13+
* Loading a page other than the first page.
14+
*/
15+
LOADING_MORE,
16+
17+
/**
18+
* Not currently loading any pages, at least one page loaded.
19+
*/
20+
LOADED,
21+
22+
/**
23+
* The most recent load encountered an error.
24+
*/
25+
ERROR
26+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.firebase.ui.firestore.paging;
2+
3+
import android.arch.core.util.Function;
4+
import android.arch.lifecycle.LiveData;
5+
import android.arch.lifecycle.Transformations;
6+
import android.arch.paging.LivePagedListBuilder;
7+
import android.arch.paging.PagedList;
8+
import android.support.annotation.RestrictTo;
9+
10+
import com.google.firebase.firestore.DocumentSnapshot;
11+
12+
/**
13+
* All of the data the adapter needs.
14+
*/
15+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
16+
public class PagingData {
17+
18+
private final LiveData<PagedList<DocumentSnapshot>> mSnapshots;
19+
private final LiveData<LoadingState> mLoadingState;
20+
21+
public PagingData(FirestoreDataSource.Factory factory,
22+
PagedList.Config config) {
23+
24+
mSnapshots = new LivePagedListBuilder<>(factory, config).build();
25+
26+
mLoadingState = Transformations.switchMap(factory.getDataSource(),
27+
new Function<FirestoreDataSource, LiveData<LoadingState>>() {
28+
@Override
29+
public LiveData<LoadingState> apply(FirestoreDataSource input) {
30+
return input.getLoadingState();
31+
}
32+
});
33+
}
34+
35+
public LiveData<PagedList<DocumentSnapshot>> getSnapshots() {
36+
return mSnapshots;
37+
}
38+
39+
public LiveData<LoadingState> getLoadingState() {
40+
return mLoadingState;
41+
}
42+
43+
}

0 commit comments

Comments
 (0)