thecodrr / react-native-google-admob

The most definitive Google Admob library for React Native.
BSD 2-Clause "Simplified" License
17 stars 4 forks source link

Fix problem - causes rejection - Android #10

Open 123dma opened 5 years ago

123dma commented 5 years ago

Hello, I'm working on fixing a problem. My app has been rejected by Violation of Interfering with Apps, Third Party Ads, or Device Functionality policy. I saw some fixes where you need to override android lifecycle events before showing the interstitial. I would like to help implement this in this plugin, but I would need to understand how I can debug this plugin. I believe this improvement is important for everyone using the android plugin.

Actual Behavior (https://play.google.com/about/monetization-ads/ads/#!?zippy_activeEl=interfering#interfering)

(Write what happened. Please add screenshots!)

Reproducible Demo (https://stackoverflow.com/questions/57251947/uncaught-exception-thrown-by-finalizer-all-webview-methods-must-be-called-on-th/57308639#57308639) (https://stackoverflow.com/questions/57234180/issue-violation-of-interfering-with-apps-third-party-ads-or-device-functional)

(Paste the link to an example project and exact instructions to reproduce the issue.) https://stackoverflow.com/questions/46382451/showing-admob-interstitial-ad-even-after-closing-the-app

thecodrr commented 5 years ago

So I have a couple of apps on the Play Store and a few of them also got that violation. I found out checking the AppState before showing the ad fixes this issue. However, I would love a contribution that adds this natively so developers don't have to clutter their code.

I would need to understand how I can debug this plugin.

The best and simplest way is to open your project in Android Studio and wait for it to sync. After it's done, this library will be shown in the projects bar. You can easily edit and debug the code there. You'd just need to create a helper class that checks whether the app is in foreground. Then use that helper method to check before all interstitial ads.

123dma commented 5 years ago

hello @thecodrr how did you solve it? could you detail better? I can't solve it, does this solution already solve this problem? I sent it to Google several times and the app is being rejected every time.

thecodrr commented 5 years ago

Try this code for a quick fix:

if (AppState.currentState === 'active') {
      AdMobInterstitial.showAd();
}

A question: do you have ads on app startup? If so then using my code may help however if you ask for permissions before showing the ad and the permissions dialog overlays the ad, you will get the rejection. A quick fix for that is to ask for permissions after the ad has been shown in the adClosed listener.

123dma commented 5 years ago

Hello @thecodrr . Yes the interstitial is presented at the beginning of the App, is the only place I display, I tried this solution, but in a version of the App that is not in react-native, in another plugin, but this other plugin I got debug, but using a variable, i'm not good at java, so i don't know if i did it correctly, if the solution is this i must have done something wrong. I do not use permissions, the App does not need additional permissions. do i have to put this in this code? RNAdMobInterstitialAdModule.java

@ReactMethod
    public void showAd(final Promise promise) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run () {
                if (mInterstitialAd.isLoaded()) {
                    mInterstitialAd.show();
                    promise.resolve(null);
                } else {
                    promise.reject("E_AD_NOT_READY", "Ad is not ready.");
                }
            }
        });
    }
thecodrr commented 5 years ago

No, there's no need to edit the Java files. What I gave you needs to be put in react-native code.

Edit: If you want to make a PR, you need to search for a method of checking if the application is in foreground and active and use it in the code you pasted.

123dma commented 5 years ago

@thecodrr did you get approval using only this fix ?, I tried this code here but it was rejected.. Do you serve interstitials at the beginning of the app? without the user clicking a button too? my code.

import { AdMobInterstitial, } from 'react-native-admob'

class SplashScreen extends React.Component {
  constructor(props) {
    super(props);
  }

  state = {
    appState: AppState.currentState,
    adLoaded: false,
  };

  _handleAppStateChange = (nextAppState) => {
    if (
      this.state.appState.match(/inactive|background/) &&
      nextAppState === 'active'
    ) {

    }
    if (this.mounted) this.setState({ appState: nextAppState });
    console.log(this.state.appState)
  };

  componentWillUnmount() {
    this.mounted = false;
    console.log(this.mounted)
    AppState.removeEventListener('change', this._handleAppStateChange);
  }

  async componentDidMount() {

    console.log('this.mounted')
    this.mounted = true;
    console.log(this.mounted)
    AppState.addEventListener('change', this._handleAppStateChange);

    AdMobInterstitial.addEventListener('adFailedToLoad',
      () => {
        setTimeout(this.CallMain, 10000)
      });

AdMobInterstitial.addEventListener('adLoaded',
      () => {
        if (this.state.appState === 'active') {
          AdMobInterstitial.showAd().catch(error => {
            AdMobInterstitial.removeAllListeners();
            this.props.navigation.navigate('Main')
          });
          AdMobInterstitial.removeAllListeners();
          this.props.navigation.navigate('Main')
        }
      }
    );

    AdMobInterstitial.addEventListener('adClosed',
      () => {
        console.log('AdMobInterstitial => adClosed');
        AdMobInterstitial.removeAllListeners();
        this.props.navigation.navigate('Main')

      }
    );
    AdMobInterstitial.addEventListener('adLeftApplication',
      () => console.log('AdMobInterstitial => adLeftApplication')
    );

    if (this.state.appState === 'active') {
      inteShow = true;
      AdMobInterstitial.setAdUnitID('ca-app-pub-3940256099942544/1033173712');
      AdMobInterstitial.setTestDevices([AdMobInterstitial.simulatorId]);

      AdMobInterstitial.requestAd().catch(error => {
        console.log(error.toString())
        if (error.toString() == 'Error: Ad is already loaded.') {
          AdMobInterstitial.showAd();
          AdMobInterstitial.removeAllListeners();
          this.props.navigation.navigate('Main');
        }
      });
    }
  }

  render() {
thecodrr commented 5 years ago

@123dma Check for AppState right before AdMobInterstitial.showAd() and it should be fixed (because there is a slight interval between the ad request and the ad showing so check right before the ad is shown).

123dma commented 5 years ago

Hello @thecodrr I believe there is not chance to fix this problem this way. This way do not solve 100%. I can reproduce the problem, when click onpause after this check. After this verification should have a way to destroy the request. I can´t find the way to do it. I believe only inside the code. You need to be very fast, but you can reproduce.

 if (this.state.appState === 'active') {
//this moment click onpause.
         AdMobInterstitial.showAd();
 }

to easier check put a console log after ( if (this.state.appState === 'active') {). Open the app and clicking at pause and look logcat. If you click at the right moment you can see the log the app will go to background and the ad will be showed.

 if (this.state.appState === 'active') {
// add console.log

         AdMobInterstitial.showAd();
 }