Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - Realtime Database Pagination #1603

Merged
merged 5 commits into from Mar 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Expand Up @@ -55,6 +55,12 @@
<activity
android:name=".database.realtime.RealtimeDbChatActivity"
android:label="@string/title_realtime_database_activity" />

<!-- Realtime database paging demo -->
<activity
android:name=".database.realtime.FirebaseDbPagingActivity"
android:label="@string/title_paging_database_activity" />

<activity
android:name=".database.realtime.RealtimeDbChatIndexActivity"
android:label="@string/title_realtime_database_activity" />
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/firebase/uidemo/ChooserActivity.java
Expand Up @@ -32,6 +32,7 @@
import com.firebase.uidemo.auth.AuthUiActivity;
import com.firebase.uidemo.database.firestore.FirestoreChatActivity;
import com.firebase.uidemo.database.firestore.FirestorePagingActivity;
import com.firebase.uidemo.database.realtime.FirebaseDbPagingActivity;
import com.firebase.uidemo.database.realtime.RealtimeDbChatActivity;
import com.firebase.uidemo.storage.ImageActivity;

Expand Down Expand Up @@ -71,6 +72,7 @@ private static class ActivityChooserAdapter
FirestoreChatActivity.class,
FirestorePagingActivity.class,
RealtimeDbChatActivity.class,
FirebaseDbPagingActivity.class,
ImageActivity.class,
};

Expand All @@ -80,6 +82,7 @@ private static class ActivityChooserAdapter
R.string.title_firestore_activity,
R.string.title_firestore_paging_activity,
R.string.title_realtime_database_activity,
R.string.title_paging_database_activity,
R.string.title_storage_activity
};

Expand All @@ -89,6 +92,7 @@ private static class ActivityChooserAdapter
R.string.desc_firestore,
R.string.desc_firestore_paging,
R.string.desc_realtime_database,
R.string.desc_paging_database,
R.string.desc_storage
};

Expand Down
@@ -0,0 +1,161 @@
package com.firebase.uidemo.database.realtime;

import android.arch.paging.PagedList;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.firebase.ui.database.paging.DatabasePagingOptions;
import com.firebase.ui.database.paging.FirebaseRecyclerPagingAdapter;
import com.firebase.ui.database.paging.LoadingState;
import com.firebase.uidemo.R;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;

import butterknife.BindView;
import butterknife.ButterKnife;

public class FirebaseDbPagingActivity extends AppCompatActivity {
samtstern marked this conversation as resolved.
Show resolved Hide resolved

private final String TAG = "PagingActivity";

@BindView(R.id.paging_recycler)
RecyclerView mRecycler;

@BindView(R.id.swipe_refresh_layout)
SwipeRefreshLayout mSwipeRefreshLayout;

private Query mQuery;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_database_paging);
ButterKnife.bind(this);

mQuery = FirebaseDatabase.getInstance().getReference().child("posts");

setUpAdapter();
}

private void setUpAdapter() {

//Initialize Paging Configurations
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(5)
.setPageSize(30)
.build();

//Initialize Firebase Paging Options
DatabasePagingOptions<Post> options = new DatabasePagingOptions.Builder<Post>()
.setLifecycleOwner(this)
.setQuery(mQuery, config, Post.class)
.build();

//Initializing Adapter
final FirebaseRecyclerPagingAdapter<Post, PostViewHolder> mAdapter =
new FirebaseRecyclerPagingAdapter<Post, PostViewHolder>(options) {
@NonNull
@Override
public PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_post, parent, false);
return new PostViewHolder(view);
}

@Override
protected void onBindViewHolder(@NonNull PostViewHolder holder,
int position,
@NonNull Post model) {
holder.bind(model);
}

@Override
protected void onLoadingStateChanged(@NonNull LoadingState state) {
switch (state) {
case LOADING_INITIAL:
case LOADING_MORE:
mSwipeRefreshLayout.setRefreshing(true);
break;

case LOADED:
mSwipeRefreshLayout.setRefreshing(false);
break;

case FINISHED:
mSwipeRefreshLayout.setRefreshing(false);
Toast.makeText(getApplicationContext(), getString(R.string.paging_finished_message), Toast.LENGTH_SHORT).show();
break;

case ERROR:
retry();
break;
}
}

@Override
protected void onError(DatabaseError databaseError) {
mSwipeRefreshLayout.setRefreshing(false);
Log.e(TAG, databaseError.getMessage());
}
};

mRecycler.setLayoutManager(new LinearLayoutManager(this));
mRecycler.setAdapter(mAdapter);

// Reload data on swipe
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//Reload Data
mAdapter.refresh();
}
});
}

public static class Post {

@Nullable public String title;
@Nullable public String body;

public Post(){}

public Post(@Nullable String title, @Nullable String body) {
this.title = title;
this.body = body;
}
}

public static class PostViewHolder extends RecyclerView.ViewHolder {

@BindView(R.id.textViewTitle)
TextView mTitleView;

@BindView(R.id.textViewBody)
TextView mBodyView;

PostViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}

void bind(@NonNull Post post) {
mTitleView.setText(post.title);
mBodyView.setText(post.body);
}
}

}
21 changes: 21 additions & 0 deletions app/src/main/res/layout/activity_database_paging.xml
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_refresh_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.firebase.uidemo.database.realtime.FirebaseDbPagingActivity">

<android.support.v7.widget.RecyclerView
android:id="@+id/paging_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/paging_loading"
android:clipToPadding="false"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
tools:listitem="@layout/item_post" />

</android.support.v4.widget.SwipeRefreshLayout>
47 changes: 47 additions & 0 deletions app/src/main/res/layout/item_post.xml
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">

<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="2dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="2dp"
android:clickable="true"
android:foreground="?android:selectableItemBackground"
app:cardElevation="2dp"
android:focusable="true">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">

<TextView
android:id="@+id/textViewTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/post_text_title"
android:textColor="#000000"
android:textSize="18sp"
android:textStyle="bold" />

<TextView
android:id="@+id/textViewBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="@string/post_text_body"
android:textColor="#000000"
android:textSize="15sp" />
</LinearLayout>
</android.support.v7.widget.CardView>


</LinearLayout>
5 changes: 5 additions & 0 deletions app/src/main/res/values/strings.xml
Expand Up @@ -6,13 +6,15 @@
<string name="title_firestore_activity">Cloud Firestore Demo</string>
<string name="title_firestore_paging_activity">Cloud Firestore Paging Demo</string>
<string name="title_realtime_database_activity">Real-time database demo</string>
<string name="title_paging_database_activity">Paging database demo</string>
<string name="title_storage_activity">Storage Image Demo</string>

<string name="desc_auth">Demonstrates the Firebase Auth UI flow, with customization options.</string>
<string name="desc_anonymous_upgrade">Demonstrates upgrading an anonymous account using FirebaseUI.</string>
<string name="desc_firestore">Demonstrates using a FirestoreRecyclerAdapter to load data from Cloud Firestore into a RecyclerView for a basic chat app.</string>
<string name="desc_firestore_paging">Demonstrates using a FirestorePagingAdapter to load/infinite scroll paged data from Cloud Firestore.</string>
<string name="desc_realtime_database">Demonstrates using a FirebaseRecyclerAdapter to load data from Firebase Database into a RecyclerView for a basic chat app.</string>
<string name="desc_paging_database">Demonstrates using a FirebaseRecyclerPagingAdapter to load/infinite scroll paged data from Firebase Database.</string>
<string name="desc_storage">Demonstrates displaying an image from Cloud Storage using Glide.</string>

<!-- Auth UI -->
Expand Down Expand Up @@ -118,6 +120,8 @@
<string name="start_chatting">No messages. Start chatting at the bottom!</string>
<string name="signing_in">Signing in…</string>
<string name="signed_in">Signed In</string>
<string name="post_text_title">Title</string>
<string name="post_text_body">Body</string>
<string name="anonymous_auth_failed_msg">
Anonymous authentication failed, various components of the demo will not work.
Make sure your device is online and that Anonymous Auth is configured in your Firebase project
Expand All @@ -126,4 +130,5 @@

<string name="menu_add_data">Add Data</string>
<string name="hint_message">Say something…</string>
<string name="paging_finished_message">Reached End of List</string>
</resources>
2 changes: 1 addition & 1 deletion database/README.md
Expand Up @@ -202,7 +202,7 @@ protected void onStop() {
If you don't want to manually start/stop listening you can use
[Android Architecture Components][arch-components] to automatically manage the lifecycle of the
`FirebaseRecyclerAdapter`. Pass a `LifecycleOwner` to
`FirebaseRecyclerAdapter.Builder#setLifecycleOwner(...)` and FirebaseUI will automatically
`FirebaseRecyclerOptions.Builder#setLifecycleOwner(...)` and FirebaseUI will automatically
start and stop listening in `onStart()` and `onStop()`.

### Data and error events
Expand Down
10 changes: 10 additions & 0 deletions database/build.gradle.kts
Expand Up @@ -4,6 +4,14 @@ android {
defaultConfig {
testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner"
}

buildTypes {
named("release").configure {
postprocessing {
consumerProguardFiles("proguard-rules.pro")
}
}
}
}

dependencies {
Expand All @@ -12,6 +20,8 @@ dependencies {

api(Config.Libs.Support.v4)
api(Config.Libs.Support.recyclerView)

compileOnly(Config.Libs.Arch.paging)
annotationProcessor(Config.Libs.Arch.compiler)

androidTestImplementation(Config.Libs.Test.junit)
Expand Down
1 change: 1 addition & 0 deletions database/proguard-rules.pro
@@ -0,0 +1 @@
-dontwarn com.firebase.ui.database.paging.**