Kotlin upload image to firebase is not working - firebase

When I tried to run the program and upload the image to the firebase, it doesn't success... Anyone can help me.. It looks like the Task is not success but I have do some research and still can't solve it.
val fileRef :StorageReference = storageProfilePicRef!!.child(firebaseUser!!.uid + ".jpg")
var uploadTask: StorageTask<*>
uploadTask = fileRef.putFile(imageUri!!)
uploadTask.continueWithTask(Continuation <UploadTask.TaskSnapshot, Task<Uri>>{ task ->
if(!task.isSuccessful){
task.exception?.let {
throw it
progressDialog.dismiss()
}
}
return#Continuation fileRef.downloadUrl
//not able to download the image url successfully so cannot go in task.isSuccessful
}).addOnCompleteListener(OnCompleteListener<Uri>{ task ->
if (task.isSuccessful){
Toast.makeText(this, "123.", Toast.LENGTH_LONG).show()
val downloadUrl = task.result
myUrl = downloadUrl.toString()
val ref = FirebaseDatabase.getInstance().reference.child("Users")
val userMap = HashMap<String, Any>()
userMap["fullname"] = full_name_profile_frag.text.toString().lowercase()
userMap["username"] = username_profile_frag.text.toString().lowercase()
userMap["bio"] = bio_profile_frag.text.toString().lowercase()
userMap["image"] = myUrl
ref.child(firebaseUser.uid).updateChildren(userMap)
Toast.makeText(this, "Account Information has been updated successfully", Toast.LENGTH_LONG).show()
val intent = Intent(this#AccountSettingsActivity, MainActivity::class.java)
startActivity(intent)
finish()
progressDialog.dismiss()
}
else{
Toast.makeText(this, task.toString(), Toast.LENGTH_LONG).show()
progressDialog.dismiss()
}
})

Related

Google authentication with Firebase and Jetpack Compose

I'm using Kotlin, Android Studio and Jetpack Compose
What I want to do:
After the button click, I would want to register the user using google authentication and firebase
What I've tried to do:
I've tried to follow Firebase Auth Documentation, but it's really hard to understand as it often sends me back to the Google Documentation, which isn't helpful either in the case of Jetpack Compose.
I couldn't find any up to date videos or guides explaining this implementation. It would be awesome if someone could explain how to start with it.
val context = LocalContext.current
val token = stringResource(R.string.default_web_client_id)
val launcherNav = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) {
navController.navigate(Screen.MainScreen.route)
}
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
) {
val task =
try {
val account = GoogleSignIn.getSignedInAccountFromIntent(it.data)
.getResult(ApiException::class.java)
val credential = GoogleAuthProvider.getCredential(account.idToken!!, null)
FirebaseAuth.getInstance().signInWithCredential(credential)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
}
}
}
catch (e: ApiException) {
Log.w("TAG", "GoogleSign in Failed", e)
}
}
Button(
onClick = {
val gso = GoogleSignInOptions
.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(token)
.requestEmail()
.build()
val googleSignInClient = GoogleSignIn.getClient(context, gso)
launcher.launch(googleSignInClient.signInIntent)
}
) {
Text(text = "Sign In")
}

I'm trying to create a new firebase user, and upload the user data to firebase database, but when I try yo get the uid it's always null

I am trying to get the user uid after creating an account, so I can create a document on Firestore with the uid as document id. The problem is that I get only null, everything is working fine and I'm able to sign the user and receive the code but the uid is always null.
I know I'm accessing the uid before it's initialized because it takes time to restore the authentication state when the app starts so any idea how to wait for it until it's initialized?
First of all, if the data entered is correct then we call login() function, then we send the verification code, when the code is sent inside the onCodeSent(), the method we call uploadSelectedImageToFirebaseStorage() to upload the user image then we call saveUserToDatabase() function, and here is the problem. Inside the saveUserToDatabase() function, the UID is always null no matter what I tried, am I missing something?
#file:Suppress("DEPRECATION")
class SignUpActivity : AppCompatActivity() {
private lateinit var binding: ActivitySignUpBinding
private lateinit var auth: FirebaseAuth
private lateinit var callbacks: PhoneAuthProvider.OnVerificationStateChangedCallbacks
lateinit var storedVerificationId: String
lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySignUpBinding.inflate(LayoutInflater.from(this))
auth = FirebaseAuth.getInstance()
setContentView(binding.root)
val flProfilePicture = binding.frameProfilePicture
val tilUserName = binding.tilUserName
val tilPhoneNumber = binding.tilPhoneNumber
val startButton = binding.startButton
val currentUser = auth.currentUser
if (currentUser != null) {
val intent = Intent(this#SignUpActivity, MainChatsActivity::class.java)
startActivity(intent)
finish()
}
callbacks = object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) { }
override fun onVerificationFailed(e: FirebaseException) { }
override fun onCodeSent(
verificationId: String,
token: PhoneAuthProvider.ForceResendingToken,
) {
Toast.makeText(baseContext, "Code Sent", Toast.LENGTH_SHORT).show()
storedVerificationId = verificationId
resendToken = token
val intent = Intent(applicationContext, AuthenticatePhoneActivity::class.java)
intent.putExtra("storedVerificationId", storedVerificationId)
uploadSelectedImageToFirebaseStorage()
startActivity(intent)
finish()
}
}
flProfilePicture.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
startActivityForResult(intent, 0)
}
startButton.setOnClickListener {
if (TextUtils.isEmpty(tilUserName.text.toString())) {
tilUserName.error = "Enter valid Name"
}
if (TextUtils.isEmpty(tilPhoneNumber.text.toString())) {
tilPhoneNumber.error = "Enter valid phone number"
} else {
val userName: String = tilUserName.text.toString()
val userPhoneNumber: String = tilPhoneNumber.text.toString()
login()
}
}
}
private var selectedProfilePicture: Uri? = null
#Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 0 && data != null) {
selectedProfilePicture = data.data
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, selectedProfilePicture)
val flProfilePicture = binding.frameProfilePicture
val selectedCircleFrame = binding.selectedPictureCircleFrame
selectedCircleFrame.setImageBitmap(bitmap)
flProfilePicture.alpha = 0f
}
}
private fun login() {
val mobileNumber = binding.tilPhoneNumber
val number = mobileNumber.text.toString().trim()
if (number.isNotEmpty()) {
sendVerificationCode(number)
} else {
mobileNumber.error = "Enter a valid phone number"
}
}
private fun sendVerificationCode(number: String) {
val options = PhoneAuthOptions.newBuilder(auth)
.setPhoneNumber(number)
.setTimeout(60L, TimeUnit.SECONDS)
.setActivity(this)
.setCallbacks(callbacks)
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
private fun uploadSelectedImageToFirebaseStorage() {
if (selectedProfilePicture == null) {
return
}
val fileName = UUID.randomUUID().toString()
val ref = FirebaseStorage.getInstance().getReference("/images/$fileName")
ref.putFile(selectedProfilePicture!!)
.addOnSuccessListener {
ref.downloadUrl.addOnSuccessListener {
it.toString()
Log.d("SignUpActivity", "image uploaded successfully")
saveUserToDatabase(it.toString())
}
}
.addOnFailureListener {
saveUserToDatabase(it.toString())
}
}
private fun saveUserToDatabase(profileImageUrl: String) {
val tilUserName = binding.tilUserName.text.toString()
val tilPhoneNumber = binding.tilPhoneNumber.text.toString()
val uid = FirebaseAuth.getInstance().uid.toString()
val database = Firebase.database("https://blend-4a9e4-default-rtdb.asia-southeast1.firebasedatabase.app")
val myRef = database.getReference("/users/$uid")
val user = User(uid, tilPhoneNumber, tilUserName, profileImageUrl)
Log.d("currentUser", uid)
myRef.setValue(user)
.addOnFailureListener {
Toast.makeText(baseContext, "Something went wrong, try again.", Toast.LENGTH_SHORT).show()
}
}
}

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.

A value of type 'Future<String>' can't be assigned to a variable of type 'String'

I have this code where am supposed to upload an image and get the downloaded url but whenever i do that I get this error
my url is String url;. So please why is this not working as it is supposed to
PS
I checked other website to learn how to properly upload but it keeps giving me an error or is there a better way to do this.
My code image
uploadTask.whenComplete(()async{
url = await refs.getDownLoadURL();
....
});
Since it returns a Future you need to wait for it to be accessed
Example :
Future<String> createFolder(String folderName) async {
final dir = Directory(
'${(io.Platform.isAndroid ? await getExternalStorageDirectory() //FOR ANDROID
: await getApplicationSupportDirectory() //FOR IOS
)!.path}/$folderName');
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if ((await dir.exists())) {
return dir.path;
} else {
dir.create();
return dir.path;
}
}
Future<String> getIslamiSahittoBookFilePath(String savename) async {
Future<String> s = createFolder("Islami_Sahitto");
String filePath = await s;
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
//add more permission to request here.
].request();
io.File? f = null;
if (statuses[Permission.storage]!.isGranted) {
Directory? dir = await DownloadsPath.downloadsDirectory();
if (dir != null) {
String savePath = "${dir.path}/$filePath/$savename";
f = new io.File(savePath);
if (await f.exists()) {}
}
}
return f.toString();
}
Now this block You can use AnyWhere : Future String, to String :
bool isPreviousDownloaded = false;
String previousFilePath = "null";
getIslamiSahittoBookFilePath(fileNameToDownload).then((value) {
if (value != null) {
setState(() {
isPreviousDownloaded = true;
previousFilePath = value;
});
}
});

Snapshot Listener Permission Error while Signing Out - Kotlin/Firestore

This is my first time setting auth rules using Firestore, and I can't seem to figure out how to fix this problem. I apologize if this is a repeat, however I cannot find what I'm missing. When I logout of my app, I get a Permission Denied with the below code, even when I return the listener if there is an error. How can I prevent this from happening?
db.collection("chats").whereEqualTo(currentUser, true)
.addSnapshotListener {snapshot, error ->
if (error != null) {
Log.e("ChatsFragment", "Error getting chats. Error: $error")
return#addSnapshotListener
}
if (snapshot!!.isEmpty) {
noMsgs.visibility = View.VISIBLE
} else {
noMsgs.visibility = View.GONE
}
}
Here is my Logout just in case.
AuthUI.getInstance().signOut(this)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val intent = Intent(this, CheckUser::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
finish()
handler.postDelayed({
startActivity(intent)
Toast.makeText(this, "You have been logged out.", Toast.LENGTH_SHORT).show()
}, 1000)
} else {
val intent = Intent(this, CheckUser::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
finish()
startActivity(intent)
Log.e("LogoutActivity", "Error logging out. ${task.exception?.message.toString()}")
}
}

Resources