I am working on a simple app that should work like uber, I want each driver to upload the image of his/her car while setting up his/her profile. I have successfully picked the two images and was able to save them to the firebaseStorage, I want to retrieve the link to the downloaded car Image and save it to the driver database reference along with his profile picture and other information but I am unable to do that. I create a method that returns a String so that I can use the String returned by that method as the link to the car Image but it's showing null in my database.
The method that should return the link to the downloaded car image:
private Uri profileImageUri, carImageUri;
private StorageReference storageProfilePicsRef, storageCarImageRef;
private StorageTask uploadProfileTask, uploadCarImageTask;
private String myProfileImaeUri = "", myCarImageUri = "";
private String uploadCarImage() {
if (carImageUri != null) {
final StorageReference storageReference = storageCarImageRef.child(mAuth.getCurrentUser().getUid() + ".jpg");
uploadCarImageTask = storageReference.putFile(carImageUri);
uploadCarImageTask.continueWithTask(new Continuation() {
#Override
public Object then(#NonNull Task task) throws Exception {
if (!task.isSuccessful()) {
throw task.getException();
}
return storageReference.getDownloadUrl();
}
}).addOnCompleteListener(new OnCompleteListener<Uri>() {
#Override
public void onComplete(#NonNull Task<Uri> task) {
if (task.isSuccessful()) {
Uri downloadCarUri = task.getResult();
myCarImageUri = downloadCarUri.toString();
}
}
});
} else {
Toast.makeText(SettingsActivity.this, "Car Image Not Selected", Toast.LENGTH_SHORT).show();
}
return myCarImageUri;
}
This is the method that upload both the profile image and other information of the driver:
private void uploadProfilePicture() {
String carImageUriFromTheMethod = uploadCarImage();//I was trying to assigned the method that returns the carImage link as a String to this String type variable so that I can use it
final ProgressDialog progressDialog = new ProgressDialog(this);
progressDialog.setTitle("Updating Profile");
progressDialog.setMessage("Please wait while we are updating your account information");
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.show();
if (profileImageUri != null) {
final StorageReference fileRef = storageProfilePicsRef.child(mAuth.getCurrentUser().getUid() + ".jpg");
uploadProfileTask = fileRef.putFile(profileImageUri);
uploadProfileTask.continueWithTask(new Continuation() {
#Override
public Object then(#NonNull Task task) throws Exception {
if (!task.isSuccessful()) {
throw task.getException();
}
return fileRef.getDownloadUrl();
}
}).addOnCompleteListener(new OnCompleteListener<Uri>() {
#Override
public void onComplete(#NonNull Task<Uri> task) {
if (task.isSuccessful()) {
Uri downloadUri = task.getResult();
myProfileImaeUri = downloadUri.toString();
int selectedID = radioGroup.getCheckedRadioButtonId();
male = (RadioButton)findViewById(selectedID);
female = (RadioButton) findViewById(selectedID);
if (male.isChecked()){
selectedGender = male.getText().toString();
}else if (female.isChecked()){
selectedGender = female.getText().toString();
}else {
selectedGender = "Not specify";
}
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("ProfileImage", myProfileImaeUri);
hashMap.put("name", name.getText().toString());
hashMap.put("city", city.getText().toString());
hashMap.put("age", age.getText().toString());
hashMap.put("maritalStatus", maritalStatus.getText().toString());
hashMap.put("phoneNumber", phoneNumber.getText().toString());
hashMap.put("country", country.getText().toString());
hashMap.put("address", address.getText().toString());
hashMap.put("gender", selectedGender);
hashMap.put("carName",carName.getText().toString());
hashMap.put("carColour", carColor.getText().toString());
hashMap.put("carPlateNumber", carPlateNumber.getText().toString());
hashMap.put("carImage", carImageUriFromTheMethod);//I now call the string here
}
databaseReference.child(mAuth.getCurrentUser().getUid()).updateChildren(hashMap);
progressDialog.dismiss();
if (getType.equalsIgnoreCase("Driver")) {
startActivity(new Intent(SettingsActivity.this, DriversMapsActivity.class));
Toast.makeText(SettingsActivity.this, "Profile Information Updated Successfully", Toast.LENGTH_SHORT).show();
finish();
} else {
startActivity(new Intent(SettingsActivity.this, CustomerMapsActivity.class));
Toast.makeText(SettingsActivity.this, "Profile Information Updated Successfully", Toast.LENGTH_SHORT).show();
finish();
}
} else {
Toast.makeText(SettingsActivity.this, "Error Occured", Toast.LENGTH_SHORT).show();
progressDialog.dismiss();
}
}
});
} else {
Toast.makeText(SettingsActivity.this, "Profile Image Not Selected", Toast.LENGTH_SHORT).show();
}
}
Here is my database which shows that the carImage is null, but there is a car Image in the firebaseStorage:
"Driver": {
"2oKrtyUa3FX1wyHAhplsxbVaPZU2": {
"ProfileImage": "https://firebasestorage.googleapis.com/v0/b/ride-booking-app-e8a95.appspot.com/o/Profile%20Images%2F2oKrtyUa3FX1wyHAhplsxbVaPZU2.jpg?alt=media&token=442b14b3-ffb8-4356-b0c0-4c77021fe391",
"address": "Ikot Obong",
"age": "28",
"carColour": "black",
"carImage": "",//this is where the link supposed to appear as it's seen in the profile image
"carName": "Range Rover sport",
"carPlateNumber": "aa377-ktm",
"city": "Ikot Abasi",
"country": "Nigeria",
"gender": "Male",
"maritalStatus": "single",
"name": "Captain Elijah",
"phoneNumber": "08168989070"
}
}
Thanks for help
All the operations you are doing (uploading the image, getting its download URL, and writing to the database) are asynchronous, which your code ignores.
If you log the myCarImageUri in uploadCarImage right before you return it, you'll see it's null, which is the case because your onComplete for the upload hasn't run yet (another log would show that too).
The correct way to handle the uploads is as you already do for the myProfileImaeUri in your uploadProfilePicture method: nesting the callbacks, so that they execute in the correct order. You'll need to do the same for the car image.
To learn more about this problem, I recommend checking out some of the top answers on the asynchronous behavior of getDownloadUrl(), such as my answers to:
Can someone help me with logic of the firebase on success listener
How do I get files synchronized from Firebase in Android?
Why can't this retieve the download URL of an image on Firebase Storage?
I'd also recommend reading Doug's excellent series of blog posts on becoming a Firebase task master.
Related
There's a mistake in this code:
String download_url=task.getResult.getStorage.getDownloadUrl.toString);
When I run the program, I choose a picture from the gallery and I post it, and I get a message:
User is not authenticated, please authenticate using Firebase Authentication and try again
final StorageReference newPhoto=mPhotosStrorage.child(imageUri.getLastPathSegment());
newPhoto.putFile(imageUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
#Override
public void onComplete(#NonNull Task<UploadTask.TaskSnapshot> task) {
if (task.isSuccessful())
{
final String myKey=mPhotosDatabase.push().getKey();
//this error String download_url=task.getResult().getDownloadUrl().toString();
String datem=getDateTime();
DatabaseReference newDatabase=mPhotosDatabase.child(myKey);
newDatabase.child("postid").setValue(myKey);
newDatabase.child("postedby").setValue(userId);
newDatabase.child("postedon").setValue(datem);
newDatabase.child("postdetails").setValue(post);
newDatabase.child("postlikes");
newDatabase.child("postviews");
newDatabase.child("postcomments");
newDatabase.child("postimage").setValue(download_url).addOnCompleteListener(new OnCompleteListener<Void>() {
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful())
{
pb.setVisibility(View.GONE);
Pair[] pairs=new Pair[1];
pairs[0]=new Pair<View,String>(homeLayout,"etTransition");
ActivityOptions options=ActivityOptions.makeSceneTransitionAnimation(PostActivity.this,pairs);
startActivity(new Intent(PostActivity.this,HomeActivity.class),options.toBundle());
}
}
});
}else {
Toast.makeText(PostActivity.this, "Error:"+task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
Please help solve it and rewrite the code for me.
According to your last comment, the problem is at the following line of code:
String download_url=task.getResult().getDownloadUrl().toString();
Please note that this is not how you get the download URL nowadays. Converting that Task object to String isn't helpful at all. To solve this, please use the following lines of code:
storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String url = uri.toString();
//Do what you need to do with the URL
}
});
Besides that, always make sure to have the user authenticated, as that error occurs only when the user is not authenticated and the rules are set to reject the operations.
I'm trying to build an app with biometric authentication (Fingerprint) and I'm having some troubles with the negative button.
The button works, but for some reason is completely invisible.
This is how the app shows
And this is how it sees when you prees the button. As you can see, it exist, but i don't know how to make it visible
I'm using BiometricPrompt and BiometricManager in Java.
Edit: It seems that the button shows normally in any other phone that isn't mine
I'm using a Xiaomi Redmi Note 8.
However this is the code that I'm using:
private void initViews()
{
biometricManager = BiometricManager.from(this);
passwordEditText=findViewById(R.id.passwordText);
loginButton=findViewById(R.id.loginButton);
switch (biometricManager.canAuthenticate()) {
case BiometricManager.BIOMETRIC_SUCCESS:
Log.d("MY_APP_TAG", "App can authenticate using biometrics.");
break;
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
Log.e("MY_APP_TAG", "No biometric features available on this device.");
break;
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
Log.e("MY_APP_TAG", "Biometric features are currently unavailable.");
break;
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
Log.e("MY_APP_TAG", "The user hasn't associated " +
"any biometric credentials with their account.");
break;
}
executor = ContextCompat.getMainExecutor(this);
biometricPrompt = new BiometricPrompt(EnterYourPassActivity.this,
executor, new BiometricPrompt.AuthenticationCallback() {
#Override
public void onAuthenticationError(int errorCode,
#NonNull CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
if(errString.equals("Use account password"))
{
passwordEditText.setVisibility(View.VISIBLE);
}
else
{
Log.d("MY_APP_TAG",""+errString);
Toast.makeText(getApplicationContext(),
"Authentication error: " + errString, Toast.LENGTH_SHORT)
.show();
}
}
#Override
public void onAuthenticationSucceeded(
#NonNull BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
Toast.makeText(getApplicationContext(),
"Authentication succeeded!", Toast.LENGTH_SHORT).show();
Intent seeingFiles = new Intent(EnterYourPassActivity.this, SeeingFilesActivity.class);
startActivity(seeingFiles);
}
#Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
Toast.makeText(getApplicationContext(), "Authentication failed",
Toast.LENGTH_SHORT)
.show();
}
});
promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle("Biometric login for my app")
.setSubtitle("Log in using your biometric credential")
.setNegativeButtonText("Use account password")
.build();
loginButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
biometricPrompt.authenticate(promptInfo);
}
});
}
Try to change negativeButton text in this way:
Important - put string into resources:
<string name="negative_button_text"><![CDATA[<font color=\'#48a134\'>Your text at given color</font>]]></string>
As you can see, you can set text color in hex.
Now put negativeText into BiometricPrompt as follows:
val negativeButtonText = getString(R.string.negative_button_text)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("title")
.setDescription("description")
.setNegativeButtonText(fromHtml(negativeButtonText))
.build()
fun fromHtml(html: String): Spanned {
return HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
}
In given example negativeText is green and prompt looks like THIS.
When using createUserWithEmailAndPassword there are two issues,
It works on the Emulator, but not in debug mode on a physical device
To make it work on a physical device I can hardcode a string for the email portion, even if it is saved into a variable. It breaks when getting the email from an EditText, but I have used Log.d() to confirm that the string is exactly the same before the creation method is called.
This works
final String sEmail = "ExampleEmail#gmail.com";
final String sPassword = password.getText().toString();
final String sDisplayName = displayName.getText().toString();
Log.d("Credentials:Email", sEmail);
Log.d("Credentials:Password", sPassword);
mAuth.createUserWithEmailAndPassword(sEmail,sPassword).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
FirebaseUser user = mAuth.getCurrentUser();
/*UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(sDisplayName).build();
user.updateProfile(profileUpdates);
mAuth.signOut();*/
Intent intent = new Intent(MainActivity.this,UserLogin.class);
startActivity(intent);
}else{
Toast creationFailed = Toast.makeText(getApplicationContext(),"Creation Failed", Toast.LENGTH_SHORT);
creationFailed.show();
}
}
});
This does not.
final String sEmail = email.getText().toString();
final String sPassword = password.getText().toString();
final String sDisplayName = displayName.getText().toString();
Log.d("Credentials:Email", sEmail);
Log.d("Credentials:Password", sPassword);
mAuth.createUserWithEmailAndPassword(sEmail,sPassword).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()){
FirebaseUser user = mAuth.getCurrentUser();
/*UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(sDisplayName).build();
user.updateProfile(profileUpdates);
mAuth.signOut();*/
Intent intent = new Intent(MainActivity.this,UserLogin.class);
startActivity(intent);
}else{
Toast creationFailed = Toast.makeText(getApplicationContext(),"Creation Failed", Toast.LENGTH_SHORT);
creationFailed.show();
}
}
});
Here is the error message
2019-01-14 13:44:48.298 3217-16541/? E/Volley: [1455] BasicNetwork.performRequest: Unexpected response code 400 for https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?alt=proto&key=AIzaSyCTfahJaTfOSAOdY7_pIN27-BGQgFlORnE
Note that for some reason, the second one that does not work will work on an emulated device. I expect the second to create the account, but it just fails.
I assumed that your input is not email type. Please keep in mind that it need email type.
Make a method to check email valid
//Check email valid
boolean isEmailValid(CharSequence email) {
return android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches();
}
Then apply to your part.
//Check email valid
if (!isEmailValid(email.getText().toString())) {
email.setError("Not email type");
email.requestFocus();
return;
}
About your toast, maybe it would be better if you put like this. So, you can know what error that you faced.
else {
Toast.makeText(getApplicationContext(), "Fail: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
I am currently using Firebase to upload an image into Firebase content in Android Studio. Once the file has successfully uploaded, I receive the url of the uploaded image using taskSnapshot.getDownloadUrl(). I want to then put this url in the Firebase Real-time database as a reference. I'm struggling to access the taskSnapshot.getDownloadUrl() from outside the onSuccess() method. Any ideas, please?
I understand that you want to put the uploaded image URL into Firebase Database.
What you can do is, Declare a global variable of type String
private String imageUrl;
Then get the url of image inside onSuccess() method and store in the string
imageUrl = taskSnapshot.getDownloadUrl().toString();
then after the image is uploaded, you have the image URL as string, so just push it to database like this
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("ImageUrl").setValue(imageUrl);
# declare variable like below:
String Storage_Path = "Image_Uploads/";
String Database_Path = "All_Image_Uploads_Database";
# declare firebase storage or real time database variable like below :
StorageReference myrefrence;
DatabaseReference databaseReference;
myrefrence = FirebaseStorage.getInstance().getReference();
databaseReference = FirebaseDatabase.getInstance().getReference(Database_Path);
public void UploadImageFileToFirebaseStorage(Uri FilePathUri) {
// Checking whether FilePathUri Is empty or not.
if (FilePathUri != null) {
// Setting progressDialog Title.
// progressDialog.setTitle("Image is Uploading...");
// Showing progressDialog.
// progressDialog.show();
// Creating second StorageReference.
StorageReference storageReference2nd = myrefrence.child(Storage_Path + System.currentTimeMillis() + "." + GetFileExtension(FilePathUri));
// Adding addOnSuccessListener to second StorageReference.
storageReference2nd.putFile(FilePathUri)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// Getting image name from EditText and store into string variable.
//String TempImageName = ImageName.getText().toString().trim();
// Hiding the progressDialog after done uploading.
// progressDialog.dismiss();
// Showing toast message after done uploading.
//Toast.makeText(getApplicationContext(), "Image Uploaded Successfully ", Toast.LENGTH_LONG).show();
#SuppressWarnings("VisibleForTests")
ImageUploadInfo imageUploadInfo = new ImageUploadInfo("",taskSnapshot.getDownloadUrl().toString());
// Getting image upload ID.
String ImageUploadId = databaseReference.push().getKey();
// Adding image upload id s child element into databaseReference.
databaseReference.child(ImageUploadId).setValue(imageUploadInfo);
}
})
// If something goes wrong .
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
// Hiding the progressDialog.
//progressDialog.dismiss();
// Showing exception erro message.
// Toast.makeText(MainActivity.this, exception.getMessage(), Toast.LENGTH_LONG).show();
}
})
// On progress change upload time.
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
#Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
// Setting progressDialog Title.
//progressDialog.setTitle("Image is Uploading...");
}
});
} else {
//Toast.makeText(MainActivity.this, "Please Select Image or Add Image Name", Toast.LENGTH_LONG).show();
}
}
// Creating Method to get the selected image file Extension from File Path URI.
public String GetFileExtension(Uri uri) {
ContentResolver contentResolver = getContentResolver();
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
// Returning the file Extension.`enter code here`
return mimeTypeMap.getExtensionFromMimeType(contentResolver.getType(uri));
}
In my Firebase application I created a fragment that allows users to update their information like namn & email and so far all is going well, however my issue is after the user have updated the information - the changes are not visible untill next relaunch of the application.
How can I reflect the changes directly from the databse without promoting the User to relaunch the app?
I have created a Method called restart(); that will like the name says says restart the application - But still the changes are not being reflected!
/**
* Update Name Only
*/
private void updateDisplayNameOnly() {
showProgress();
AuthCredential credential = EmailAuthProvider
.getCredential(FirebaseAuth.getInstance().getCurrentUser().getEmail(), mConfirm.getText().toString());
FirebaseAuth.getInstance().getCurrentUser().reauthenticate(credential)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
UserProfileChangeRequest profileUpdate = new UserProfileChangeRequest.Builder()
.setDisplayName(mName.getText().toString())
//.setPhotoUri(Uri.parse("https://avatarfiles.alphacoders.com/862/86285.jpg"))
.build();
user.updateProfile(profileUpdate);
Log.d(TAG, "onComplete: User Profile updated");
Toast.makeText(getActivity(), "Name is updated", Toast.LENGTH_SHORT).show();
restartApp();
} else {
Toast.makeText(getActivity(), "Name was not updated", Toast.LENGTH_SHORT).show();
}
hideProgress();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
hideProgress();
Toast.makeText(getActivity(), "You have entered wrong password", Toast.LENGTH_SHORT).show();
}
});
}
Restart Method
public void restartApp() {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
Firebase has a listener onDataChange(), so when you query data from firebase, make sure you implement that, see doc. If you want to reflect the change, implement it in this method (like resetting the text fields). There is no need for a restart method.
Update profile call is asynchronous, and its results are not available immediately, so for some time you'll still observe "obsolete" data.
When you call updateProfile, you get a task as a result. You can subscribe on completion of this task, and if it's completed successfully, then you will be able to get updated data from user instance. E.g.:
final FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
Log.i("MyActivity", "before updateProfile: username=" + currentUser.getDisplayName());
UserProfileChangeRequest profileUpdate = new UserProfileChangeRequest.Builder()
.setDisplayName("UPDATED_NAME")
.build();
final Task<Void> task = currentUser.updateProfile(profileUpdate);
Log.i("MyActivity", "after updateProfile: username=" + currentUser.getDisplayName());
task.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.i("MyActivity", "onComplete: username=" + currentUser.getDisplayName());
}
});
And here is an output:
I/MyActivity: before updateProfile: username=old name
I/MyActivity: after updateProfile: username=old name
I/MyActivity: onComplete: username=UPDATED_NAME
Also there is a method user.reload(), which force reload user data from Firebase server. This is useful when your client user cache is obsolete for some reason. It is also an asynchronous method, which gives you Task and you need to subscribe on its completion.