I followed the guide on the Android docs but for some reason nothing is showing when i start my app.
I even tried logging the listeners but nothing is showing up in logcat.
I also changed the ad technology in admob setting to Custom set of ad technology providers, but still not working.
My code
ConsentInformation consentInformation = ConsentInformation.getInstance(getApplicationContext());
ConsentInformation.getInstance(getApplicationContext()).addTestDevice("6AE7D8950FE9E464D988F340C0D625B0");
ConsentInformation.getInstance(getApplicationContext()).
setDebugGeography(DebugGeography.DEBUG_GEOGRAPHY_EEA);
String[] publisherIds = {""};
consentInformation.requestConsentInfoUpdate(publisherIds, new ConsentInfoUpdateListener() {
#Override
public void onConsentInfoUpdated(ConsentStatus consentStatus) {
// User's consent status successfully updated.
Log.d(TAG,"onConsentInfoUpdated");
}
#Override
public void onFailedToUpdateConsentInfo(String errorDescription) {
// User's consent status failed to update.
Log.d(TAG,"onFailedToUpdateConsentInfo");
}
});
form = new ConsentForm.Builder(this, privacyUrl)
.withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
// Consent form loaded successfully.
Log.d(TAG,"form loaded!");
form.show();
}
#Override
public void onConsentFormOpened() {
// Consent form was displayed.
}
#Override
public void onConsentFormClosed(
ConsentStatus consentStatus, Boolean userPrefersAdFree) {
// Consent form was closed.
}
#Override
public void onConsentFormError(String errorDescription) {
// Consent form error.
Log.d(TAG,"form error!");
}
})
.withPersonalizedAdsOption()
.withNonPersonalizedAdsOption()
.withAdFreeOption()
.build();
form.load();
Gradle
dependencies {
classpath 'com.google.gms:google-services:4.3.2'
}
implementation 'com.google.android.ads.consent:consent-library:1.0.7'
implementation 'com.google.android.gms:play-services-plus:17.0.0'
implementation 'com.google.android.gms:play-services-ads:18.2.0'
EDIT
I tried it on a project which was pre android x and now it calls the listener onFailedToUpdateConsentInfo.
With following error message:
onFailedToUpdateConsentInfoCould not parse Event FE preflight response.
Searched a bit and found this could be because of an invalid pub id, but i'm certain i'm using the right one.
1) I think you forget to check isRequestLocationInEeaOrUnknown() method.
It will return true If user already agreed to the consent. In this case, you don't need to ask it again. I think you already agreed to consent.
wrap your code with
if(ConsentInformation.getInstance(context).isRequestLocationInEeaOrUnknown()){
//setup admob
}else{
//Ask for consent
}
2) You have to call form.show(); to present the form to the user, check Google Doc
I was still using test app id and test ad ids, remove them and change it with your id's and make sure you use it as a testdevice so you don't violate admob policies.
Like this
adLoader.loadAd(new AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR).build());
Related
I am trying to implement the ad in my app with Custom Native Ad Format - https://developers.google.com/ad-manager/mobile-ads-sdk/android/native/custom-formats#java_1
So, according to the documentation I am going with the approach described there and creating the ad
...
private void setListeners() {
...
imageView.setOnClickListener(v -> {
nativeCustomFormatAd.performClick("IMAGE");
});
...
}
private NativeCustomFormatAd nativeCustomFormatAd;
AdLoader adLoader = new AdLoader.Builder(context, "/6499/example/native")
.forCustomFormatAd("10063170",
new NativeCustomFormatAd.OnCustomFormatAdLoadedListener() {
#Override
public void onCustomFormatAdLoaded(NativeCustomFormatAd ad) {
// Show the custom format and record an impression.
nativeCustomFormatAd = ad;
Drawable drawable = vm.nativeCustomFormatAd.getImage("IMAGE").getDrawable();
imageView.setDrawable(drawable);
}
},
new NativeCustomFormatAd.OnCustomClickListener() {
#Override
public void onCustomClick(NativeCustomFormatAd ad, String s) {
// Handle the click action
}
})
.withAdListener( ... )
.withNativeAdOptions( ... )
.build();
#SuppressLint("VisibleForTests")
AdManagerAdRequest adManagerAdRequest = new AdManagerAdRequest.Builder().build();
adLoader.loadAd(adManagerAdRequest);
...
So, it looks pretty simple I try to make a request for the ad then I got (in a callback) NativeCustomFormatAd, save it as a class member, and along with it get drawable and set it to the imageView (to present it in the UI). Once a user clicks on the imageView I get an event in the click listener and invoke nativeCustomFormatAd.performClick("IMAGE");.
The problem is that I expect that once I transfer the ad click to the SDK (by nativeCustomFormatAd.performClick("IMAGE");) SDK is supposed to open the external browser, but instead nothing happens.
P.S. I am sure that nativeCustomFormatAd.performClick("IMAGE"); getting invoked and also I see that SDK gets the click as I got a callback event here:
...
new NativeCustomFormatAd.OnCustomClickListener() {
#Override
public void onCustomClick(NativeCustomFormatAd ad, String s) {
// Handle the click action
}
})
...
What am I missing here?
According to the docs you linked:
When a click is performed on a custom format ad, there are three possible responses from the SDK, attempted in this order:
Invoke the OnCustomClickListener from AdLoader, if one was provided.
For each of the ad's deep link URLs, attempt to locate a content resolver and start the first one that resolves.
Open a browser and navigate to the ad's traditional Destination URL.
Also:
If you pass a listener object in, the SDK instead invokes its onCustomClick method and takes no further action.
Therefore, it seems you have to pass a null OnCustomClickListener.
I've integrated UnityAds on my Android app (that is not published yet).
I get app id and placement id from database on my server.
App id and placement id are correct, I've copied and pasted about 30 times for be sure of it.
So, when I try to get an ad in test mode, it give me the INVALID_ARGUMENT error.
Here an explaination of the error code by Unity, but as you can see it is a little generic.
I have an object that simply represents an ad service (like admob, FAN, inmobi etc)
In this case the object is called advert, and here it's how I show an ad with Unity:
protected void showUnity(){
UnityAds.initialize(this, advert.getApiKey(), true); //advert.getApiKey() returns the app id
UnityAds.addListener(new IUnityAdsListener() {
#Override
public void onUnityAdsReady(String s) {
Log.i(TAG, "onUnityAdsReady "+s);
if(s.equals(advert.getUnitId()) && !unityReady)
UnityAds.show(ActivityAd.this, advert.getUnitId()); //advert.getUnitId() returns the placement id
}
#Override
public void onUnityAdsStart(String s) {
Log.i(TAG, "onUnityAdsStart "+s);
unityReady = true;
}
#Override
public void onUnityAdsFinish(String s, UnityAds.FinishState finishState) {
if (finishState.compareTo(UnityAds.FinishState.COMPLETED) == 0) {
onAdReward(); //my callback for reward
} else if (finishState.compareTo(UnityAds.FinishState.SKIPPED) == 0) {
onAdClosed(); //my callback for ad close
} else if (finishState.compareTo(UnityAds.FinishState.ERROR) == 0) {
onAdError(finishState.toString()); //my callback for errors
}
}
#Override
public void onUnityAdsError(UnityAds.UnityAdsError unityAdsError, String s) {
onAdError(unityAdsError.toString()); //my callback for errors, here results INVALID_ARGUMENT error
}
});
}
Does anyone know what is wrong? Thanks in advance
If you check the callback closely the onUnityAdsError has 2 params, first provides the error code and the second param provides you information about what went wrong.
#Override
public void onUnityAdsError(UnityAds.UnityAdsError unityAdsError, String reason) {
onAdError(unityAdsError.toString()); //my callback for errors, here results INVALID_ARGUMENT error
}
So just check the reason and you should be able to find out what is going wrong in your integration.
Here are some methods which you can follow to solve this INVALID_ARGUMENT problem
1. Make sure you are implementing the right Initialization code in your app. There are 2 types of Initialization.
Only Unity ads Initialization
Mediation Initialization
and both methods have their own banner, interstitial, and rewarded ad code.
2. Make sure you enable test mode as Boolean. (i.e: private Boolean testMode = true;) (make sure to do false this before publish on store)
3. You can add your mobile phone as a test device to get test ads on your phone forcefully. for this, you have to first copy the Ad ID of your device. For that, go to your mobile settings > Google > Ads > This device's advertising ID. copy that ID and go to unity dashboard > Monetization > Testing > Add Test Device. Add your device Ads ID here with any name, and now you will be able to see test ads on the device.
Please bear with me as this is going to be a pretty detailed and specific requirement. I also looked into numerous questions already listed on stackoverflow regarding Platform.runLater() usage. But I couldn't find an appropriate response. If someone can identify the correct question and mark this as a duplicate I would be great full all the same. Also I'm a newbie to JavaFX programming, so if I need to modify the architecture, please let me know.
I'm creating a stand-alone JavaFX application to run some unit tests based on user-defined requirements. Before running the actual tests, I need to make sure that the end-user is using the test with latest libraries available, by comparing the manifest files from remote Artifactory repo and the manifest files from jars on the current classpath. I want to store the credentials for Artifactory so that I can reuse them for subsequent executions. If the build numbers from remote and local manifest doesn't match, then I want to display an Error Alert window informing the user. I want to use a pre-loader to display a gif and appropriate messages in each of these steps.
public class UI extends Application
{
String username=null, password=null;
Manifest remoteManifest, localManifest;
#Override
public void init() {
notifyPreloader("Loading Stored Credentials");
loadStoredCredentials();
if(username == null && password == null) {
notifyPreloader("Requesting New Credentials");
getNewCredentials();
}
notifyPreloader("Reading remote manifest");
readRemoteManifest();
notifyPreloader("Reading local manifest");
readLocalManifest();
notifyPreloader("Comparing manifests");
compareManifests();
}
private void getNewCredentials() {
Platform.runLater(new Runnable() {
public void run() {
// Login window for new credentials
//Save credentials
}
});
}
private void readRemoteManifest() {
// REST API call to read remote manifest
if(HttpStatus.SC_UNAUTHORIZED) { // Stored credentials might have been read the first time which are expired, so try again.
getNewCredentials();
readRemoteManifest();
} else {
// update remote manifest object
}
}
private void readLocalManifest() {
// Update local manifest object
}
private void showAlert() {
Platform.runLater( new Runnable() {
// Display Alert window
});
}
private void compareManifests() {
if(remoteManifest == null) {
showAlert();
}
if(localAlert == null) {
showAlert();
}
if(remoteBuildNumber != localBuildNumber) {
showAlert();
}
}
#Override
public void start(Stage primaryStage){
//UI to select options and run tests
}
}
I have three issues with this approach.
If I don't user Platform.runLater() for Alert Windows and Login Windows, then I get an error saying Not on FX application thread; currentThread = JavaFX Application Thread error?
If I use Platform.runLater() then even though the login window is active, the remaining alert windows also show up immediately without waiting to read the login credentials.
If I manually create the file to store current credentials, then any alert window that I have shows up twice.
Let me know if you need any additional info.
Thanks in advance.
EDIT-1:
Someone gave me an answer to fix the first two issues. But now the answer is missing. If they can re-post the answer, it would be helpful for others.
The solution was to move the Platform.runLater() code to compareManifests() method from showAlert() method. Instead I moved the Platform.runLater() to init() method. Now I only have one call to runLater().
The modified code to fix the first two issues.
public class UI extends Application
{
String username=null, password=null;
Manifest remoteManifest, localManifest;
#Override
public void init() {
Platform.runLater( new Runnable() {
public void run() {
notifyPreloader("Loading Stored Credentials");
loadStoredCredentials();
if(username == null && password == null) {
notifyPreloader("Requesting New Credentials");
getNewCredentials();
}
notifyPreloader("Reading remote manifest");
readRemoteManifest();
notifyPreloader("Reading local manifest");
readLocalManifest();
notifyPreloader("Comparing manifests");
compareManifests();
}
});
}
private void getNewCredentials() {
// Login window for new credentials
//Save credentials
}
private void readRemoteManifest() {
// REST API call to read remote manifest
if(HttpStatus.SC_UNAUTHORIZED) { // Stored credentials might have been read the first time which are expired, so try again.
getNewCredentials();
readRemoteManifest();
} else {
// update remote manifest object
}
}
private void readLocalManifest() {
// Update local manifest object
}
private void showAlert() {
// Display Alert window
}
private void compareManifests() {
if(remoteManifest == null) {
showAlert();
}
if(localAlert == null) {
showAlert();
}
if(remoteBuildNumber != localBuildNumber) {
showAlert();
}
}
private void loadStoredCredentials() {
// Read the credentials from a local file
}
#Override
public void start(Stage primaryStage){
//UI to select options and run tests
}
}
Issue 3 is still occurring, there are two login windows being displayed now.
Thanks.
I have successfully implemented In-App-Billing into my app. The IAP is working successfully and I have tested it to make sure its working.
When a user clicks on a button, they have to make an IAP to proceed. However, everytime a user clicks on the button it starts the IAP, even though they have already made the IAP. I want my IAP to be non-consumable obviously. Currently, I'm storing the IAP in SharedPreferences, but if the user reinstalls the app, they lose their IAP.
So how can I use getPurchases() or restoreTransactions() on my onCreate or onClick method to check whether the user has purchases a specific item? I have searched all over the Internet and read through so many samples and it doesn't seem to work, perhaps I am misunderstanding though.
If you need me to post any code, please ask and I'll update my post.
Use this library:
https://github.com/anjlab/android-inapp-billing-v3
How to use?
Use this in your gradle:
repositories {
mavenCentral()
}
dependencies {
implementation 'com.anjlab.android.iab.v3:library:1.0.44'
}
Manifest permission for in-app billing:
<uses-permission android:name="com.android.vending.BILLING" />
How to use library method:
public class SomeActivity extends Activity implements BillingProcessor.IBillingHandler {
BillingProcessor bp;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bp = new BillingProcessor(this, "YOUR LICENSE KEY FROM GOOGLE PLAY CONSOLE HERE", this);
bp.initialize();
// or bp = BillingProcessor.newBillingProcessor(this, "YOUR LICENSE KEY FROM GOOGLE PLAY CONSOLE HERE", this);
// See below on why this is a useful alternative
}
// IBillingHandler implementation
#Override
public void onBillingInitialized() {
/*
* Called when BillingProcessor was initialized and it's ready to purchase
*/
}
#Override
public void onProductPurchased(String productId, TransactionDetails details) {
/*
* Called when requested PRODUCT ID was successfully purchased
*/
}
#Override
public void onBillingError(int errorCode, Throwable error) {
/*
* Called when some error occurred. See Constants class for more details
*
* Note - this includes handling the case where the user canceled the buy dialog:
* errorCode = Constants.BILLING_RESPONSE_RESULT_USER_CANCELED
*/
}
#Override
public void onPurchaseHistoryRestored() {
/*
* Called when purchase history was restored and the list of all owned PRODUCT ID's
* was loaded from Google Play
*/
}
}
Note: onPurchaseHistoryRestored called only first time when you initialize BillingProcessor
Their are lot of ways to check onPurchaseHistoryRestored() but its my opinion. And I have solved it by using..
you can check through transection detail using this code.
#Override
public void onPurchaseHistoryRestored() {
/*
* Called when purchase history was restored and the list of all owned PRODUCT ID's
* was loaded from Google Play
*/
// Check whether 'premium_id' has previously been purchased:
TransactionDetails premiumTransactionDetails = bp.getPurchaseTransactionDetails("premium_id");
if (premiumTransactionDetails == null) {
Log.i(TAG, "onPurchaseHistoryRestored(): Havn't bought premium yet.");
purchasePremiumButton.setEnabled(true);
}
else {
Log.i(TAG, "onPurchaseHistoryRestored(): Already purchases premium.");
purchasePremiumButton.setText(getString(R.string.you_have_premium));
purchasePremiumButton.setEnabled(false);
statusTextView.setVisibility(View.INVISIBLE);
}
}
And the second is to put the check on "product id".check below code.
if(bp.isPurchased(REMOVE_ID_SKU)){
if (bp.loadOwnedPurchasesFromGoogle()) {
Toast.makeText(this, R.string.subs_updated, Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, R.string.no_purchases, Toast.LENGTH_SHORT).show();
}
}
Facebook sdk can only login either with read permission or publish permission, but what if I want both? I only need read permission when logging in to register a user or to authenticate it.
So I thought the right way was to first signIn with SignInMode.READ and then signIn again but now with SignInMode.PUBLISH
The problem is when I try to sign in the second time, it just load the accessToken, I tried to put facebook.signOut() in between but nothing...
facebook.signIn(SignInMode.READ, readPermissions, new GDXFacebookCallback<SignInResult>() {
#Override
public void onSuccess(SignInResult result) {
// Login successful
clientManager.login(result.getAccessToken().getToken());
}
#Override
public void onError(GDXFacebookError error) {
// Error handling
}
#Override
public void onCancel() {
}
#Override
public void onFail(Throwable t) {
}
});
//after the user log ins, the app ask for publish permissions, and I
// try to get it by logging in again but now wit `SignInMode.PUBLISH` like so....
facebook.signIn(SignInMode.PUBLISH, publishPermissions, new GDXFacebookCallback<SignInResult>() {
#Override
public void onSuccess(SignInResult result) {
// Login successful
clientManager.login(result.getAccessToken().getToken());
}
#Override
public void onError(GDXFacebookError error) {
// Error handling
}
#Override
public void onCancel() {
}
#Override
public void onFail(Throwable t) {
}
});
What im trying to ask here is how I get an accessToken capable of publishing after I singin with SignInMode.READ?
I am the developer of gdx-facebook extension.
You are doing it correctly, sign in with read permissions first and then follow with publish permission.
There are 2 cases:
You did not authorize the app yet or did authorize the app with less permissions than the ones you request.
-> you get a new token, which contains all existing and newly requested permissions. All existing tokens become invalid.
You already have granted all the requested permissions.
-> you get the latest token.
What im trying to ask here is how I get an accessToken capable of
publishing after I singin with SignInMode.READ?
Just request the permission you need, you get either a new upgraded token which contains old and new permissions or an old one which already contains the permissions.
It is likely that you already granted the permissions and thats why the token does not change. Look in your FB settings -> apps to see which permissions you granted.
Tokens often look similar the first 10-20 characters. Don't get confused by that.