Firebase Database in Android with Phone Authentication - Kotlin
Configure Database:
(a) Go to https://console.firebase.google.com/
(b) Then click Add Project, a dialog will be prompted. Enter project name and choose country in it and click on Create Project.
(c) Click on Add Firebase to your Android App, another dialog will be prompted. In that, enter package name and SHA-1 (compulsary for using Database) and click Register App.
(d) Then download google_services.json file, by clicking download button and put that file in your module's root directory.
(e) Then navigate to Database on Side tab and click on Get Started.
Initially the Database rules will be like,
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Below rule is to allow everyone to read and write data without any authentication.
{
"rules": {
".read": true,
".write": true
}
}
(f) Then click on Authentication in side menu and navigate to Sign-in Method. And enable Phone and Save it.
Structuring Database:
The most important task is planning your database structure, i.e., How you gonna save data and querying it. In Firebase Database, the data can only be saved as JSONObject.
{ "users": { "alovelace": { "name": "Ada Lovelace", "contacts": { "ghopper": true }, }, "ghopper": { ... }, "eclarke": { ... } } }
In Case, if you would like to store it as JSONArray, it can't be stored directly. But we can store it as follows:
{ "groups": { "-KeYm3ITV5PWK4u-KjVW": { "name": "My Family Group", "photo": "http://dsfds" }, "-KeYmGi58ersPDWhC4EM": { "name": "Crime Partners", "photo": "http://frefg" } } }
Here, "-KeYm3ITV5PWK4u-KjVW" and "-KeYmGi58ersPDWhC4EM" are auto-generated keys. So, the JSON Tree will read that as an array as follows:
{ "groups": { [0]: { "name": "My Family Group", "photo": "http://dfdsf....", }, [1]: { "name": "Crime Partners", "photo": "http://dggfdsf....", } } }
Posting to Database:
To access Database, get reference to your exact node,
For posting data as Array, we can use:
Here, push() will auto-generate a key and post values to that. If you need that key, you can get that with push().getKey()
var mFirebaseInstance = FirebaseDatabase.getInstance()
var mFirebaseDatabase = mFirebaseInstance.getReference("users/").getRef()
Here, "users" is node here. Let's just consider it as "Table" for our understanding.
For posting data to our database as JSONObject, we can use:
val map = mapOf("name" to nameText, "image" to imageUrl)
mFirebaseDatabase.child(currentUser).setValue(map)
For posting data as Array, we can use:
val map = mapOf("name" to nameText, "image" to imageUrl)
mFirebaseDatabase.push().setValue(map)
Here, push() will auto-generate a key and post values to that. If you need that key, you can get that with push().getKey()
Querying Database:
Firebase Database can be queried either with ValueEventListener or ChildEventListener.
ValueEventListener will read entire data in the given path at once and notify us when any changes happened.
ChildEventListener will read each child in the given path and notify us when any child added, removed or changed.
Code:
Wheww!! Now we done with prerequisite and understood basic concepts. Lets proceed with code now.
build.gradle
Include FirebaseUI in dependencies
compile 'com.firebaseui:firebase-ui:3.1.2'
info_page.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="104dp"
android:layout_margin="15dp"
android:scaleType="centerCrop"
android:src="@drawable/default_pic"
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintBottom_toTopOf="@+id/update"
app:layout_constraintTop_toBottomOf="@+id/image">
<android.support.design.widget.TextInputEditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name" />
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:gravity="center"
android:padding="15dp"
android:text="Update"
android:textColor="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
FirebaseActivity.kt
Using Firebase UI, we can authenticate users with phone number very easily. Once the authentication is success, we can redirect the user to next page, there he can be able to upload his name and profile picture to the Firebase Database.
class FirebaseActivity : AppCompatActivity() {
private val RC_SIGN_IN = 1001
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val auth = FirebaseAuth.getInstance()
if (auth.currentUser != null) {
// If already signed in
startActivity(Intent(this@FirebaseActivity, InfoActivity::class.java))
finish()
} else {
// If not signed in
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(
Arrays.asList(
AuthUI.IdpConfig.Builder(AuthUI.PHONE_VERIFICATION_PROVIDER).build()
))
.build(),
RC_SIGN_IN)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val response = IdpResponse.fromResultIntent(data)
Log.v("response.toString()", "" + response.toString());
if (resultCode == ResultCodes.OK) {
startActivity(Intent(this@FirebaseActivity, InfoActivity::class.java))
finish()
return
} else {
when {
response == null -> finish()
response.errorCode == ErrorCodes.NO_NETWORK -> {
Toast.makeText(this@FirebaseActivity, "No Internet Connection", Toast.LENGTH_SHORT).show()
return
}
response.errorCode == ErrorCodes.UNKNOWN_ERROR -> {
Toast.makeText(this@FirebaseActivity, "Unknown Error", Toast.LENGTH_SHORT).show()
return
}
}
}
}
}
}
InfoActivity.kt
class InfoActivity : AppCompatActivity(), View.OnClickListener { val PERMISSION_REQUEST_CODE = 1001 val PICK_IMAGE_REQUEST = 900 lateinit var filePath: Uri var imageUrl: String? = "" lateinit var prefs: SharedPreferences lateinit var editor: SharedPreferences.Editor val FIREBASE_USERS = "users/" var mAuth: FirebaseAuth? = null var currentUser: String? = "" lateinit var mFirebaseInstance: FirebaseDatabase lateinit var mFirebaseDatabase: DatabaseReference override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.info_page) prefs = getSharedPreferences("", Context.MODE_PRIVATE) editor = prefs.edit() mAuth = FirebaseAuth.getInstance() currentUser = mAuth?.currentUser?.phoneNumber mFirebaseInstance = FirebaseDatabase.getInstance() mFirebaseDatabase = mFirebaseInstance.getReference(FIREBASE_USERS).getRef() mFirebaseDatabase.addListenerForSingleValueEvent(object : ValueEventListener { override fun onCancelled(data: DatabaseError?) { } override fun onDataChange(data: DataSnapshot?) { if (data != null) if (data.hasChild(currentUser)) { val map : Map<String, String>? = data.child(currentUser).getValue() as? Map<String,String> Log.v("getData", "getData==" + map) editor.putString("name", map?.get("name")) editor.putString("image", map?.get("image")) editor.putBoolean("infoAdded", true) editor.commit() if (prefs.getBoolean("infoAdded", false)) { setUI() } } } }) image.setOnClickListener(this) update.setOnClickListener(this) } private fun setUI() { name.setText(prefs.getString("name", "")) name.isEnabled = false update.text = "LogOut" imageUrl = prefs.getString("image", "") Glide.with(this@InfoActivity).load(imageUrl).into(image) } private fun chooseFile() { val intent = Intent().apply { type = "image/*" action = Intent.ACTION_GET_CONTENT } startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST) } private fun uploadFile() { val progress = ProgressDialog(this).apply { setTitle("Uploading Picture....") setCancelable(false) setCanceledOnTouchOutside(false) show() } val data = FirebaseStorage.getInstance() var value = 0.0 var storage = data.getReference().child("mypic.jpg").putFile(filePath) .addOnProgressListener { taskSnapshot -> value = (100.0 * taskSnapshot.bytesTransferred) / taskSnapshot.totalByteCount Log.v("value", "value==" + value) progress.setMessage("Uploaded.. " + value.toInt() + "%") } .addOnSuccessListener { taskSnapshot -> progress.dismiss() imageUrl = taskSnapshot.downloadUrl.toString() Log.v("Download File", "File.." + imageUrl); Glide.with(this@InfoActivity).load(imageUrl).into(image) } .addOnFailureListener { exception -> exception.printStackTrace() } } private fun updateDetails() { val nameText = name.text.toString().trim() val map = mapOf("name" to nameText, "image" to imageUrl) mFirebaseDatabase.child(currentUser).setValue(map) editor.putString("name", nameText) editor.putString("image", imageUrl) editor.putBoolean("infoAdded", true) editor.commit() setUI() } private fun logOut() { mAuth?.signOut() editor.clear() editor.commit() startActivity(Intent(this, FirebaseActivity::class.java)) finish() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { PERMISSION_REQUEST_CODE -> { if (grantResults.isEmpty() || grantResults[0] == PackageManager.PERMISSION_GRANTED) Toast.makeText(this@InfoActivity, "Oops! Permission Denied!!", Toast.LENGTH_SHORT).show() else chooseFile() } } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode != Activity.RESULT_OK) { return; } when (requestCode) { PICK_IMAGE_REQUEST -> { filePath = data!!.getData() uploadFile() } } } @RequiresApi(Build.VERSION_CODES.M) override fun onClick(v: View?) { when(v?.id){ R.id.image -> { when { (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) -> { if (ContextCompat.checkSelfPermission(this@InfoActivity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE) } else { chooseFile() } } else -> chooseFile() } } R.id.update -> { when { prefs.getBoolean("infoAdded", false) -> logOut() imageUrl.toString().isEmpty() -> Toast.makeText(this@InfoActivity, "Please upload an image!", Toast.LENGTH_SHORT).show() name.text.toString().trim().length == 0 -> Toast.makeText(this@InfoActivity, "Please enter your name!", Toast.LENGTH_SHORT).show() else -> updateDetails() } } } } }
Run Application:
Note:
* Also remember that the Phone Authentication will not work in Emulator, it requires Physical device as per Document.
* If you would like to do that in Custom UI, you can refer my other JAVA post.
* If you would like to do that in Custom UI, you can refer my other JAVA post.
Reference:
1. Website : https://firebase.google.com/docs/database/android/start/
Comments
Post a Comment