User does not have permission to access this object: Firebase error - firebase

I have an app in which the user can upload images to create a task list. But, whenever I upload the image and say "create", the onFailureListener is called and "User does not have permission to access this object" occurs.
My firebase rules:
// Allow read/write access on all documents to any user signed in to the application
service cloud.firestore {
match /databases/{database}/documents {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
I tried all the rules mentioned on User does not have permission to access this object . Firebase storage android
All of these result in another error which is "Error in updating Profile". This is the line I had added on the onFailureListener when the profile data is loaded initially.
Below is my CreateBoardActivity.kt class- when I upload an image and then click on the "btn_create" button, the error (User does not have permission to access this object) pops up. (I suppose the "uploadBoardImage()" method is called in this scenario)
package com.example.projectmanager.activities
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.bumptech.glide.Glide
import com.example.projectmanager.Firebase.FireStoreClass
import com.example.projectmanager.R
import com.example.projectmanager.models.Board
import com.example.projectmanager.utils.Constants
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.StorageReference
import kotlinx.android.synthetic.main.activity_create_board.*
import java.io.IOException
class CreateBoardActivity : BaseActivity() {
private var mSelectedImageFileUri : Uri? = null
private lateinit var mUserName: String
private var mBoardImageURL : String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_board)
setUpActionBar()
if(intent.hasExtra(Constants.NAME)){
mUserName = intent.getStringExtra(Constants.NAME).toString()
}
iv_board_image.setOnClickListener{
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
Constants.showImageChooser(this)
}else{
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), Constants.READ_STORAGE_PERMISSION_CODE)
}
}
btn_create.setOnClickListener {
if(mSelectedImageFileUri != null){
uploadBoardImage()
}
else{
showProgressDialog(resources.getString(R.string.please_wait))
createBoard()
}
}
}
private fun createBoard(){
val assignedUsersArrayList: ArrayList<String> = ArrayList()
assignedUsersArrayList.add(getCurrentUserId())
val board = Board(et_board_name.text.toString(), mBoardImageURL, mUserName, assignedUsersArrayList)
FireStoreClass().createBoard(this, board)
}
private fun uploadBoardImage(){
showProgressDialog(resources.getString(R.string.please_wait))
val sRef : StorageReference = FirebaseStorage.getInstance().reference.child(
"BOARD_IMAGE" + System.currentTimeMillis() + "." + Constants.getFileExtension(this, mSelectedImageFileUri))
sRef.putFile(mSelectedImageFileUri!!).addOnSuccessListener {
taskSnapshot->
Log.i("Board Image URL", taskSnapshot.metadata!!.reference!!.downloadUrl.toString())
taskSnapshot.metadata!!.reference!!.downloadUrl.addOnSuccessListener {
uri->
Log.i("Downloadable Image URL", uri.toString())
mBoardImageURL = uri.toString()
createBoard()
}
}.addOnFailureListener{
exception->
Toast.makeText(this, exception.message, Toast.LENGTH_LONG).show()
hideProgressionDialog()
}
}
fun boardCreatedSuccessfully(){
hideProgressionDialog()
setResult(Activity.RESULT_OK)
finish()
}
private fun setUpActionBar(){
setSupportActionBar(toolbar_create_board_activity)
val actionBar = supportActionBar
if(actionBar!=null){
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setHomeAsUpIndicator(R.drawable.ic_black_color_white_24dp)
actionBar.title = resources.getString(R.string.create_board_title)
}
toolbar_create_board_activity.setNavigationOnClickListener { onBackPressed() }
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if(requestCode== Constants.READ_STORAGE_PERMISSION_CODE){
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
}
}else{
Toast.makeText(this, "You have denied the permission for storage. You can allow it from settings", Toast.LENGTH_LONG).show()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK && requestCode == Constants.PICK_IMAGE_REQUEST_CODE && data!!.data != null){
mSelectedImageFileUri = data.data
try{
Glide.with(this)
.load(mSelectedImageFileUri)
.centerCrop()
.placeholder(R.drawable.ic_board_place_holder)
.into(iv_board_image)
}catch(e: IOException){
e.printStackTrace()
}
}
}
}
Please do let me know what rules I should use to fix this issue.

Related

Download Multiple images from Firebase Realtime Database to device storage

I have a recyclerview that displays multiple images from Firebase Realtime Database. The recyclerview also has a button within it. I want this button to allow users to be able to download these images ONE AT A TIME once they click it.
Once users click "download" I want the images to be saved to their device storage. I've tried multiple solutions for this, but they weren't helpful as they were for either Firestore Database or only allowed for one image to be downloaded.
Code
class AbstractAdapter(private val mContext: Context, private val abstractList: List<Abstract>) :
RecyclerView.Adapter<AbstractAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.abstract_image_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.download_btn.setOnClickListener {
downloadFile()
}
Glide.with(mContext)
.load(abstractList[position].abstract)
.into(holder.imageView)
}
override fun getItemCount(): Int {
return abstractList.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.abstractImageView)
val download_btn: Button = itemView.findViewById(R.id.abstractDownloadBtn)
}
private fun downloadFile() {
val storage = FirebaseStorage.getInstance()
val storageRef = storage.getReferenceFromUrl("https://notes-72413.firebaseio.com/")
val islandRef = storageRef.child("Abstract")
val rootPath = File(Environment.getExternalStorageDirectory(), "abstract")
if (!rootPath.exists()) {
rootPath.mkdirs()
}
val localFile = File(rootPath, "imageName.jpeg")
islandRef.getFile(localFile)
.addOnSuccessListener(OnSuccessListener<FileDownloadTask.TaskSnapshot?> {
Log.e("firebase ", ";local tem file created created $localFile")
// updateDb(timestamp,localFile.toString(),position);
}).addOnFailureListener(OnFailureListener { exception ->
Log.e(
"firebase ",
";local tem file not created created $exception"
)
})
}
companion object {
private const val Tag = "RecyclerView"
}
I've tried this code, but once I click the "download" button it immediately crashes and Logcat says Firebase Storage URLs must point to an object in your Storage Bucket. Please obtain a URL using the Firebase Console or getDownloadUrl()
My Firebase Realtime Database
There's 64 files in total
Summary
I have a recyclerview that displays images from Firebase Realtime Database. Once users click the "download" button, it only downloads one image at a time to their device storage.
Update
private fun downloadFile() {
val storage = FirebaseStorage.getInstance()
val storageRef = storage.getReferenceFromUrl("abstract")
val rootPath = File(Environment.getExternalStorageDirectory(), "abstract")
if (!rootPath.exists()) {
rootPath.mkdirs()
}
val localFile = File(rootPath, "imageName.jpeg")
storageRef.child("Abstract").downloadUrl.addOnSuccessListener { Log.e("firebase ", ";local tem file created created $localFile")
}.addOnFailureListener(OnFailureListener { exception ->
Log.e("firebase ", ";local tem file not created created $exception")
})
}
These are the changes I made to my downloadFile function, but I still get an error:
The storage Uri could not be parsed
Second update
2022-06-11 21:36:00.536 29751-29751/com.khumomashapa.notes E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.khumomashapa.notes, PID: 29751
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:523)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055) 
Caused by: java.net.MalformedURLException: no protocol:
at java.net.URL.<init>(URL.java:601)
at java.net.URL.<init>(URL.java:498)
at java.net.URL.<init>(URL.java:447)
at com.khumomashapa.notes.adapter.AbstractAdapter.downloadFile(AbstractAdapter.kt:57)
at com.khumomashapa.notes.adapter.AbstractAdapter.onBindViewHolder$lambda-0(AbstractAdapter.kt:35)
at com.khumomashapa.notes.adapter.AbstractAdapter.$r8$lambda$Rrmx0DFlwJu1z6QtjG8WCQp6NQQ(Unknown Source:0)
at com.khumomashapa.notes.adapter.AbstractAdapter$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7216)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1217)
at android.view.View.performClickInternal(View.java:7190)
at android.view.View.access$3500(View.java:827)
at android.view.View$PerformClick.run(View.java:27663)
at android.os.Handler.handleCallback(Handler.java:900)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:219)
at android.app.ActivityThread.main(ActivityThread.java:8349)
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1055) 
Code
class AbstractAdapter(private val mContext: Context, private val abstractList: List<Abstract>) :
RecyclerView.Adapter<AbstractAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.abstract_image_view, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.download_btn.setOnClickListener {
downloadFile(url = String(), file = String.toString())
}
Glide.with(mContext)
.load(abstractList[position].abstract)
.into(holder.imageView)
}
override fun getItemCount(): Int {
return abstractList.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.abstractImageView)
val download_btn: Button = itemView.findViewById(R.id.abstractDownloadBtn)
}
#Throws(IOException::class)
private fun downloadFile(url: String, file: String) {
val urlObj = URL(url)
val fileObj = File(file)
val conn = urlObj.openConnection()
val buffer = ByteArray(1024)
object : BufferedOutputStream(FileOutputStream(fileObj)) {
var `in` = BufferedInputStream(conn.getInputStream())
init {
var read: Int
while (`in`.read(buffer, 0, buffer.size) >= 0.also { read = it });
run {
out.write(buffer, 0, read)
}
out.flush()
}
}.use { out -> }
}
I found the perfect solution to my problem. All I had to do was create an OnItemClick interface to get a different result for each item click and use Download manager to download the images.
override fun onItemClick(item: String, pos:Int) {
abstractData = item
positionItem = pos
if (checkSelfPermission(requireActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ){
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQ_CODE)
}else{
startDownloading()
}
Toast.makeText(requireActivity(), "Saved to Internal storage/Pictures/AbstractWallpaper", Toast.LENGTH_LONG).show()
}
private fun startDownloading() {
val request = DownloadManager.Request(Uri.parse(abstractData))
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
request.setTitle("Abstract Wallpaper")
request.setDescription("Your image is downloading")
request.allowScanningByMediaScanner()
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, "AbstractWallpapers")
val manager = activity?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
manager.enqueue(request)
Toast.makeText(requireActivity(), "Download is starting...", Toast.LENGTH_LONG).show()
}
As you are downloading based on your storage URLs so need to use getDownloadUrl() method.
storageRef.child("Abstract").getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
...Uri
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// To handle error
}
});
As those URLs in your database are publicly visible HTTP URLs. You can just download the files with a good old URLConnection and binary streams.
private void downloadFile(String url, String file) throws IOException {
URL urlObj = new URL(url);
File fileObj = new File(file);
URLConnection conn = urlObj.openConnection();
byte[] buffer = new byte[1024];
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fileObj)) {
try (BufferedInputStream in = new BufferedInputStream(conn.getInputStream())) {
int read;
while ((read = in.read(buffer, 0, buffer.length) >= 0) {
out.write(buffer, 0, read);
}
out.flush();
}
}
}
Not tested, but for idea,
I'm using okhttp and okio
fun saveImageToPicture(context: Countext, url: String, imageName: String, imageMimeType: String) {
val appImagePath = File(Environment.DIRECTORY_PICTURES, "FolderNameInPictures")
val date = System.currentTimeMillis()
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$imageName.$imageMimeType")
put(MediaStore.Images.Media.MIME_TYPE, "image/$imageMimeType")
put(MediaStore.Images.Media.DATE_ADDED, date)
put(MediaStore.Images.Media.DATE_MODIFIED, date)
}
var collection = if (Build.VERSION.SDK_INT >= 29 {
values.put(MediaStore.Images.Media.RELATIVE_PATH, "$appImagePath${File.separator}")
values.put(MediaStore.Images.Media.IS_PENDING, 1)
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
} else {
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val resolver = context.contentResolver
val insertedUri = resolver.insert(collection, values)
val outputStream = resolver.openOutputStream(insertedUri!!, "w")
downloadFromUrlToOutputStream(url, outputStream)
values.clear()
if (Build.VERSION.SDK_INT >= 29 {
values.put(MediaStore.Images.Media.IS_PENDING, 0)
}
resolver.update(insertedUri, values, null, null)
}
fun downloadFromUrlToOutputStream(url: String, output: OutputStream) {
val request = Request.Builder().url(url).build()
val response = OkHttpClient().newCall(request).execute();
val body = response.body()
val bufferSource = body.source()
val sink = Okio.buffer(Okio.sink(output))
val sinkBuffer = sink.buffer()
val bufferSize = 1024 * 8
while (bufferSource.read(sinkBuffer, bufferSize) != -1) {
sink.emit()
}
sink.flush()
sink.close()
bufferSource.close()
}

A flaw inside app when using recyclerview to load documents from firestore in kotlin

I am using recycler view to load documents from firestore. Now my recycler view lists has three buttons. One of the button name is notify. When I click notify button(btn_notify) I want to make the button visibity to gone using holder.button.visibility = View.GONE. But what happens is that when I change the button visibility to gone, the clicked button visibility changes to gone. At the same time the other recycler view lists button visibility also changes to gone. Why this error is happening.
[![enter image description here][1]][1]
package com.example.bloodbankcompany.recyclerview
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
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 androidx.recyclerview.widget.RecyclerView
import com.example.bloodbankcompany.*
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.messaging.FirebaseMessaging
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.lang.Exception
const val TOPIC = "/topics/myTopic"
class MyAdapter2(private val userList: ArrayList<User2>): RecyclerView.Adapter<MyAdapter2.MyViewHolder>(){
var id: String = ""
private lateinit var userArrayList: ArrayList<User2>
private val mFireStore = FirebaseFirestore.getInstance()
val TAG = "MyAdapter2"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyAdapter2.MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.list_item2,parent,false)
return MyViewHolder(itemView)
}
//
// interface OnItemClickListener {
// fun onItemClick(position: Int)
// }
override fun onBindViewHolder(holder: MyAdapter2.MyViewHolder, position: Int) {
// holder.bind( position ,onItemClickListener)
val user:User2 = userList[position]
holder.name.text= user.name
holder.phone.text= user.phone
holder.address.text =user.address
holder.bloodGroup.text= user.bloodgroup
holder.id.text = user.id
holder.status.text= user.status
holder.donatedby.text = user.donatedBy.toString()
val statust = user.status?.trim()
val requester_name = user.name
val requester_no = user.phone
holder.emailn.text= user.email
val idp: String? =user.id
val phonen: String? =user.phone
val emailt: String? = user.email
holder.button.setOnClickListener {
holder.button.visibility = View.GONE
val washingtonre = mFireStore.collection("applicationForm").document("$idp")
washingtonre.update("status","Verified").addOnCompleteListener {
holder.status.text = "Verified"
val title = "New blood donation request."
val message = "Please contact $requester_name at $requester_no."
//val recipientToken = etToken.text.toString()
if(title.isNotEmpty() && message.isNotEmpty()) {
PushNotificationNotif(
NotificationDataNotif(title, message),
TOPIC
// recipientToken
).also {
sendNotification(it)
}
}
}
}
holder.phonen.setOnClickListener {
val context=holder.button.context
val intent1 = Intent(Intent.ACTION_DIAL)
intent1.data = Uri.parse("tel:" + phonen)
context.startActivity(intent1)
}
holder.bemail.setOnClickListener {
val context1=holder.button.context
val intent= Intent(Intent.ACTION_SEND)
intent.data = Uri.parse("mailto:" + emailt)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_EMAIL, emailt)
intent.putExtra(Intent.EXTRA_SUBJECT, " ")
intent.putExtra(Intent.EXTRA_TEXT, " ")
intent.setType("message/rfc822")
try {
// context1.startActivity(Intent.createChooser(intent, "Choose email Client"))
context1.startActivity(intent)
} catch (e: Exception){
}
}
}
#SuppressLint("LongLogTag")
private fun sendNotification(notification: PushNotificationNotif) = CoroutineScope(
Dispatchers.IO).launch {
try {
val response = RetrofitInstanceNotif.api.postNotification(notification)
if(response.isSuccessful) {
Log.d(TAG, "Response: ${Gson().toJson(response)}")
} else {
Log.e(TAG, response.errorBody().toString())
}
} catch(e: Exception) {
Log.e(TAG, e.toString())
}
}
override fun getItemCount(): Int {
return userList.size
}
public class MyViewHolder(itemView : View): RecyclerView.ViewHolder(itemView){
// fun bind(position: Int, listener: OnItemClickListener) {
// button.setOnClickListener { v -> listener.onItemClick(position) }
// }
val name : TextView = itemView.findViewById(R.id.tvfirstname1)
val phone :TextView= itemView.findViewById(R.id.tvphone11)
val address : TextView= itemView.findViewById(R.id.tvaddress1)
val bloodGroup: TextView =itemView.findViewById(R.id.tvbloodg1)
val id: TextView = itemView.findViewById(R.id.tvid)
val status: TextView = itemView.findViewById(R.id.tvstatus1)
// val donationsta: TextView = itemView.findViewById(R.id.tvdonated1)
var io: String? = ""
val button: Button = itemView.findViewById(R.id.btn_notify)
val phonen: Button =itemView.findViewById(R.id.btn_phone1)
val emailn: TextView = itemView.findViewById(R.id.tvemail1)
val bemail: TextView = itemView.findViewById(R.id.btn_email1)
val donatedby: TextView=itemView.findViewById(R.id.tvdonated1)
}
} ```
/** This is the recyclerview activity code
package com.example.bloodbankcompany.recyclerview
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.WindowManager
import android.widget.Adapter
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.bloodbankcompany.MyAdapter
import com.example.bloodbankcompany.R
import com.example.bloodbankcompany.User1
import com.example.bloodbankcompany.User2
import com.google.firebase.firestore.*
import kotlinx.android.synthetic.main.activity_blood_application_form.*
import kotlinx.android.synthetic.main.activity_main2.*
import java.lang.NullPointerException
class MainActivity2 : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var userArrayList: ArrayList<User2>
private lateinit var myAdapter: MyAdapter2
private val mFireStore = FirebaseFirestore.getInstance()
var db = FirebaseFirestore.getInstance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
// setupActionBar()
recyclerView= findViewById(R.id.recyclerview)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
userArrayList= arrayListOf()
myAdapter =MyAdapter2(userArrayList)
recyclerView.adapter = myAdapter
EventChangeListener()
}
private fun EventChangeListener() {
try {
mFireStore.collection("applicationForm").addSnapshotListener(object :
EventListener<QuerySnapshot> {
override fun onEvent(value: QuerySnapshot?, error: FirebaseFirestoreException?) {
if (error != null) {
Log.e("firestore error", error.message.toString())
}
try {
for (dc: DocumentChange in value?.documentChanges!!) {
if (dc.type == DocumentChange.Type.ADDED) {
userArrayList.add(dc.document.toObject(User2::class.java))
}
// Toast.makeText(applicationContext,userArrayList.toString(), Toast.LENGTH_SHORT).show()
}
} catch (e:NullPointerException){
}
myAdapter.notifyDataSetChanged()
}
})
} catch (e:NullPointerException){
}
}
}
[1]: https://i.stack.imgur.com/loob8.jpg

Firebase Custom Google Auth Login

I need to login users with Google Sign On onto Firebase. But since I need to use the Calendar API, I have to do a custom login (and not the one provided through Firebase on default), as I need the Oauth refresh token - which is not provided by Firebase. I have looked at the docs but none seem to work, I am using the below code as of now... can anybody suggest how do I use the token obtained from Google to login/register with Firebase?
private const val TAG = "WelcomeActivity"
class WelcomeActivity : AppCompatActivity() {
var firebaseUser: FirebaseUser? = null
//For Google Sign In
val RC_SIGN_IN: Int = 9001
private lateinit var mGoogleSignInClient: GoogleSignInClient
lateinit var mGoogleSignInOptions: GoogleSignInOptions
private lateinit var firebaseAuth: FirebaseAuth
private var firebaseUserID: String = ""
private lateinit var refUsers: DatabaseReference
//get data from google signin in handlesigninresult
private var googleId = ""
private var googleFirstName = ""
private var googleLastName = ""
private var googleEmail = ""
private var googleProfilePicURL = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_welcome)
// //For google sign in
// configureGoogleSignIn()
// setupUI()
// enablePersistence()
firebaseAuth = FirebaseAuth.getInstance()
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("")
.requestEmail()
.build()
mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
google_login.setOnClickListener {
signIn()
}
login_welcome.setOnClickListener {
val intent = Intent(this#WelcomeActivity, LoginActivity::class.java)
startActivity(intent)
finish()
}
}
private fun signIn() {
val signInIntent = mGoogleSignInClient.signInIntent
startActivityForResult(signInIntent, RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
val task =
GoogleSignIn.getSignedInAccountFromIntent(data)
handleSignInResult(task)
}
}
private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
try {
val account = completedTask.getResult(
ApiException::class.java
)
// Signed in successfully
googleId = account?.id ?: ""
Log.i("Google ID", googleId)
googleFirstName = account.givenName ?: ""
Log.i("Google First Name", googleFirstName)
googleLastName = account?.familyName ?: ""
Log.i("Google Last Name", googleLastName)
googleEmail = account?.email ?: ""
Log.i("Google Email", googleEmail)
val googleIdToken: String = account?.idToken ?: ""
Log.i("Google ID Token", googleIdToken)
googleProfilePicURL = account?.photoUrl.toString()
Log.i("Google Profile Pic URL", googleProfilePicURL)
firebaseAuthWithGoogle(googleIdToken)
} catch (e: ApiException) {
// Sign in was unsuccessful
Log.e(
"failed code=", e.statusCode.toString()
)
}
}
private fun enablePersistence() {
// [START rtdb_enable_persistence]
Firebase.database.setPersistenceEnabled(true)
// [END rtdb_enable_persistence]
}
private fun firebaseAuthWithGoogle(idToken: String) {
val credential = GoogleAuthProvider.getCredential(idToken, null)
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success")
firebaseUserID = firebaseAuth.currentUser!!.uid
refUsers = FirebaseDatabase.getInstance().reference.child("Users").child(firebaseUserID)
refUsers.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
val user: Users? = p0.getValue(Users::class.java)
//Check if user exists in the database
if (user!!.getFirstName() != null) {
val intent = Intent(
this#WelcomeActivity,
IntroSplashScreen::class.java
)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
} else {
val usersHashMap = HashMap<String, Any>()
usersHashMap["uid"] = firebaseUserID
usersHashMap["firstname"] = googleFirstName
usersHashMap["surname"] = googleLastName
usersHashMap["profile"] = googleProfilePicURL
usersHashMap["primaryEmail"] = googleEmail
usersHashMap["search"] =
googleFirstName.toLowerCase(Locale.ROOT)
refUsers.updateChildren(usersHashMap)
.addOnCompleteListener {
if (task.isSuccessful) {
val intent = Intent(
this#WelcomeActivity,
NewProfileData::class.java
)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
}
}
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.exception)
// ...
}
// ...
}
}
private fun refreshIdToken() {
// Attempt to silently refresh the GoogleSignInAccount. If the GoogleSignInAccount
// already has a valid token this method may complete immediately.
//
// If the user has not previously signed in on this device or the sign-in has expired,
// this asynchronous branch will attempt to sign in the user silently and get a valid
// ID token. Cross-device single sign on will occur in this branch.
mGoogleSignInClient.silentSignIn()
.addOnCompleteListener(
this
) { task -> handleSignInResult(task) }
}
override fun onStart() {
super.onStart()
//Checks if the Google IDToken has expired, if yes it refreshes by SilentSign in and generates new Firebase Token
refreshIdToken()
//Checks if user is logged in to firebase
firebaseUser = FirebaseAuth.getInstance().currentUser
//If logged in then sends to MainActivity
if (firebaseUser != null) {
startActivity(IntroSplashScreen.getLaunchIntent(this))
finish()
}
}
override fun onResume() {
super.onResume()
refreshIdToken()
}
}
My method of checking if the user exists was incorrect with the currect process being to put the check on Datasnapshot exits or not. If exists then login else register.

Kotlin searchView does not reload Firebase array

I have one activity with 2 recyclerViews, both recyclers are used with Firebase. One of the recyclers displays the results of a query, the other recycler has a listener that updates every time there is an update in Firebase. I added a searchView to filter the results from Firebase. The issue I'm having is when I'm trying to search the results from the Firebase query, when I start typing I see results but when I click the X to stop searching, the adapter does not reload the array and i don't see the list of items unless I reload the activity. I'm not sure what I'm missing here. Any help/suggestion is greatly appreciated. Here is my code:
Adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.eduardoz.ezmdapp.Model.Charges
import com.eduardoz.ezmdapp.R
class ChargesAdapter (private var charges: ArrayList<Charges>
, private var chargesAll: ArrayList<Charges>
, private val itemClick: (Charges) -> Unit)
: RecyclerView.Adapter<ChargesAdapter.ViewHolder>()
, Filterable {
inner class ViewHolder(itemView: View, val itemClick: (Charges) -> Unit) :
RecyclerView.ViewHolder(itemView) {
private val chargeCode = itemView.findViewById<TextView>(R.id.chargeCodeTxt)
private val chargeDescription = itemView.findViewById<TextView>(R.id.chargeDescriptionTxt)
fun bindCharges(charges: Charges) {
chargeCode?.text = charges.chargeCode
chargeDescription?.text = charges.chargeDescription
itemView.setOnClickListener { itemClick(charges) }
}
}
init {
this.charges = charges
chargesAll = java.util.ArrayList(charges)
}
override fun getItemCount(): Int {
return charges.count()
}
override fun onBindViewHolder(holder: ChargesAdapter.ViewHolder, position: Int) {
holder.bindCharges(charges[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChargesAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.charges_list, parent, false)
return ViewHolder(view, itemClick)
}
override fun getFilter(): Filter {
return searchFilter
}
private val searchFilter: Filter = object: Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList: ArrayList<Charges> = ArrayList()
if (constraint!!.isEmpty()) {
filteredList.addAll(chargesAll)
} else {
for(item in chargesAll) {
if
(item.chargeDescription.toLowerCase().contains(constraint.toString().toLowerCase())) {
filteredList.add(item)
}
}
}
val searchResults = FilterResults()
searchResults.values = filteredList
return searchResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
charges.clear()
charges.addAll(results!!.values as Collection<Charges>)
notifyDataSetChanged()
}
}
}
Activity
descriptionSearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText.isNotEmpty()) {
searchViewBar(newText)
} else {
if (newText.isEmpty()) { //I ADDED THIS TO RELOAD THE ADAPTER
charges.clear()
chargeList()
}
}
return false
}
})
private fun searchViewBar(newText: String) {
chargesListener = chargesCollectionRef
.whereGreaterThanOrEqualTo(CHARGE_DESCRIPTION, newText)
.whereLessThanOrEqualTo(CHARGE_DESCRIPTION, newText+"z")
.addSnapshotListener(this) { snapshot, exception ->
if (exception != null) {
println("error")
}
if (snapshot != null) {
charges.clear()
parseData(snapshot)
}
}
}
fun parseData(snapshot: QuerySnapshot) {
for (document in snapshot.documents) {
val data = document.data
val chargeCode = data!![CHARGE_CODE] as String
val chargeDescription = data[CHARGE_DESCRIPTION] as String
val chargeSpecialty = data[CHARGE_SPECIALTY] as String
val newChargeList = Charges(chargeCode, chargeDescription, chargeSpecialty)
charges.add(newChargeList)
}
chargesFromAdapter.notifyDataSetChanged()
}

how does onAuthStateChange() works and it's code in kotlin?

I am trying to log a message when there is a change in state, but apparently it's not working, I am not sure where I am going wrong.
I want to update the UI when the state is changed to user logged in, but for starters I am only trying to log a message.
I also don't quiet understand the registering it and unregistering it.
This is my login activity's code.
val TAG = "LoginActivity"
//private var mDatabaseReference: DatabaseReference? = null
//private var mDatabase: FirebaseDatabase? = null
// Firebase refferences for Authentication.
private var mAuth: FirebaseAuth? = null
private var mUser : FirebaseUser? = null
private var mDatabase : DatabaseReference? = null
private var mAuthListener : FirebaseAuth.AuthStateListener? = null
//global variables
private var emailString : String? = null
private var passwordString : String? = null
// ActivityState : ONCREATE.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
// initializing firebase Auth and database Reference.
mAuth = FirebaseAuth.getInstance()
mDatabase = FirebaseDatabase.getInstance().reference
// getting the currently logined user.
mUser = mAuth?.currentUser
//[START auth_state_listener]
FirebaseAuth.AuthStateListener { firebaseAuth ->
val cuser = firebaseAuth.currentUser
if(mUser != null) {
Log.d("WOWOWOWOWO : ", "you dont girl!")
}
}
}
//ActivityState : ONSTART.
override fun onStart() {
super.onStart()
mAuth?.addAuthStateListener(mAuthListener!!)
}
//ActivityState : ONPAUSE.
override fun onPause() {
super.onPause()
}
// function for when the login button is clicked.
// TODO : Login Activity : Function# 1.
fun loginBtnClicked(view : View) {
emailString = loginEmailTxt.text.toString()
passwordString = loginPasswordtxt.text.toString()
if(!emailString.isNullOrEmpty() && !passwordString.isNullOrEmpty()) {
// Checking if the login cridentials are correct. and then changing the Auth State to logged in.
//TODO : Login Activity : Function# 3.
mAuth!!.signInWithEmailAndPassword(emailString!!, passwordString!!).addOnCompleteListener(this) { task ->
if(task.isSuccessful) {
// User ID token retrival TODO: not sure what to do with the token yet.
mUser!!.getIdToken(true)
.addOnCompleteListener(OnCompleteListener {task: Task<GetTokenResult> ->
if(task.isSuccessful) {
var idToken = task.getResult().token
Log.d(TAG, "signInWithEmail:success :" + idToken)
} else {
}
})
} else {
Log.e(TAG, "signInWithEmail:failure", task.exception)
Toast.makeText(this#LoginActivity, "Authentication failed. Make sure email and password are correct",
Toast.LENGTH_SHORT).show()
}
}
} else {
Toast.makeText(this, "Email or Password can not be empty.", Toast.LENGTH_LONG).show()
}
}
// TODO : Login Activity : Function#2
fun getHelpImgClicked(view : View) {
val builder = AlertDialog.Builder(this)
val dialogView = layoutInflater.inflate(R.layout.get_help_dialog, null)
builder.setView(dialogView)
.setNegativeButton("Close" ){ _, _ -> }.show()
}
You're never assigning a non-null value to your mAuthListener. This is the only time it's assigned:
private var mAuthListener : FirebaseAuth.AuthStateListener? = null
And this is where you pass that null value to become an auth state listener:
override fun onStart() {
super.onStart()
mAuth?.addAuthStateListener(mAuthListener!!)
}
You also create an auth state listener, but never assign that object anywhere, so it doesn't do anything:
//[START auth_state_listener]
FirebaseAuth.AuthStateListener { firebaseAuth ->
val cuser = firebaseAuth.currentUser
if(mUser != null) {
Log.d("WOWOWOWOWO : ", "you dont girl!")
}
}
Did you mean to take that AuthStateListener object and assign it to mAuthListener during onCreate()?

Resources