GithubHelp home page GithubHelp logo

amrro / firestore-android-arch-components Goto Github PK

View Code? Open in Web Editor NEW
106.0 6.0 31.0 1.77 MB

Firestore sample with Android architecture component.

License: Apache License 2.0

Java 100.00%
cloud-firestore android android-architecture-component firebase-auth setup-android mvvm-architecture

firestore-android-arch-components's Introduction

Cloud Firestore Quickstart

Introduction

Fire Eats is a restaurant recommendation app built on Cloud Firestore alongside Android Architecture Component.

After reading about the new Android Architecture Component's guide to architect your app. I converted the orignal sample presented by firebase to use the component with respect due architecture recommendation in the guide.

For more information about Firestore visit the docs. For more information about Android Architecture Components visit the docs.

Getting Started

Security Rules

Add the following security rules to your project in the: rules tab:

service cloud.firestore {  
  match /databases/{database}/documents {
    // Anyone can read a restaurant, only authorized
    // users can create, update, or delete them.
  	 match /restaurants/{restaurantId} {
    	 allow read: if true;
    	 allow create, update, delete: if request.auth.uid != null;
    }
    
    // Anyone can read a rating. Only the user who made the rating
    // can delete it. Ratings can never be updated.
    match /restaurants/{restaurantId}/ratings/{ratingId} {
    	 allow read: if true;
      allow create: if request.auth.uid != null;
    	 allow delete: if request.resource.data.userId == request.auth.uid;
    	 allow update: if false;
    }
  }
}

Run the App

  • When you open the app you will be prompted to sign in, choose any email and password.
  • When you first open the app it will be empty, choose Add Random Items from the overflow menu to add some new entries.

Result

Indexes

As you use the app's filter functionality you may see warnings in logcat that look like this:

com.google.firebase.example.fireeats W/Firestore Adapter: onEvent:error
com.google.firebase.firestore.FirebaseFirestoreException: FAILED_PRECONDITION: The query requires an index. You can create it here: https://console.firebase.google.com/project/...

This is because indexes are required for most compound queries in Cloud Firestore. Clicking on the link from the error message will automatically open the index creation UI in the Firebase console with the correct paramters filled in:

This app also provides an index specification file in indexes.json which specifies all indexes required to run the application. You can add all of these indexes programatically using the Firebase CLI.

firestore-android-arch-components's People

Contributors

ahmed-abdelmeged avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

firestore-android-arch-components's Issues

Inject ViewModelProvider.Factory

I get some error while injecting ViewModelProvider.Factory. How can I do it? Looks like injector doesn't know about it and asks me to provide it. Help somebody, pls

where is DetailHandler implemented?

This is not an issue. Its a question though. Let me know if I should remove this.

I would like to know that in "activity_restaurant_detail.xml" file, there are variables noReviews and handler.
Where is noReviews set in .java file.
Where is handler implemented?
Which code gets executed when "@{(view) -> handler.addRating(true)}" gets called?

I found addRating methods but they dont have this signature addRating(boolean val).

Thanks.

Unit Tests

Unit tests for repository classes and other parts of the application.

ViewModel does not persist data through lifecycle changes as expected.

Thanks for publishing this sample. I learned a lot from studying your code. However, I am puzzled about one detail.

One of the biggest benefits of using a viewmodel is that model data survives destruction and recreation of the activity during configuration changes. This makes it easier to restore for example the position of the list. Scroll down a long restaurant list, rotate the phone, and watch the list being recreated at the same position.

Currently, your restaurant list does not respect the previous scroll position on rotation. I've added onSaveInstanceState and onRestoreInstanceState to capture the state of the layout manager during rotation. (See code snippet below.) However, this alone is not enough since the adapter is still empty at the time onRestoreInstanceState is called.

The adapter is only populated later through restaurant.observe which was set up in MainActivity.onCreate. This observer is triggered only when the repository has retrieved a new copy of the (unchanged) restaurant list from the firebase query.

IMHO this defeats the purpose of the ViewModel class. This layer is supposed to maintain a link to the data that survives rotation changes etc. By delegating data handling to Firebase the data is recreated after each lifecycle change.

Any idea how to make the restaurant data reference persist through the life time of the viewmodel?

Cheers!

P.S.: Here is the code snipped I tried to capture the layout manager state

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    if (binding.recycler != null) {
        RecyclerView.LayoutManager manager = binding.recycler.getLayoutManager();
        if (manager != null) {
            Parcelable parcel = manager.onSaveInstanceState();
            outState.putParcelable(BUNDLE_LAYOUT, parcel);
        }
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    if (savedInstanceState != null) {
        // PROBLEM!! The adapter is not re-populated yet so the layout state is meaningless.
        Log.d("MainActivity", "current adapter size " + binding.recycler.getAdapter().getItemCount());
        
        RecyclerView.LayoutManager manager = binding.recycler.getLayoutManager();
        if (manager != null) {
            Parcelable savedRecyclerLayoutState = savedInstanceState.getParcelable(BUNDLE_LAYOUT);

            manager.onRestoreInstanceState(savedRecyclerLayoutState);
        }
    }
}

Invalid data. Unsupported type: com.google.firebase.firestore.QueryDocumentSnapshot

ImageDataModel.kt

class ImageDataModel @Inject constructor(private val firebaseFirestore: FirebaseFirestore) : ViewModel() {


var isLoading  = MutableLiveData<Boolean>()
var isPagerLoading  = MutableLiveData<Boolean>()

var apiError = MutableLiveData<String>()

var imgResponse = MutableLiveData<QuerySnapshot>()


fun getPhotosByOrder(orderBy: String){
    isLoading.value = true
    var query: Query
    query = firebaseFirestore.collection(DBConstant.PHOTO.tableNm)
        .orderBy(orderBy)
        .limit(12)
    query.get()
        .addOnSuccessListener { documentSnapshots ->
            // Get the last visible document
            if(documentSnapshots != null){
                isLoading.value = false
                imgResponse.value = documentSnapshots
            }else{
                isLoading.value = false
                apiError.value = Constant.SERVER_CONNECTION_ERROR
            }
        }
        .addOnFailureListener {
            isLoading.value = false
            apiError.value = it.message
        }

}

fun getPhotosByOrderPagination(orderBy: String, lastVisible: DocumentSnapshot?){
    isPagerLoading.value = true
    var query: Query
    query = firebaseFirestore.collection(DBConstant.PHOTO.tableNm)
        .orderBy(orderBy)
        .startAfter(lastVisible)
        .limit(6)

    query.get()
        .addOnSuccessListener { documentSnapshots ->
            // Get the last visible document
            if(documentSnapshots != null){
                isPagerLoading.value = false
                imgResponse.value = documentSnapshots
            }else{
                isPagerLoading.value = false
                apiError.value = Constant.SERVER_CONNECTION_ERROR
            }
        }
        .addOnFailureListener {
            isPagerLoading.value = false
            apiError.value = it.message
        }

}

}

Activity class:

  private var lastVisible: DocumentSnapshot?= null

  private fun bindObservers(){
    imageDataModel.isLoading.observe(this, androidx.lifecycle.Observer {
        if(it){
            llPbLoading.visibility = View.VISIBLE
            rvListFragBrowse.visibility = View.GONE
        }else{
            llPbLoading.visibility = View.GONE
            rvListFragBrowse.visibility = View.VISIBLE
        }
    })

    imageDataModel.apiError.observe(this, androidx.lifecycle.Observer {
        toast(it, activity!!)
    })

    imageDataModel.imgResponse.observe(this, androidx.lifecycle.Observer {
        photosList.clear()
        if(it.size() > 0){
            lastVisible = it.documents[it.size() - 1]
            Log.e("BrowseFrag","Last Item Name: "+lastVisible!!)
            for(item in it){
                var photoData = item.toObject(PhotosData::class.java)
                photoData.id = item.id
                photosList.add(photoData)
                mAdapterBrowse.notifyDataSetChanged()

            }
        }
    })

}

private fun getPhotosByOrder(orderBy: String){
    if(lastVisible == null){
        imageDataModel.getPhotosByOrder(orderBy)
    }else{
        imageDataModel.getPhotosByOrderPagination(orderBy, lastVisible)
    }
}

I am getting below error log:
java.lang.IllegalArgumentException: Invalid data. Unsupported type: com.google.firebase.firestore.QueryDocumentSnapshot at com.google.firebase.firestore.core.UserData$ParseContext.createError(com.google.firebase:firebase-firestore@@17.1.5:293) at com.google.firebase.firestore.UserDataConverter.parseScalarValue(com.google.firebase:firebase-firestore@@17.1.5:405) at com.google.firebase.firestore.UserDataConverter.parseData(com.google.firebase:firebase-firestore@@17.1.5:254) at com.google.firebase.firestore.UserDataConverter.parseQueryValue(com.google.firebase:firebase-firestore@@17.1.5:186) at com.google.firebase.firestore.Query.boundFromFields(com.google.firebase:firebase-firestore@@17.1.5:669) at com.google.firebase.firestore.Query.startAfter(com.google.firebase:firebase-firestore@@17.1.5:517) at com.firestoredemo.viewmodel.ImageDataModel.getPhotosByOrderPagination(ImageDataModel.kt:64) at com.firestoredemo.ui.fragment.BrowseFragment.getPhotosByOrder(BrowseFragment.kt:228) at com.firestoredemo.ui.fragment.BrowseFragment.access$getPhotosByOrder(BrowseFragment.kt:44) at com.firestoredemo.ui.fragment.BrowseFragment$setListener$2.onLoadMore(BrowseFragment.kt:153) at com.firestoredemo.adapter.BrowseResultAdapter$1.onScrolled(BrowseResultAdapter.kt:57)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.