I am building a Kotlin app and am using FirebaseAuth for login. I activated Facebook login via Firebase and it works fine. My problem is that the created Firebase user's photoUrl: (https://graph.facebook.com/<UserId>/picture) is pointing to a tiny version of it's Facebook profile picture, instead of a normal sized one.
I found this solution: https://stackoverflow.com/a/52099896/13674106. The Google part of this answer worked perfectly for me, but in the Facebook case I am now getting a default avatar image (in the right resolution). My code looks like this:
fun handleSignInSucceeded(dataIntent: Intent?) {
val response = IdpResponse.fromResultIntent(dataIntent)
// For new created users, check if a photo is available from the auth provider
if (response?.isNewUser == true) {
// All users have a default FirebaseAuth provider data - we want to check which is the
// other one
currentUser.value?.providerData
?.find { it.providerId != FirebaseAuthProvider.PROVIDER_ID }?.apply {
val photoUrl = when (providerId) {
GoogleAuthProvider.PROVIDER_ID ->
photoUrl.toString().replace("s96-c", "s400-c").toUri()
FacebookAuthProvider.PROVIDER_ID ->
photoUrl.toString().plus("?type=large").toUri()
else -> null
}
photoUrl?.let { updatePhotoUri(it, false) }
}
}
}
fun updatePhotoUri(photoUrl: Uri, uploadToStorage: Boolean = true): Task<Void>? {
val profileUpdates = UserProfileChangeRequest.Builder()
return if (uploadToStorage) ...
else updateProfile(profileUpdates.setPhotoUri(photoUrl).build())
}
private fun updateProfile(profileUpdates: UserProfileChangeRequest): Task<Void>? {
return currentUser.value?.updateProfile(profileUpdates)
?.addOnCompleteListener {
if (it.isSuccessful) {
Log.d(TAG, "User profile updated.")
_currentUser.value = firebaseAuth.currentUser
}
}
}
Where _currentUser is a private MutableLiveData<FirebaseUser?> shadowed by a public LiveData<FirebaseUser?> called user which is binded to my ImageView on the fragment:
<ImageView
android:id="#+id/image_profile_picture"
...
app:userPhotoSrc="#{profileViewModel.user}" />
Where userPhotoSrc is implemented by the following BindingAdapter using Glide:
#BindingAdapter("userPhotoSrc")
fun ImageView.setUserPhotoSrc(user: FirebaseUser?) {
Glide.with(context)
.load(user?.photoUrl)
.circleCrop()
.fallback(R.drawable.ic_profile_black_24)
.into(this)
}
I checked in debug and the value of user?.photoUrl at that point is https://graph.facebook.com/<UserId>/picture?type=large as expected.
I found this thread: Retrieving Default Image All Url Profile Picture from Facebook Graph API, Which shows a problem similar to mine, but according to the answers there, my code should work by now. I also tried to use the height parameter instead of type, like stated in this answer: https://stackoverflow.com/a/50710161/13674106, But I got the same result.
Please help me solve it,
Omer
Related
I'm exploring the Microsoft Graph UWP Tutorial [1]: https://learn.microsoft.com/en-us/graph/tutorials/uwp?tutorial-step=1 and having difficulty with the app.
An inconsistent behavior occurs with the "Sign-In". Sometimes after the username and password are entered the information is "accepted" and a token is provided. Control then opens the HomePage. However, after signing out and re-entering the login details, the app just hangs indefinitely and a little blue flashing dot appears in the upper left hand corner.
I have tried multiple live.com user accounts and the behavior is the same. Since I use the MSAL for my other apps, I'm seeing the same result. I'm using VS 2022 .
Here is the affected code:
public MainPage()
{
this.InitializeComponent();
// Load OAuth settings
var oauthSettings = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView("OAuth");
var appId = oauthSettings.GetString("AppId");
var scopes = oauthSettings.GetString("Scopes");
if (string.IsNullOrEmpty(appId) || string.IsNullOrEmpty(scopes))
{
Notification.Show("Could not load OAuth Settings from resource file.");
}
else
{
// Configure MSAL provider
MsalProvider.ClientId = appId;
MsalProvider.Scopes = new ScopeSet(scopes.Split(' '));
// Handle auth state change
ProviderManager.Instance.ProviderUpdated += ProviderUpdated;
// Navigate to HomePage.xaml
RootFrame.Navigate(typeof(HomePage));
}
}
// </ConstructorSnippet>
// <ProviderUpdatedSnippet>
private void ProviderUpdated(object sender, ProviderUpdatedEventArgs e)
{
var globalProvider = ProviderManager.Instance.GlobalProvider;
SetAuthState(globalProvider != null && globalProvider.State == ProviderState.SignedIn);
RootFrame.Navigate(typeof(HomePage));
}
// </ProviderUpdatedSnippet>
// <SetAuthStateSnippet>
private void SetAuthState(bool isAuthenticated)
{
(Application.Current as App).IsAuthenticated = isAuthenticated;
// Toggle controls that require auth
Calendar.IsEnabled = isAuthenticated;
NewEvent.IsEnabled = isAuthenticated;
}
// </SetAuthS
It is very inconsistent..sometimes the login/password is accepted and the program continues, however, most of the times, it just hangs.
I've checked for some type of "time-out" setting where multiple logins with the same time period will not be accepted, but could find no solution.
And yes, I've checked with MS Forums, but that has been a bit of a black hole.
I have recently started working on an android project that uses Microsft authentication and graph API. By following this:
https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-android
I am able to authenticate and get some data from graph API. Now I want to load the profile photo of Microsoft account in the app. For this purpose, I used ProfilePhoto object with a call back as follows:
graphClient
.me()
.photo()
.buildRequest()
.get(new ICallback<ProfilePhoto>() {
#Override
public void success(ProfilePhoto profilePhoto) {
Log.d(TAG, "Found " + profilePhoto.getRawObject().toString());
}
#Override
public void failure(ClientException ex) {
displayError(ex);
}
});
Here profilePhoto.getRawObject() returns a json file like:
{"#odata.context":"https://graph.microsoft.com/v1.0/$metadata#users('hanzla_hawk%40outlook.com')/photo/$entity","#odata.mediaContentType":"image/png","#odata.mediaEtag":"W/\"8050a078da935403cf67163f23f1baace5c7abf3ff784452cb08c38660308a83\"","id":"default","height":256,"width":256}
With this Json, how can I load the image into an image view? I have previous experience with Picasso and other fake apis. But right now I just dont know what should I pass in the Picasso to load image from this json.
I have just make a call to get a profile photo to show on an Android app using Jetpack Compose views in Kotlin. To achieve it I have followed your question and this tutorial:
https://learn.microsoft.com/en-us/graph/tutorials/android
You almost got it. Just add .content() call between .me().photo() and .buildRequest().
This is my code on my project to get the photo content:
// GET /me/photo/$value (logged in user)
fun getUserPhoto(
onGotPhoto: (ImageBitmap) -> Unit,
onGotError: (Exception) -> Unit
) {
mClient!!.me().photo().content().buildRequest()
.async
.thenAccept { inputStream ->
val bitmap = BitmapFactory.decodeStream(inputStream).asImageBitmap()
onGotPhoto.invoke(bitmap)
}
.exceptionally { processError(it, onGotError) }
}
I new in android , i'm attempting open gmail app through Intent , i have clicked on menu bottom for open E-address in gmail , and i have got problem top sentence .
this is my code
private fun menuClicks() {
binding?.toolbar?.toolbar?.setOnMenuItemClickListener {
when(it.itemId){
R.id.conact ->{
val client = Intent(Intent.ACTION_VIEW , Uri.parse("zhanysch#gmail.com"))
startActivity(client)
return#setOnMenuItemClickListener true
}
R.id.FAQ ->{
findNavController().navigate(R.id.action_mainFragment_to_faqFragment)
return#setOnMenuItemClickListener true
}
R.id.terms ->{
findNavController().navigate(R.id.action_mainFragment_to_termsConditionsFragment)
return#setOnMenuItemClickListener true
}
R.id.Privacy -> {
findNavController().navigate(R.id.action_mainFragment_to_privacyFragment)
return#setOnMenuItemClickListener true
}
else -> super.onOptionsItemSelected(it)
}
}
}
what the problem can any one help me
Your intent for opening Gmail is wrong. I recommend looking into "Send email intent" in Android.
But for the simple case, change this:
val client = Intent(Intent.ACTION_VIEW , Uri.parse("zhanysch#gmail.com"))
into this:
val client = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:zhanysch#gmail.com"))
For a better, more generic solution, I recommend checking the official docs.
I have successfully configured iOS app to handle audio document types (not deeplinks!) so I can use share dialog to open audio files in my app, this works fine.
I have successfully configured Android intent-filter in the AndroidManifest.xml to hande audiofiles same way, this is also recognized fine by Android system and I see my app when I use share menu on audiofiles. However, Android implementation of Linking component seems to ignore SEND intent actions and only care about View intent actions as I saw on IntentModule.java:55:
if (Intent.ACTION_VIEW.equals(action) && uri != null) {
initialURL = uri.toString();
}
I tried to patch intent in my MainActivity.java to make it return same uri with VIEW action, but this always produces an error in runtime.
#Override
public Intent getIntent() {
Intent origIntent = super.getIntent();
if (origIntent != null && Intent.ACTION_SEND.equals(origIntent.getAction())) {
return new Intent(Intent.ACTION_VIEW, origIntent.getData());
}
return origIntent;
}
However I always get an error indicating that getData is null.
I saw this answer, but having a share extension is an overkill for me.
I discovered that Intent might contain additional information called ClipData, which can contain Uri as well. My luck was that I had exactly one Uri (I guess it can contain multiple Uri objects if I share multiple audiofiles at once. So this code worked, I can now share files to react native app
#Override
public Intent getIntent() {
Intent origIntent = super.getIntent();
if (origIntent != null && Intent.ACTION_SEND.equals(origIntent.getAction())) {
return new Intent(Intent.ACTION_VIEW, this.uriFromClipData(origIntent.getClipData()));
}
return origIntent;
}
private Uri uriFromClipData(ClipData clip) {
if (clip != null && clip.getItemCount() > 0) {
return clip.getItemAt(0).getUri();
}
return null;
}
How can I get public info of a user from google plus login button integrated on the site, here is the code which is giving me email, I need more info which is provide by google plus :
<div id="signin-button" class="show">
<div class="g-signin" data-callback="loginFinishedCallback"
data-approvalprompt="force"
data-clientid="9076269517.apps.googleusercontent.com"
data-scope="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email"
data-height="short"
data-cookiepolicy="single_host_origin">
</div>
java script :
function loginFinishedCallback(authResult) {
if (authResult) {
if (authResult['error'] == undefined){
gapi.auth.setToken(authResult); // Store the returned token.
toggleElement('signin-button'); // Hide the sign-in button after successfully signing in the user.
getEmail(); // Trigger request to get the email address.
} else {
console.log('An error occurred');
}
} else {
console.log('Empty authResult'); // Something went wrong
}
}
function getEmail(){
// Load the oauth2 libraries to enable the userinfo methods.
gapi.client.load('oauth2', 'v2', function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(getEmailCallback);
});
}
function getEmailCallback(obj){
var el = document.getElementById('email');
var email = '';
if (obj['email']) {
email = 'Email: ' + obj['email'];
}
//console.log(obj); // Uncomment to inspect the full object.
el.innerHTML = email;
toggleElement('email');
}
function toggleElement(id) {
var el = document.getElementById(id);
if (el.getAttribute('class') == 'hide') {
el.setAttribute('class', 'show');
} else {
el.setAttribute('class', 'hide');
}
}
I tried replacing email with name, userId but getting nothing from these variables.
How can I get basic information of a user when he is logged in through google plus.
Similar to how you have loaded the oauth2 v2 client package using gapi.client.load, you will use this again to load the plus v1 client package. This will give you a number of packages and methods under the gapi.client.plus namespace.
The Plus API includes a package to load information about People, including getting them by their User ID or, since they have authenticated with you, you can use the special identifier "me".
Full details and an example are given at https://developers.google.com/+/api/latest/people/get, but here is an (untested) similar function to your getEmail() method that would get their full name:
function getFullName(){
// Load the Plus library to get the People package and methods
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get('me');
request.execute(getFullNameCallback);
});
};
function getFullNameCallback(obj){
var el = document.getElementById('email');
var name = '';
if (obj['displayName']) {
name = 'Name: '+obj.displayName;
}
el.innerHTML = name;
toggleElement('name');
};
The above code snippet no longer seems to work.
Once again we are chasing our tails over something
google now changed......
error "Access Not Configured. Please use Google Developers Console to activate the API for your project."
I assumed it it might be the "Google+ API" so it is switched on in the developer console,
still no working however.
Yet api explorer shows promisingly that some sort of code can work,
however its a dogs breakfast trying to discern what javascript code is working there.
So so useful api explorer...., but how about google show a simple WORKING example in code that we can look at for this same request?