Android, UMP, how to know if the user did not consent?
Asked Answered
N

1

8

Android, UMP always returns "OBTAINED" even if the user did not consent or if the user dismissed the form. So it's very hard to do anything with it. I want to know if the user did not consent, and in that case close the app. And then next time the app is opened the form should be shown immediately. However another problem is that when user selects things in the form and approves the consent, next time when the form is opened again the settings are reset (like if the form was never saved). Another problem is that it's almost impossible that the user has selected correct things to get ads and in most cases the user would get a free app without any ads (since the OBTAINED status does not tell if the user consented or not). Also I cannot get what the user selected (do I really need to look in sharedpreferences to get that info? Overall, this UMP seems to work very strange, or maybe I have implemented it wrong? Maybe there will be released any improvement to it soon? Using implementation 'com.google.android.ump:user-messaging-platform:2.1.0'

Also it seems if user has already given consent, and Admob settings is changed from personal to non-personal ads for my app, then user will still get the personal ads.

This is the code:

ConsentRequestParameters params = new ConsentRequestParameters
                .Builder()
                .setTagForUnderAgeOfConsent(false)
                .build();

        ConsentInformation consentInformation;
        consentInformation = UserMessagingPlatform.getConsentInformation(this.getApplicationContext());
        consentInformation.requestConsentInfoUpdate(
                (Activity) this,
                params,
                new ConsentInformation.OnConsentInfoUpdateSuccessListener() {
                    @Override
                    public void onConsentInfoUpdateSuccess() {
                        // The consent information state was updated.
                        // You are now ready to check if a form is available.
                        if (consentInformation.isConsentFormAvailable()) {
                            // Loads a consent form. Must be called on the main thread.
                            UserMessagingPlatform.loadConsentForm(
                                    MyActivity.this.getApplicationContext(),
                                    new UserMessagingPlatform.OnConsentFormLoadSuccessListener() {
                                        @Override
                                        public void onConsentFormLoadSuccess(ConsentForm consentForm) {
                                            consentForm.show(
                                                    (Activity) MyActivity.this,
                                                    new ConsentForm.OnConsentFormDismissedListener() {
                                                        @Override
                                                        public void onConsentFormDismissed(@Nullable FormError formError) {
                                                            // TODO: Always getting "OBTAINED" even if user dismissed form or selected to not consent.
                                                            // User did not consent so consentStatus is still REQUIRED
                                                            if (consentInformation.getConsentStatus() == ConsentInformation.ConsentStatus.REQUIRED) {
                                                                MyActivity.this.finish();
                                                            }
                                                        }
                                                    });
                                        }
                                    },
                                    new UserMessagingPlatform.OnConsentFormLoadFailureListener() {
                                        @Override
                                        public void onConsentFormLoadFailure(FormError formError) {
                                            // Handle Error.
                                        }
                                    }
                            );
                        } else {
                            // Handle error
                        }
                    }
                },
                new ConsentInformation.OnConsentInfoUpdateFailureListener() {
                    @Override
                    public void onConsentInfoUpdateFailure(FormError formError) {
                        // Handle the error.
                    }
                });

I have tried to change the code in various ways but still have the same problems. So for now I have selected non-personal ads in Admob settings because UMP does not seem to work.

Natation answered 5/9, 2023 at 9:33 Comment(2)
I am also having the same problem. After displaying the form, UMP always returns OBTAINED and canRequestAds() is set to true. From the docs it looks like canRequestAds() should only be true if the user consents to them, so it seems to be a bug in UMP. I am also using version 2.1.0Devaluate
Still same problem. Using 2.1.0 as well. No update yet.Aldine
T
3

After a week of getting absolutely nowhere with Google... their philosophy seems to be to turn AdMob into a dumpster fire on purpose (un-consenting from 'legitimate interest' is not a thing!)... I have settled on my interim solution until they hopefully one day get their act together.

In short i am...

  1. presenting the user the official google consent screen
  2. using the iabtcf_consent_info package to retrieve their responses
  3. Passing those responses through a custom parser class which translates their responses into the information we actually care about 'no ads, untargetted ads, targetted ads'
  4. Outputting that summary via a stream
  5. Disabling access to certain features if that stream is outputting 'no ads'

If the stream outputs 'no-ads', then i present a dialog to them when they reach the home page, explaining why certain features are disabled and inviting them to resubmit their consent options.

This is a dreadful user experience of course... entering a bunch of cryptic consent options only to be rebuffed at the home page and told to do it again because their chosen combination was wrong... But this seems to be the user experience Google want.

Either that or they're seriously expecting us to support the costs of non-consenting users without any means to monetise them!

The following is my parser class incase anyone would like to follow a similar approach (I'm a flutter developer but i understand dart is not dissimilar to JavaScript).

import 'dart:async';
import 'package:buck_the_critics/enums.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:iabtcf_consent_info/iabtcf_consent_info.dart';
class AdmobConsentParser {
  // User must consent to these for any ads at all
  List < DataUsagePurpose > consentsRequiredForAds = [
    DataUsagePurpose.storeAndAccessInformationOnADevice // 1
  ];
  // User must consent to these for targetted ads
  List < DataUsagePurpose > consentsRequiredForTargettedAds = [
    DataUsagePurpose.storeAndAccessInformationOnADevice, // 1
    DataUsagePurpose.createAPersonalisedAdsProfile, // 3
    DataUsagePurpose.selectPersonalisedAds, // 4
  ];
  // User must either consent or NOT untoggle legitimate interest for any of these for any ads at all
  List < DataUsagePurpose > usagesRequiringConsentOrLegitimateInterest = [
    DataUsagePurpose.selectBasicAds, // 2
    DataUsagePurpose.measureAdPerformance, // 7
    DataUsagePurpose.applyMarketResearchToGenerateAudienceInsights, // 9
    DataUsagePurpose.developAndImproveProducts // 10
  ];
  Future < AdConsent > getAdConsentLevel(BasicConsentInfo ? usersConsent) async {
    if (usersConsent != null && usersConsent is ConsentInfo) {
      final gdprApplies = isGDPR(usersConsent);
      if (gdprApplies == true) {
        final targetedAds = await canShowPersonalizedAds(usersConsent);
        if (targetedAds != null && targetedAds) {
          return AdConsent.targeted;
        } else {
          final untargetedAds = await canShowAds(usersConsent);
          if (untargetedAds != null && untargetedAds) {
            return AdConsent.untargeted;
          } else {
            return AdConsent.none;
          }
        }
      } else {
        return AdConsent.none;
      }
    } else {
      return AdConsent.targeted;
    }
  }
  bool ? isGDPR(BasicConsentInfo ? usersConsent) {
    return usersConsent ? .gdprApplies;
  }
  // https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
  // https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
  Future < bool ? > canShowAds(ConsentInfo usersConsent) async {
    final hasConsent = _hasConsent(usersConsent, consentsRequiredForAds);
    final hasConsentOrLegitimateInterest = _hasConsentOrLegitimateInterest(
      usersConsent, usagesRequiringConsentOrLegitimateInterest);
    return hasConsent && hasConsentOrLegitimateInterest;
  }
  // https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details
  // https://support.google.com/admob/answer/9760862?hl=en&ref_topic=9756841
  Future < bool ? > canShowPersonalizedAds(ConsentInfo usersConsent) async {
    return _hasConsent(usersConsent, consentsRequiredForTargettedAds) &&
      _hasConsentOrLegitimateInterest(
        usersConsent, usagesRequiringConsentOrLegitimateInterest);
  }
  _hasConsent(
    ConsentInfo usersConsent, List < DataUsagePurpose > requiredConsents) {
    return requiredConsents
      .every((purpose) => usersConsent.purposeConsents.contains(purpose));
  }
  _hasConsentOrLegitimateInterest(ConsentInfo usersConsent,
    List < DataUsagePurpose > requiredLegitimateInterests) {
    return requiredLegitimateInterests.every((purpose) =>
      usersConsent.purposeConsents.contains(purpose) ||
      usersConsent.purposeLegitimateInterests.contains(purpose));
  }
}
final admobConsentParserProvider = Provider.autoDispose((ref) {
  return AdmobConsentParser();
});
final adConsentLevelProvider = StreamProvider.autoDispose < AdConsent > ((ref) {
  final controller = StreamController < AdConsent > ();
  final consentParser = ref.watch(admobConsentParserProvider);
  IabtcfConsentInfo.instance.consentInfo().listen((event) async {
    final consentLevel = await consentParser.getAdConsentLevel(event);
    controller.sink.add(consentLevel);
  });
  return controller.stream;
});
Thoron answered 5/10, 2023 at 14:23 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.