12

I try to use Google ConsentSDK to show in Android app consent form. When I call form.show() I get this error: "Consent form error Consent form is not ready to be displayed." Who can help me?

My code:

  ConsentForm form = new ConsentForm.Builder(context, privacyUrl)
            .withListener(new ConsentFormListener() {
                @Override
                public void onConsentFormLoaded() {
                    // Consent form loaded successfully.
                    Log.d("SplashScreen", "Consent form Loaded ");
                }

                @Override
                public void onConsentFormOpened() {
                    // Consent form was displayed.
                    Log.d("SplashScreen", "Consent form opened ");
                }

                @Override
                public void onConsentFormClosed(
                        ConsentStatus consentStatus, Boolean userPrefersAdFree) {
                    // Consent form was closed.
                    Log.d("SplashScreen", "Consent form Closed ");
                }

                @Override
                public void onConsentFormError(String errorDescription) {
                    // Consent form error.
                    Log.d("SplashScreen", "Consent form error " + errorDescription);
                }
            })
            .withPersonalizedAdsOption()
            .withNonPersonalizedAdsOption()
            .build();
    form.load();
    form.show();
Nace
  • 2,854
  • 1
  • 15
  • 21
Pingu
  • 123
  • 2
  • 6
  • 1
    Actually I have the same problem.First of all you have to be sure that you have "custom provider's list (limited at 12 providers)" in your AdMob console. I did it but it shoulds requires 1 hour or more to upgrade. – Marco May 26 '18 at 09:47

5 Answers5

16

Here is my helper class for the Google Consent SDK that I use in my app. To initialise the consent information and display the Consent Form if needed, I have following code in onCreate() method of my main activity:

GdprHelper gdprHelper = new GdprHelper(this);
gdprHelper.initialise();

Similarly, I run following code when user clicks on "Reset my privacy consent" in preferences:

GdprHelper gdprHelper = new GdprHelper(this);
gdprHelper.resetConsent();

where both times, this is referencing current running activity.

Full implementation of the helper class:

    package com.example.app;

    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.widget.Toast;

    import com.google.ads.consent.ConsentForm;
    import com.google.ads.consent.ConsentFormListener;
    import com.google.ads.consent.ConsentInfoUpdateListener;
    import com.google.ads.consent.ConsentInformation;
    import com.google.ads.consent.ConsentStatus;

    import java.net.MalformedURLException;
    import java.net.URL;

    public class GdprHelper {

        private static final String PUBLISHER_ID = "YOUR-PUBLISHER-ID";
        private static final String PRIVACY_URL = "YOUR-PRIVACY-URL";
        private static final String MARKET_URL_PAID_VERSION = "market://details?id=com.example.app.pro";

        private final Context context;

        private ConsentForm consentForm;

        public GdprHelper(Context context) {
            this.context = context;
        }

        // Initialises the consent information and displays consent form if needed
        public void initialise() {
            ConsentInformation consentInformation = ConsentInformation.getInstance(context);
            consentInformation.requestConsentInfoUpdate(new String[]{PUBLISHER_ID}, new ConsentInfoUpdateListener() {
                @Override
                public void onConsentInfoUpdated(ConsentStatus consentStatus) {
                    // User's consent status successfully updated.
                    if (consentStatus == ConsentStatus.UNKNOWN) {
                        displayConsentForm();
                    }
                }

                @Override
                public void onFailedToUpdateConsentInfo(String errorDescription) {
                    // Consent form error. Would be nice to have proper error logging. Happens also when user has no internet connection
                    if (BuildConfig.BUILD_TYPE.equals("debug")) {
                        Toast.makeText(context, errorDescription, Toast.LENGTH_LONG).show();
                    }
                }
            });
        }

        // Resets the consent. User will be again displayed the consent form on next call of initialise method
        public void resetConsent() {
            ConsentInformation consentInformation = ConsentInformation.getInstance(context);
            consentInformation.reset();
        }

        private void displayConsentForm() {

            consentForm = new ConsentForm.Builder(context, getPrivacyUrl())
                    .withListener(new ConsentFormListener() {
                        @Override
                        public void onConsentFormLoaded() {
                            // Consent form has loaded successfully, now show it
                            consentForm.show();
                        }

                        @Override
                        public void onConsentFormOpened() {
                            // Consent form was displayed.
                        }

                        @Override
                        public void onConsentFormClosed(
                                ConsentStatus consentStatus, Boolean userPrefersAdFree) {
                            // Consent form was closed. This callback method contains all the data about user's selection, that you can use.
                            if (userPrefersAdFree) {
                                redirectToPaidVersion();
                            }
                        }

                        @Override
                        public void onConsentFormError(String errorDescription) {
                            // Consent form error. Would be nice to have some proper logging
                            if (BuildConfig.BUILD_TYPE.equals("debug")) {
                                Toast.makeText(context, errorDescription, Toast.LENGTH_LONG).show();
                            }
                        }
                    })
                    .withPersonalizedAdsOption()
                    .withNonPersonalizedAdsOption()
                    .withAdFreeOption()
                    .build();
            consentForm.load();
        }

        private URL getPrivacyUrl() {
            URL privacyUrl = null;
            try {
                privacyUrl = new URL(PRIVACY_URL);
            } catch (MalformedURLException e) {
                // Since this is a constant URL, the exception should never(or always) occur
                e.printStackTrace();
            }
            return privacyUrl;
        }

        private void redirectToPaidVersion() {
            Intent i = new Intent(
                    Intent.ACTION_VIEW,
                    Uri.parse(MARKET_URL_PAID_VERSION));
            context.startActivity(i);
        }
    }
Nace
  • 2,854
  • 1
  • 15
  • 21
  • Could you please explain in code how to call initialize() form class – Leenah May 31 '18 at 23:08
  • 1
    Thank you so much! – Leenah Jun 01 '18 at 11:39
  • 4
    Hi i would suggest to change a piece of code. Normally the consent form should only load if the user is within the european union ! i have changed if (consentStatus == ConsentStatus.UNKNOWN) to if (consentStatus == ConsentStatus.UNKNOWN &&ConsentInformation.getInstance(context).isRequestLocationInEeaOrUnknown() ) – Frank Jun 06 '18 at 17:17
  • 1
    Don't forget to update how your AdMob will request ads after the user has made his choice. In `onConsentFormClosed()` method you may want to update some SharedPref and use it when you prepare your ad: `if(!GdprHelper.isConsent()){ Bundle extras = new Bundle(); extras.putString("npa", "1"); adRequest = new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, extras).build(); }else{ adRequest = new AdRequest.Builder().build(); } mInterstitialAd.loadAd(adRequest); ` – Kirill Karmazin Jul 12 '19 at 17:03
8

Ok I solved this way: create an instance of your form with the Builder and then you have to call the form.load().

Wait for the form to be loaded and call the .show() inside:

@Override
public void onConsentFormLoaded() {
                // Consent form loaded successfully... now you can show it.
                Log.d("SplashScreen", "Consent form Loaded ");
                showConsentForm();
}

To accomplish this I created a private function :

private showConsentForm(){ form.show(); }

To see how the form works you can try this app: https://play.google.com/store/apps/details?id=com.mapkcode.whereis

Marco
  • 676
  • 8
  • 12
  • Is it required to use `Bundle extras = new Bundle(); extras.putString("npa", "1"); AdRequest request = new AdRequest.Builder() .addNetworkExtrasBundle(AdMobAdapter.class, extras) .build();` or is it taken care automatically after user chooses non-personlized ads option – Thracian May 28 '18 at 08:19
  • 1
    it is required only if you choose on your AdMob console to use the standard provider list (first option). In that case you need to create your own form. Using the custom provider list (you can choose up to 12 that ion my opinion are enough) the provided form will take care of it. If you wanna try how it works you can try this app: https://play.google.com/store/apps/details?id=com.mapkcode.whereis – Marco May 28 '18 at 11:22
  • Marco thats not true ! Google says "If a user has consented to receive only non-personalized ads, you'll need to forward consent to the Google Mobile Ads SDK." even if you are using the second option. https://developers.google.com/admob/android/eu-consent#publisher_managed_consent_collection – Frank Jun 11 '18 at 00:53
  • But if you're using the second option and the SDK Consent, then it will forward the consent, you don't have to do write code for it. I think it was right to point it out btw., if you use second option and your own consent form you HAVE TO forward the consent. Thanks Frank. – Marco Jun 16 '18 at 09:44
  • @Marco could you post the full code of you created an instance for the builder ? and where did you declare the showConsentForm method ? – isJulian00 Feb 02 '19 at 00:16
2

The simple answer is that the form cannot be shown until the form has completed loading. The docs on this nuance are rather dreadful. The fix is to call form.show() within onConsentFormLoaded() like this:

public class MyGdprHelper {
    private ConsentForm form;

    private void getUsersConsent() {
        form = new ConsentForm.Builder(context, privacyUrl)
                .withListener(new ConsentFormListener() {
                    @Override
                    public void onConsentFormLoaded() {
                        // Consent form loaded successfully.
                        form.show();
                    }

                    @Override public void onConsentFormOpened() {...}
                    @Override public void onConsentFormClosed(ConsentStatus consentStatus, Boolean userPrefersAdFree) {...}
                    @Override public void onConsentFormError(String errorDescription) {...}
                )
                .withPersonalizedAdsOption()
                .withNonPersonalizedAdsOption()
                .withAdFreeOption()
                .build();
        form.load();
    }
    ...
}
Chris Knight
  • 24,333
  • 24
  • 88
  • 134
0

Based on @WebMajstr answer and @Frank's comment, here is my own class that has two additional features: A callback listener and non EEA users are checked.

package com.levionsoftware.photos.utils.consensus;

import android.content.Context;
import android.util.Log;

import com.google.ads.consent.ConsentForm;
import com.google.ads.consent.ConsentFormListener;
import com.google.ads.consent.ConsentInfoUpdateListener;
import com.google.ads.consent.ConsentInformation;
import com.google.ads.consent.ConsentStatus;
import com.google.ads.consent.DebugGeography;
import com.levionsoftware.photos.MyApplication;
import com.levionsoftware.photos.R;

import java.net.MalformedURLException;
import java.net.URL;

public class GdprHelper {
    private static final String PUBLISHER_ID = "pub-2308843076741286";

    private final Context context;
    private final ConsensusUpdatedListener consensusUpdatedListener;

    private ConsentForm consentForm;

    public GdprHelper(Context context, ConsensusUpdatedListener consensusUpdatedListener) {
        this.context = context;
        this.consensusUpdatedListener = consensusUpdatedListener;
    }

    // Initialises the consent information and displays consent form if needed
    public void initialise() {
        ConsentInformation consentInformation = ConsentInformation.getInstance(context);

        consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_EEA);

        consentInformation.requestConsentInfoUpdate(new String[]{PUBLISHER_ID}, new ConsentInfoUpdateListener() {
            @Override
            public void onConsentInfoUpdated(ConsentStatus consentStatus) {
                Log.d("GdprHelper", "onConsentInfoUpdated: " + consentStatus.toString());

                if(consentInformation.isRequestLocationInEeaOrUnknown()) {
                    Log.d("GdprHelper", "isRequestLocationInEeaOrUnknown: true");
                    // If the isRequestLocationInEeaOrUnknown() method returns true:
                    //  If the returned ConsentStatus is PERSONALIZED or NON_PERSONALIZED, the user has already provided consent. You can now forward consent to the Google Mobile Ads SDK.
                    //  If the returned ConsentStatus is UNKNOWN, see the Collect consent section below, which describes the use of utility methods to collect consent.

                    // User's consent status successfully updated.
                    if (consentStatus == ConsentStatus.UNKNOWN) {
                        consensusUpdatedListener.reset();
                        displayConsentForm();
                    } else {
                        consensusUpdatedListener.set(consentStatus == ConsentStatus.NON_PERSONALIZED, false);
                    }
                } else {
                    Log.d("GdprHelper", "isRequestLocationInEeaOrUnknown: false");
                    // If the isRequestLocationInEeaOrUnknown() method returns false:
                    //  the user is not located in the European Economic Area and consent is not required under the EU User Consent Policy. You can make ad requests to the Google Mobile Ads SDK.
                    consensusUpdatedListener.set(false, true);
                }
            }

            @Override
            public void onFailedToUpdateConsentInfo(String errorDescription) {
                // Consent form error. Would be nice to have proper error logging. Happens also when user has no internet connection
                MyApplication.toastSomething(new Exception(errorDescription));
            }
            });
    }

    // Resets the consent. User will be again displayed the consent form on next call of initialise method
    public void resetConsent() {
        ConsentInformation consentInformation = ConsentInformation.getInstance(context);
        consentInformation.reset();
    }

    private void displayConsentForm() {
        consentForm = new ConsentForm.Builder(context, getPrivacyUrl())
                .withListener(new ConsentFormListener() {
                    @Override
                    public void onConsentFormLoaded() {
                        // Consent form has loaded successfully, now show it
                        consentForm.show();
                    }

                    @Override
                    public void onConsentFormOpened() {
                        // Consent form was displayed.
                    }

                    @Override
                    public void onConsentFormClosed(
                            ConsentStatus consentStatus, Boolean userPrefersAdFree) {
                        // Consent form was closed. This callback method contains all the data about user's selection, that you can use.
                        Log.d("GdprHelper", "onConsentFormClosed: " + consentStatus.toString());
                        if (consentStatus == ConsentStatus.UNKNOWN) {
                            consensusUpdatedListener.reset();
                            displayConsentForm();
                        } else {
                            consensusUpdatedListener.set(consentStatus == ConsentStatus.NON_PERSONALIZED, false);
                        }
                    }

                    @Override
                    public void onConsentFormError(String errorDescription) {
                        // Consent form error. Would be nice to have some proper logging
                        MyApplication.toastSomething(new Exception(errorDescription));
                    }
                })
                .withPersonalizedAdsOption()
                .withNonPersonalizedAdsOption()
                //.withAdFreeOption()
                .build();
        consentForm.load();
    }

    private URL getPrivacyUrl() {
        URL privacyUrl = null;
        try {
            privacyUrl = new URL(MyApplication.get().getString(R.string.privacyPolicyURL));
        } catch (MalformedURLException e) {
            // Since this is a constant URL, the exception should never(or always) occur
            e.printStackTrace();
        }
        return privacyUrl;
    }
}

Listener:

package com.levionsoftware.photos.utils.consensus;

public interface ConsensusUpdatedListener {
    void set(Boolean npa, Boolean consensusNotNeeded);

    void reset();
}

Edit: See Android: Getting user's location using Admob's Consent SDK, isRequestLocationInEeaOrUnknown must be called AFTER onConsentInfoUpdated.

Denny Weinberg
  • 2,492
  • 1
  • 21
  • 34
0
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import com.google.ads.consent.ConsentForm;
import com.google.ads.consent.ConsentFormListener;
import com.google.ads.consent.ConsentInfoUpdateListener;
import com.google.ads.consent.ConsentInformation;
import com.google.ads.consent.ConsentStatus;
import com.google.ads.consent.DebugGeography;
import com.google.ads.mediation.admob.AdMobAdapter;
import com.google.android.gms.ads.AdRequest;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

    private ConsentForm consentForm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getConsentInformation();

        String android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
        Log.d("Android", "Android ID : " + android_id);
    }

    private void getConsentInformation() {
        final ConsentInformation consentInformation = ConsentInformation.getInstance(MainActivity.this);
        String[] publisherIds = {"pub-0123456789012345"};

        consentInformation.requestConsentInfoUpdate(publisherIds, new ConsentInfoUpdateListener() {
        @Override
        public void onConsentInfoUpdated(ConsentStatus consentStatus) {
            boolean inEEA = ConsentInformation.getInstance(MainActivity.this).isRequestLocationInEeaOrUnknown();
            if (inEEA) {
            switch (consentStatus) {
                case PERSONALIZED:
                personalizeAds(true);
                break;
                case NON_PERSONALIZED:
                personalizeAds(false);
                break;
                case UNKNOWN:
                displayConsentDialog();
                break;
                default:
                break;
        }
    }
}

        @Override
        public void onFailedToUpdateConsentInfo(String reason) {

        }
    });

    consentInformation.addTestDevice("33BE2250B43518CCDA7DE426D04EE231");
    consentInformation.setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_EEA);
    }

    private void personalizeAds(boolean isPersonalize) {

        AdRequest adRequest;
        if (isPersonalize) {
        adRequest = new AdRequest.Builder().build();
        } else {
        Bundle extras = new Bundle();
        extras.putString("npa", "1");
        adRequest = new AdRequest.Builder().addNetworkExtrasBundle(AdMobAdapter.class, extras).build();
        }
    }

    private void displayConsentDialog() {
        URL privacyUrl = null;
        try {
        // TODO: Replace with your app's privacy policy URL.
        privacyUrl = new URL("PRIVACY_POLICY");
        } catch (MalformedURLException e) {
        e.printStackTrace();
        // Handle error.
    }

    consentForm = new ConsentForm.Builder(this, privacyUrl).withListener(new ConsentFormListener() {
    @Override
    public void onConsentFormLoaded() {
        super.onConsentFormLoaded();
        consentForm.show();
    }

    @Override
    public void onConsentFormClosed(ConsentStatus consentStatus, Boolean userPrefersAdFree) {
        super.onConsentFormClosed(consentStatus, userPrefersAdFree);
        if (consentStatus.equals(ConsentStatus.PERSONALIZED))
            personalizeAds(true);
        else
            personalizeAds(false);
        }
    })
    .withNonPersonalizedAdsOption()
    .withNonPersonalizedAdsOption()
    .build();
    consentForm.load();

    }
}
Jhonson
  • 11
  • 2