Consent SDK : consentStatus become UNKNOWN again - android

I'm currently trying to integrate the Google Consent SDK.
something is happening that I don't understand with the onConsentInfoUpdated(consentStatus:ConsentStatus) function
User opens the app for the first time and makes a choice when the popup is diplayed
Consent.Status -> UNKNOWN
User kills / re-opens the app.
Sometimes Consent.Status -> PERSONALIZED or Consent.Status -> UNKNOWN and user makes a choice again.
here my code:
fun requestConsent(activity: Activity) {
val consentInformation = ConsentInformation.getInstance(activity)
val publisherIds = arrayOf(activity.getString(R.string.admob_publisher_id))
consentInformation.requestConsentInfoUpdate(publisherIds, object : ConsentInfoUpdateListener {
override fun onConsentInfoUpdated(consentStatus: ConsentStatus) {
Log.d("test--", consentStatus.toString())
when (consentStatus) {
ConsentStatus.PERSONALIZED -> showPersonalizedAds()
ConsentStatus.NON_PERSONALIZED -> showNonPersonalizedAds()
ConsentStatus.UNKNOWN -> loadConsentForm(activity)
}
}
override fun onFailedToUpdateConsentInfo(errorDescription: String) {
//onFailedToUpdateConsentInfo()
}
})
}
private fun showPersonalizedAds() {
ConsentInformation.getInstance(activity).consentStatus = ConsentStatus.PERSONALIZED
}
private fun showNonPersonalizedAds() {
ConsentInformation.getInstance(activity).consentStatus = ConsentStatus.NON_PERSONALIZED
}
here my logs:

This is the first result that came up on Google and I tried your solution but it didn't resolve the issue. In my case I was using:
com.google.android.ads.consent:consent-library:1.0.6
The issue was resolved by upgrading to:
com.google.android.ads.consent:consent-library:1.0.8
Google removed the restriction on using Google rendered consent form with the commonly used set of ad technology providers in 1.0.6 and changed logic for re-consent prompts in 1.0.8 - as seen here: https://github.com/googleads/googleads-consent-sdk-android/blob/master/ChangeLog.md
Could it be that you also changed your library version without realising?

by default, all advertisers are selected in the admob console. That's why I was able to solve my problem by going to the blocking settings section for users in the European Union.
click Blocking controls and then EU user consent
https://support.google.com/admob/answer/7666519#providers

Related

Handling FirebaseAuthUserCollisionException when using FirebaseUI

I am currently using Firebase-UI for Android to implement authentication flow in my app. I currently have Google, Facebook, and Email auth providers enabled. My Android app is built using Jetpack Compose and I'm using rememberLauncherForActivityResult to launch the login intent. Everything is working as expected with the normal flow.
However, when I try to use my Facebook login with the same email that I have previously authenticated using Gmail, I am getting the below error.
A sign-in error occurred.
com.google.firebase.auth.FirebaseAuthUserCollisionException: This credential is already associated with a different user account.**
at com.google.android.gms.internal.firebase-auth-api.zzxc.zzb(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzya.zza(com.google.firebase:firebase-auth##21.1.0:7)
at com.google.android.gms.internal.firebase-auth-api.zzyb.zzl(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzxy.zzq(com.google.firebase:firebase-auth##21.1.0:3)
at com.google.android.gms.internal.firebase-auth-api.zzxy.zze(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzxa.zze(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzd(com.google.firebase:firebase-auth##21.1.0:8)
at com.google.android.gms.internal.firebase-auth-api.zzuf.zzb(com.google.firebase:firebase-auth##21.1.0:2)
at com.google.android.gms.internal.firebase-auth-api.zzyj.zzb(com.google.firebase:firebase-auth##21.1.0:12)
at com.google.android.gms.internal.firebase-auth-api.zzyj.zza(com.google.firebase:firebase-auth##21.1.0:14)
at com.google.android.gms.internal.firebase-auth-api.zzxp.zzq(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzug.zzb(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzM(com.google.firebase:firebase-auth##21.1.0:5)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzs(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzxb.zzm(com.google.firebase:firebase-auth##21.1.0:6)
at com.google.android.gms.internal.firebase-auth-api.zzvr.zzc(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzyc.run(com.google.firebase:firebase-auth##21.1.0:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
I would like someway to handle this exception but I am not able to find a way. Am I missing something obvious?
Here is my implementation
ProfileViewModel.kt
override fun buildLoginIntent(): Intent {
val authUILayout = AuthMethodPickerLayout.Builder(R.layout.auth_ui)
.setGoogleButtonId(R.id.btn_gmail)
.setEmailButtonId(R.id.btn_email)
.setFacebookButtonId(R.id.btn_facebook)
.build()
val googleScopes = arrayListOf(
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email"
)
val intent = AuthUI.getInstance().createSignInIntentBuilder()
.setAvailableProviders(
listOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().setScopes(googleScopes).build(),
AuthUI.IdpConfig.FacebookBuilder().build()
)
)
.enableAnonymousUsersAutoUpgrade()
.setLogo(R.mipmap.ic_launcher)
.setAuthMethodPickerLayout(authUILayout)
.build()
return intent
}
#SuppressLint("RestrictedApi")
override fun onLoginResult(result: FirebaseAuthUIAuthenticationResult) {
// Handle result
}
ProfileUI.kt
Composable UI where I launch the intent
val loginLauncher = rememberLauncherForActivityResult(
profileViewModel.buildLoginActivityResult()
) { result ->
if (result != null) {
profileViewModel.onLoginResult(result = result)
}
}
if (isAnonymousUser) {
SignInUI() {
loginLauncher.launch(profileViewModel.buildLoginIntent())
}
}
However, when I try to use my Facebook login with the same email that I have previously authenticated using Gmail, I am getting the below error.
This credential is already associated with a different user account.
That's the expected behavior since your user was first time authenticated with Google. When you authenticate a user in Firebase with Google, there is a user created that contains the data that corresponds to that particular provider. If you try to use that data to authenticate to Facebook, the authentication will fail, because the user was already created with another provider, hence the error.
A solution for this kind of situation would be to check if the user already exists, before authenticating it. If the user already exists, then you have to read the provider and display a message to the user, so it can choose the right authentication provider.
Another option might be to allow the creation of different accounts for different providers. You can find this option which is called "Create multiple accounts for each identity provider" right inside the Firebase Console, in the Settings tab inside the Authentication.

Android dynamic link doesn't survive fresh app installation

I'm trying to implement referral in my app with Firebase Dynamic link.
The happy path would be:
Link is shared with a JWT token as parameter
Another user clicks on the link, doesn't have the app so it goes to the Playstore
After app was installed, at app startup, the JWT token is retrieved trough parameters and stored locally
New signup is complete, the token is sent to the backend to activate referrer's reward
Problem is that in this scenario, step 3 doesn't find any data at first app startup, whereas it is found when I click on the link anew after the app has been installed.
This is the way the link is generated for the referrer:
val parameters = new DynamicLink.SocialMetaTagParameters.Builder()
.setImageUrl(...)
.setTitle(...)
.build();
val link = Uri.parse("https://my.domain.com/path/?token=${jwtToken}");
val dynamicLink = FirebaseDynamicLinks.getInstance().createDynamicLink()
.setLink(link)
.setSocialMetaTagParameters(parameters)
.setDomainUriPrefix("https://my.domain.com")
.setAndroidParameters(DynamicLink.AndroidParameters.Builder().build()))
.setIosParameters(...)
.setNavigationInfoParameters(
DynamicLink.NavigationInfoParameters.Builder()
.setForcedRedirectEnabled(true)
.build
).buildDynamicLink()
The link is shorten by:
FirebaseDynamicLinks.getInstance().createDynamicLink()
.setDomainUriPrefix("https://my.domain.com/")
.setLongLink(dynamicLink.getUri())
.buildShortDynamicLink()
.addOnCompleteListener(activity, task -> {
if (task.isSuccessful() && task.getResult() != null) {
linkCallback.success(task.getResult().getShortLink().toString());
} else {
linkCallback.failure(task.getException());
}
})
At app's opening, link is read in the onResume() method of the starting activity :
override fun onResume() {
super.onResume()
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent()) // getIntent() refers to the Activity's method
.addOnSuccessListener(activity) { data -> // Get deep link from result (may be null if no link is found)
val isReferralLink = data?.link?.toString()?.startsWith("https://my.domain.com/path") == true
val jwt = data?.link?.getQueryParameter("token")
// Here, data is null. <-------
// Other attempt :
data?.let { aiData ->
FirebaseAppInvite.getInvitation(aiData)?.let { result ->
// Here result is still null
}
}
}
}
I've seen on a stack overflow thread that it doesn't survive to beta track install, so I tried to leave the beta and use remote config to hide it in production track, but I have not been able see any difference.
And the version I'm using is :
implementation 'com.google.firebase:firebase-dynamic-links:21.0.0'
implementation 'com.google.firebase:firebase-analytics:20.0.0'
implementation 'com.google.firebase:firebase-invites:17.0.0'
Question:
Is there something I'm missing here ?
Thank you in advance for your help !
EDIT:
In this scenario the link and token are correctly found.
Link is shared with a JWT token as parameter
Another user clicks on the link, doesn't have the app so it goes to the Playstore
After app was installed, the second user clicks again on the link and opens the app for the first time
Ok so after a few days of testing and researches, I found out why it was not working.
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(activity) { data ->
// Some data reading
}
In this case I was using the addOnSuccessListener() with an activity as parameter, which makes it lifecycle aware. In my app, a new user (fresh install) will be redirected to an onboarding activity, so the listener's activity is paused, and the callback is never fired.
=> TLDR: Removing this activity parameter solved my problem.

Play Billing Library: What if app fails to give entitlement?

I'm learning Google Play Billing Library to use in my Android App. I sell coin in my app as consumables product.
The flow is: user buy the item, app consume the item, if consume success increment the coin saved in Firestore.
What i want to ask is, what if increment coin saved in Firestore fails? Lets say because network or other things. This can cause a problem for users because our app has already consumed the item and users don't get their coins.
private fun handleConsumablePurchasesAsync(consumables: List<Purchase>) {
Timber.d("handleConsumablePurchasesAsync called")
consumables.forEach { purchase ->
Timber.d("handleConsumablePurchasesAsync foreach it is $purchase")
val params = ConsumeParams
.newBuilder()
.setPurchaseToken(purchase.purchaseToken)
.build()
playStoreBillingClient.consumeAsync(params) { billingResult, purchaseToken ->
when (billingResult.responseCode) {
BillingClient.BillingResponseCode.OK -> {
purchaseToken.apply { disburseConsumableEntitlements(purchase) }
}
else -> Timber.w(billingResult.debugMessage)
}
}
}
}
private fun disburseConsumableEntitlements(purchase: Purchase) {
when (purchase.sku) {
SkuKeys.COINS_5K -> addCoin(5000)
SkuKeys.COINS_10K -> addCoin(10000)
SkuKeys.COINS_100K -> addCoin(100000)
SkuKeys.COINS_500K -> addCoin(500000)
SkuKeys.COINS_1M -> addCoin(1000000)
SkuKeys.COINS_2M -> addCoin(2000000)
}
}
//what if this fails?
private fun addCoin(amount: Long) =
FirestoreRepository.incrementCoins(FirebaseAuthRepository.currentUserId, amount)
How to fix this problem? Are there any better approach?
My proposal,
Consume the item only after writing to Firebase successfully.
When the app is restarted and queryPurchases() returns that he still owns the item then try to update in Firebase again
I recommend save the purchases in to database once consumed , You can sync the data from database to firebase .
Once synced you can remove the data from database .
You can trigger a workmanager to sync the data additionally if required .(would be helpful if user kills the app)
This how ever is not secure as anyone with root access can get into database and manupulate with data . You can encrypt the database to make it little more secure but it wont be 100 % fool proof .
If you had a server then purchases could be validated on server side , but for you I can't think of any other solution .

Google Ads attribution in Firebase SDK

We have recently switched from Google Analytics SDK to Firebase SDK in our Android app.
Before that, we used INSTALL_REFERRER to get the user's source and medium.
Now we have started an App Campaign on Google Ads and INSTALL_REFERRER no longer works, though conversions keep coming.
How do we use Firebase SDK to know that user came from Google Ads campaign?
I think you'll want to use the Play Install Referrer API.
The link above cautions that the install referrer information will be available for 90 days and to only invoke the API during the first run of the app to avoid unnecessary API calls.
Here's an example (taking from the link above), assuming you have added the library to your build.gradle file:
Initialization:
private lateinit var referrerClient: InstallReferrerClient
...
referrerClient = InstallReferrerClient.newBuilder(this).build()
referrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerResponse.OK -> {
// Connection established
}
InstallReferrerResponse.FEATURE_NOT_SUPPORTED -> {
// API not available on the current Play Store app
}
InstallReferrerResponse.SERVICE_UNAVAILABLE -> {
// Connection could not be established
}
}
}
override fun onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
})
Getting the referrer:
val response: ReferrerDetails = referrerClient.installReferrer
val referrer = response.installReferrer
val clickTimestamp = response.referrerClickTimestampSeconds
val installTimestamp = response.installBeginTimestampSeconds
Wrapping Up:
referrerClient.endConnection()
Checking for gclid (Google Ads)
if ("gclid" in referrer) {
//report to Firebase Analytics
} else {
//do something else
}
In this way, we will determine gclid only if the user has clicked on the advertisement in the browser, but if he clicked on the advertisement in the play market, then nothing will work.

Error : "Forbidden" when doing GeocodeRequest in Here-API for android

When doing a Geocoderequest in the Here API for Android I get the result FORBIDDEN for any search string.
Here is a snippet of the code (Kotlin) :
class GeoListner : ResultListener <MutableList<Location>>
{
override fun onCompleted(p0: MutableList<Location>?, p1: ErrorCode?) {
Log.d(this.javaClass.toString(),"Result code of search is ${p1?.name}")
}
}
fab_search.setOnClickListener { View ->
var currentPos = GeoCoordinate(49.2849,-123.1252)
val listner : ResultListener<MutableList<Location>> = GeoListner()
val request = GeocodeRequest("Granville").setSearchArea(currentPos,5000)
if (request.execute(listner) != ErrorCode.NONE)
{
}
}
This search area and string is picked from the HERE-API documentation for Here. Also i notice that the GeocodeRequest is deprecated, but the result for GeocodeRequest2 is the same error.
Anyone ?
Regards
Trond
Summary: Please ensure that you set the APP_ID and APP_CODE in the manifest file correctly.
For the background: Developers using HERE SDK with their app are required to register for a set of HERE credentials and to specify these credentials (App_Id and App_Code) in their app's Android manifest XML file. Failure to do so results in blocked access to certain features and degradation in the quality of other services.
To obtain these credentials visit the developer portal at https://developer.here.com/plans and register for a license. Once your project is created, you can generate these credentials on your Project Details page. If you already have a plan, you can also retrieve these credentials from your Project Details page.
Note: Credentials are unique to your account and your application's package namespace. Avoid reusing credentials across multiple applications.

Categories

Resources