ZOFTINO.COM android and web dev tutorials

Android Firebase Cloud Functions Realtime Database Trigger Example

In this post, you can learn how to use Firebase cloud functions which are run in response to realtime database events such as adding, updating and deleting nodes performed from android application.

If you are new to Firebase cloud functions, I suggest reading pervious post Firebase cloud functions tutorial with http-trigger cloud functions and android example to know about Firebase cloud functions, how to setup Firebase cloud function project and how to create http-trigger cloud functions.

As the examples uses realtime database and firebase authentication, please read Firestore realtime database tutorial and Firebase authentication tutorial. In these posts, you can also find information on how to secure data in realtime database using authentication and security rules.

Cloud Functions Realtime Database Trigger

To handle realtime database events in cloud functions, you need to write cloud function using functions.database object. First you need to get reference to the realtime database node where the function will listen for events and then specify one of the event handlers that functions.database object provides such as onCreate, onUpdate, onDelete and onWrite.

When data is added, updated or deleted at the target node, the function will be called passing the event data which contains previous data and new data of the node at which the function listens for events.

Let’s see how Firebase function will be triggered by realtime database events using an android example app and example cloud function. The example app lets user to enter test scores and the data will be saved in realtime database. This will trigger our example Firebase cloud function which will calculate average score and update the database.

Our example app also allows user to search by student name and grade to view the average score and list of subjects and scores. There are edit score and delete score options which allow user to perform update and delete scores. These operations in the realtime database will trigger cloud functions to execute which will recalculate and update the average score.

Below are the android application screens. You can find cloud function code and android app code in the following sections.

Add screen.

firebase cloud functions realtime database add android example

Search, edit and delete screen.

firebase cloud functions realtime database edit and delete android example

Realtime Database Trigger Cloud Function

'use strict';

const functions = require('firebase-functions');


exports.addScoreAverage = functions.database.ref('/scores/{userId}/{grade}/{name}/{subscoreID}')
  .onWrite(event => {
        var subScoreSnap = event.data;	
	var avgScorUpd = subScoreSnap.ref.parent.once('value', function (snap) {
		var scoreAvg = 0;
		var counter = 0;
		var totalScore = 0;
 		snap.forEach(function (subScoreItem) {
			//console.log('values', subScoreItem.val()); 			
			var scoreVal = subScoreItem.child("score").val();
			console.log('score', scoreVal); 
			if(scoreVal){
			 totalScore = totalScore + parseInt(subScoreItem.child("score").val());
			 counter = counter + 1;
			}
			console.log('total', totalScore); 
 		});
		scoreAvg = Math.round(totalScore / counter);
        	console.log(`average score ${scoreAvg}`); 
        	return subScoreSnap.ref.parent.child('scoreAvg').set(scoreAvg);
	});
	return avgScorUpd;
});

Base Activity

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import zoftino.com.firestore.R;

public class BaseMarksActivity extends AppCompatActivity {
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.marks_menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.add_marks_m:
                addTestMark();
                return true;
            case R.id.view_marks_m:
                viewTestMarks();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
    public void addTestMark(){
        Intent i = new Intent();
        i.setClass(this, TestScoresActivity.class);
        startActivity(i);
    }
    public void viewTestMarks(){
        Intent i = new Intent();
        i.setClass(this, ViewScoresActivity.class);
        startActivity(i);
    }
}

Add Activity

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.util.HashMap;
import java.util.Map;

import zoftino.com.firestore.EmailPasswordAuthActivity;
import zoftino.com.firestore.R;

public class TestScoresActivity extends BaseMarksActivity{
    private static final String TAG = "TestScoresActivity";
    private DatabaseReference dbRef;
    private FirebaseUser user;
    private String recId = "";

    private EditText studentNameET;
    private EditText gradeET;
    private EditText subjectET;
    private EditText scoresET;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.scores_activity);

        user = FirebaseAuth.getInstance().getCurrentUser();
        dbRef = FirebaseDatabase.getInstance().getReference();

        //if user not signed in, show login screen - user id is tread as school id
        if(user == null){
            Intent i = new Intent();
            i.setClass(this, EmailPasswordAuthActivity.class);
            startActivity(i);
        }

        Toolbar tb = findViewById(R.id.toolbar);
        setSupportActionBar(tb);
        tb.setSubtitle("Add Scores");

        studentNameET = (EditText)findViewById(R.id.student_name_a);
        gradeET = (EditText)findViewById(R.id.grade_a);
        scoresET = (EditText)findViewById(R.id.marks_a);
        subjectET = (EditText)findViewById(R.id.subject_a);
        Button addMarksButton = findViewById(R.id.add_marks);

        //update intent
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            studentNameET.setText(extras.getString("studentName"));
            studentNameET.setEnabled(false);
            gradeET.setText(extras.getString("grade"));
            gradeET.setEnabled(false);
            scoresET.setText(extras.getString("score"));
            subjectET.setText(extras.getString("subject"));
            subjectET.setEnabled(false);

            addMarksButton.setText("Update Marks");
            recId = extras.getString("recId");
        }

        addMarksButton.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v)
            {
                addMarks();
            }
        });


    }
    private void addMarks(){
        if(recId.isEmpty()){
            addScore();
        }else {
            updateScore();
        }
    }
    private Score populateScoreObj(){
        Score score = new Score();
        score.setSubject(subjectET.getText().toString());
        score.setScore(scoresET.getText().toString());
        return score;
    }
    private void addScore() {
        String studentName = studentNameET.getText().toString();
        String grade = gradeET.getText().toString();
        Score score = populateScoreObj();

        dbRef.child("scores").child(user.getUid())
                .child(grade)
                .child(studentName).push()
                .setValue(score)
                .addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if (task.isSuccessful()) {
                              Log.d(TAG, "Score has been added to db");
                            Toast.makeText(TestScoresActivity.this, "Score added",
                                    Toast.LENGTH_SHORT).show();
                            restUi();
                        } else {
                            Log.d(TAG, "Score couldn't be added to db");
                            Toast.makeText(TestScoresActivity.this,
                                    "Failed to add score",
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }
    private void updateScore() {
        String studentName = studentNameET.getText().toString();
        String grade = gradeET.getText().toString();

        Map<String, Object> scoreUpdate = new HashMap<>();
        scoreUpdate.put("score", scoresET.getText().toString());

        dbRef.child("scores").child(user.getUid()).child(grade)
                .child(studentName).child(recId)
                .updateChildren(scoreUpdate)
                .addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if (task.isSuccessful()) {
                            Log.d(TAG, "Score has been updated to db");
                            Toast.makeText(TestScoresActivity.this, "Score updated",
                                    Toast.LENGTH_SHORT).show();
                            showAddScoresScreen();
                        } else {
                            Log.d(TAG, "Score couldn't be updated to db");
                            Toast.makeText(TestScoresActivity.this,
                                    "Failed to update score",
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    private void restUi(){
        studentNameET.setText("");
        subjectET.setText("");
        gradeET.setText("");
        scoresET.setText("");
    }
    private void showAddScoresScreen() {
        Intent i = new Intent();
        i.setClass(this, TestScoresActivity.class);
        startActivity(i);
    }
}

Add Activity Layout

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="zoftino.com.firebase.cloudfuncion.realtime.TestScoresActivity">
    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary" />
    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="16dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp">
        <android.support.design.widget.TextInputLayout
            android:id="@+id/student_name_la"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">
            <android.support.design.widget.TextInputEditText
                android:id="@+id/student_name_a"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Student Name"/>
        </android.support.design.widget.TextInputLayout>
        <android.support.design.widget.TextInputLayout
            android:id="@+id/grade_la"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/student_name_la">
            <android.support.design.widget.TextInputEditText
                android:id="@+id/grade_a"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Grade"/>
        </android.support.design.widget.TextInputLayout>
        <android.support.design.widget.TextInputLayout
            android:id="@+id/subject_la"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/grade_la">
            <android.support.design.widget.TextInputEditText
                android:id="@+id/subject_a"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Subject"/>
        </android.support.design.widget.TextInputLayout>
        <android.support.design.widget.TextInputLayout
            android:id="@+id/marks_la"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/subject_la">
            <android.support.design.widget.TextInputEditText
                android:id="@+id/marks_a"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Score"/>
        </android.support.design.widget.TextInputLayout>
        <Button
            android:id="@+id/add_marks"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@style/Widget.AppCompat.Button.Colored"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/marks_la"
            android:text="Add Score"/>
    </android.support.constraint.ConstraintLayout>
</LinearLayout>

View Activity

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;
import java.util.List;

import zoftino.com.firestore.EmailPasswordAuthActivity;
import zoftino.com.firestore.R;

public class ViewScoresActivity extends BaseMarksActivity{
    private static final String TAG = "ViewScoresActivity";
    private DatabaseReference dbRef;
    private RecyclerView scoresRecyclerView;
    private FirebaseUser user;

    private EditText studentNameET;
    private EditText gradeET;
    private TextView avgScoreTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.scores_view_activity);

        dbRef = FirebaseDatabase.getInstance().getReference();
        user = FirebaseAuth.getInstance().getCurrentUser();
        if(user == null){
            Intent i = new Intent();
            i.setClass(this, EmailPasswordAuthActivity.class);
            startActivity(i);
        }

        Toolbar tb = findViewById(R.id.toolbar);
        setSupportActionBar(tb);
        tb.setSubtitle("View Scores");

        studentNameET = findViewById(R.id.student_name_v);
        gradeET = findViewById(R.id.grade_v);
        avgScoreTV = findViewById(R.id.avg_score);
        scoresRecyclerView = findViewById(R.id.scores_lst);

        Button button = findViewById(R.id.view_scores_b);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getScores();
            }
        });

        LinearLayoutManager recyclerLayoutManager =
                new LinearLayoutManager(getApplicationContext());
        scoresRecyclerView.setLayoutManager(recyclerLayoutManager);

        DividerItemDecoration dividerItemDecoration =
                new DividerItemDecoration(scoresRecyclerView.getContext(),
                        recyclerLayoutManager.getOrientation());
        scoresRecyclerView.addItemDecoration(dividerItemDecoration);
    }

    private void getScores(){
        final String studentName = studentNameET.getText().toString();
        final String grade = gradeET.getText().toString();

        dbRef.child("scores").child(user.getUid()).child(grade)
                .child(studentName)
                .addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                List<Score> scoresList = new ArrayList<Score>();
                Score score;
                for (DataSnapshot adSnapshot: dataSnapshot.getChildren()) {
                    if("scoreAvg".equals(adSnapshot.getKey())){
                        Log.d(TAG, "score average"+
                                " "+adSnapshot.getValue());
                        avgScoreTV.setText("Average Score "+adSnapshot.getValue());
                        continue;
                    }
                    score = adSnapshot.getValue(Score.class);
                    score.setRecId(adSnapshot.getKey());
                    scoresList.add(score);
                }
                Log.d(TAG, "No of subjects "+scoresList.size());
                ScoresRecyclerViewAdapter recyclerViewAdapter = new
                        ScoresRecyclerViewAdapter(ViewScoresActivity.this, scoresList,
                                studentName, grade, user.getUid());
                scoresRecyclerView.setAdapter(recyclerViewAdapter);
            }
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.d(TAG, "Error trying to get scores"+
                        " "+databaseError);
                Toast.makeText(ViewScoresActivity.this,
                        "Error trying to get scores",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

}

View Activity Layout

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="zoftino.com.firebase.cloudfuncion.realtime.ViewScoresActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary" />

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp">

        <android.support.design.widget.TextInputLayout
            android:id="@+id/student_name_lv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/student_name_v"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Student Name" />
        </android.support.design.widget.TextInputLayout>
        <android.support.design.widget.TextInputLayout
            android:id="@+id/grade_lv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/student_name_lv">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/grade_v"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Grade" />
        </android.support.design.widget.TextInputLayout>

        <Button
            android:id="@+id/view_scores_b"
            style="@style/Widget.AppCompat.Button.Colored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="View Scores"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/grade_lv"/>
    </android.support.constraint.ConstraintLayout>
    <TextView
        android:id="@+id/avg_score"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        android:layout_marginTop="16dp"/>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/scores_lst"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="16dp"
        android:scrollbars="vertical" />
</LinearLayout>

RecyclerView Adapter

package zoftino.com.firebase.cloudfuncion.realtime;


import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
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.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.util.List;

import zoftino.com.firestore.R;

public class ScoresRecyclerViewAdapter extends
        RecyclerView.Adapter<ScoresRecyclerViewAdapter.ViewHolder> {

    private List<Score> scoreList;
    private Context context;
    private DatabaseReference dbRef;
    private String studentName;
    private String grade;
    private String userId;

    public ScoresRecyclerViewAdapter(Context ctx, List<Score> list,
                                     String studentNme, String gradeIn, String uId) {
        context = ctx;
        scoreList = list;
        studentName = studentNme;
        grade = gradeIn;
        userId = uId;
        dbRef = FirebaseDatabase.getInstance().getReference();
    }

    @Override
    public int getItemCount() {
        return scoreList.size();
    }

    @Override
    public ScoresRecyclerViewAdapter.ViewHolder
    onCreateViewHolder(ViewGroup parent, int viewType) {

        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.scores_item, parent, false);

        ScoresRecyclerViewAdapter.ViewHolder viewHolder =
                new ScoresRecyclerViewAdapter.ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ScoresRecyclerViewAdapter.ViewHolder holder, int position) {
        final int itemPos = position;
        final Score item = scoreList.get(position);
        holder.subject.setText(item.getSubject());
        holder.score.setText(item.getScore());

        holder.edit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                editScore(item);
            }
        });

        holder.delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                deleteScore(item.getRecId(), itemPos);
            }
        });
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public TextView subject;
        public TextView score;

        public Button edit;
        public Button delete;

        public ViewHolder(View view) {
            super(view);

            subject = (TextView) view.findViewById(R.id.subject_i);
            score = (TextView) view.findViewById(R.id.score_i);

            edit = view.findViewById(R.id.edit_score_b);
            delete = view.findViewById(R.id.delete_score_b);
        }
    }

    private void editScore(Score item) {
        Intent i = new Intent();
        i.putExtra("studentName", studentName);
        i.putExtra("grade", grade);
        i.putExtra("subject", item.getSubject());
        i.putExtra("score", item.getScore());
        i.putExtra("recId", item.getRecId());
        i.setClass(context, TestScoresActivity.class);
        context.startActivity(i);
    }

    private void deleteScore(String recId, final int position) {
        dbRef.child("scores").child(userId).child(grade)
                .child(studentName).child(recId).removeValue()
                .addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if (task.isSuccessful()) {
                            scoreList.remove(position);
                            notifyItemRemoved(position);
                            notifyItemRangeChanged(position, scoreList.size());

                            Log.d("RecyclerView", "Score deleted");
                            Toast.makeText(context,
                                    "Score deleted",
                                    Toast.LENGTH_SHORT).show();
                        } else {
                            Log.d("RecyclerView", "Score couldn't be deleted");
                            Toast.makeText(context,
                                    "Score couldn't be deleted",
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }
}

RecyclerView Item Layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp"
    android:layout_marginLeft="8dp"
    android:layout_marginRight="8dp">
    <TextView
        android:id="@+id/subject_i"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/score_i"
        app:layout_constraintTop_toTopOf="parent" />
    <TextView
        android:id="@+id/score_i"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/subject_i"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/edit_score_b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="0dp"
        android:minWidth="0dp"
        style="@style/Widget.AppCompat.Button.Colored"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/delete_score_b"
        app:layout_constraintTop_toBottomOf="@+id/subject_i"
        android:text="Edit"/>
    <Button
        android:id="@+id/delete_score_b"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:minHeight="0dp"
        android:minWidth="0dp"
        style="@style/Widget.AppCompat.Button.Colored"
        app:layout_constraintLeft_toRightOf="@+id/edit_score_b"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/subject_i"
        android:text="Delete"/>
</android.support.constraint.ConstraintLayout>

Realtime Database Data Model

firebase cloud functions realtime database android example data model

Firebase Cloud Functions Logs

firebase cloud functions realtime database android example logs

To know how to use cloud functions with firestore trigger, please see Firebase cloud functions firestore trigger android example

To know how to use cloud functions with cloud storage trigger, please see Firebase cloud functions firestore trigger android example