Activity "freezed" after onResume

Viewed 344

I'm implementing AdMob in my game but there is a problem: after closing the rewarderAd video, the game does not respond to touch, nor log anything.

I searched on Web founding a suggestion to insert gameView.requestFocus(); in the ad's callback but it doesn't work.

MyGdxGame.java (core):

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;

public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
    public final static float CARD_WIDTH = 1f;
    public final static float CARD_RATIO_W_H = 277f / 200f;
    public final static float CARD_HEIGHT = CARD_WIDTH * CARD_RATIO_W_H;
    public final static float MINIMUM_VIEWPORT_SIZE = 7f;

    float widthPixel;
    float heightPixel;
    float widthPixel1Card;
    float heightPixel1Card;

    CardBatch cards;
    CardActions actions;
    
    Vector3 vector3touch;

    Stage stage;
    Label labelStart;

    ModelBatch modelBatch;
    TextureAtlas atlas;
    ShapeRenderer shapeRenderer;
    OrthographicCamera cam;
    private Viewport viewport;

    private IActivityRequestHandler myRequestHandler;
    public MyGdxGame(IActivityRequestHandler handler) {
        myRequestHandler = handler;
    }

    @Override
    public void create () {
        widthPixel =  Gdx.graphics.getWidth();
        heightPixel =  Gdx.graphics.getHeight();
        widthPixel1Card = widthPixel / MINIMUM_VIEWPORT_SIZE;
        heightPixel1Card = widthPixel1Card * CARD_RATIO_W_H;

        shapeRenderer = new ShapeRenderer();

        modelBatch = new ModelBatch();
        atlas = new TextureAtlas("carddeck.atlas");
        Material material = new Material(
                TextureAttribute.createDiffuse(atlas.getTextures().first()),
                new BlendingAttribute(false, 1f),
                FloatAttribute.createAlphaTest(0.2f));
        cards = new CardBatch(material);

        cam = new OrthographicCamera(MINIMUM_VIEWPORT_SIZE, MINIMUM_VIEWPORT_SIZE * CARD_RATIO_W_H);
        viewport = new FitViewport(MINIMUM_VIEWPORT_SIZE, MINIMUM_VIEWPORT_SIZE * CARD_RATIO_W_H, cam);

        Gdx.input.setInputProcessor(this);

        actions = new CardActions();

        vector3touch = new Vector3();

        stage = new Stage(new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getWidth() * CARD_RATIO_W_H));

        Texture texture = new Texture(Gdx.files.internal("arial32.png"), true); // true enables mipmaps
        texture.setFilter(Texture.TextureFilter.MipMapLinearNearest, Texture.TextureFilter.Linear); // linear filtering in nearest mipmap image
        BitmapFont fontDistanceField = new BitmapFont(Gdx.files.internal("arial32.fnt"), new TextureRegion(texture), false);
        Label.LabelStyle labelStyleDistanceField = new Label.LabelStyle();
        labelStyleDistanceField.font = fontDistanceField;

        labelStart = new Label("Touch to start", labelStyleDistanceField);
        labelStart.setSize(widthPixel/2, heightPixel1Card);
        labelStart.setPosition(widthPixel/2-widthPixel1Card*1.5f, (heightPixel/2f)-heightPixel1Card);
        labelStart.setAlignment(Align.left);
        labelStart.setWrap(false);
        labelStart.setColor(Color.BLACK);
        stage.addActor(labelStart);
    }

    @Override
    public void render() {
        final float delta = Math.min(1/30f, Gdx.graphics.getDeltaTime());

        Gdx.gl.glClearColor(167/255f, 219/255f, 216/255f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

        drawCoordsDebug();

        actions.update(delta);

        modelBatch.begin(cam);
        modelBatch.render(cards);
        modelBatch.end();

        stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
        stage.draw();
    }

    public static class CardActions {
        Pool<CardAction> actionPool = new Pool<CardAction>() {
            protected CardAction newObject() {
                return new CardAction(CardActions.this);
            }
        };
        Array<CardAction> actions = new Array<>();

        public void actionComplete(CardAction action) {
            action.card.inMovimento = false;
            actions.removeValue(action, true);
            actionPool.free(action);
        }

        public void update(float delta) {
            for (CardAction action : actions) {
                action.update(delta);
            }
        }

        // ...
    }

    public static class CardAction {
        public CardActions parent;
        public Card card;
        public final Vector3 fromPosition = new Vector3();
        public float fromAngle;
        public final Vector3 toPosition = new Vector3();
        public float toAngle;
        public float speed;
        public float alpha;

        public CardAction(CardActions parent) {
            this.parent = parent;
        }

        public void update(float delta) {
            alpha += delta * speed;
            if (alpha >= 1f) {
                alpha = 1f;
                parent.actionComplete(this);
            }
            card.position.set(fromPosition).lerp(toPosition, alpha);
            card.angle = fromAngle + alpha * (toAngle - fromAngle);
            card.update();
        }
    }

    @Override
    public void resize(int width, int height) {
        cam.position.set(0, 0, 10);
        cam.lookAt(0, 0, 0);

        cam.update();

        viewport.update(width, height);

        stage.getViewport().update(width, height);
    }

    @Override
    public boolean keyDown(int keycode) {return false; }
    @Override
    public boolean keyUp(int keycode) {return false; }
    @Override
    public boolean keyTyped(char character) {return false; }
    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {return false; }
    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        Gdx.app.log("touchUp", "");

        // handle it

        return true;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {return false; }
    @Override
    public boolean mouseMoved(int screenX, int screenY) {return false; }
    @Override
    public boolean scrolled(int amount) {return false; }

    public void drawCoordsDebug() {
        shapeRenderer.setProjectionMatrix(cam.combined);
        shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
        shapeRenderer.setColor(Color.WHITE);
        float wTot = Gdx.graphics.getWidth();
        float hTot = Gdx.graphics.getHeight();
        shapeRenderer.circle(0, 0, 1f);
        for(float w = 0; w >= -wTot; w--) {
            shapeRenderer.line(new Vector2(w,-hTot), new Vector2(w,hTot));
        }
        for(float w = 0; w <= wTot; w++) {
            shapeRenderer.line(new Vector2(w,-hTot), new Vector2(w,hTot));
        }
        for(float h = 0; h >= -hTot; h--) {
            shapeRenderer.line(new Vector2(-wTot, h), new Vector2(wTot, h));
        }
        for(float h = 0; h <= hTot; h++) {
            shapeRenderer.line(new Vector2(-wTot, h), new Vector2(wTot, h));
        }
        shapeRenderer.end();
    }

    @Override
    public void dispose () {
        modelBatch.dispose();
        atlas.dispose();
        cards.dispose();

        shapeRenderer.dispose();

        stage.dispose();
    }
}

DesktopLauncher.java:

public class DesktopLauncher implements IActivityRequestHandler {
    private static DesktopLauncher application;
    public static void main (String[] arg) {
        LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();

        if (application == null) {
            application = new DesktopLauncher();
        }
        new LwjglApplication(new MyGdxGame(application), config);
    }

    @Override
    public void showAds(boolean show) {    
    }
}

AndroidLauncher.java:

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RelativeLayout;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdSize;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.RequestConfiguration;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedList;
import java.util.List;

public class AndroidLauncher extends AndroidApplication implements IActivityRequestHandler {

    private final int SHOW_ADS = 1;
    private final int HIDE_ADS = 0;

    protected AdView mAdView;
    
    @Override
    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MobileAds.initialize(this, initializationStatus -> { });
        
        // Create and setup the AdMob view
        mAdView = new AdView(this);
        mAdView.setAdSize(AdSize.BANNER);
        mAdView.setAdUnitId("");
        AdRequest adRequest = new AdRequest.Builder().build();
        mAdView.loadAd(adRequest);
        
        // Create the layout
        RelativeLayout layout = new RelativeLayout(this);

        // Do the stuff that initialize() would do for you
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        // Add the libGDX view
        View gameView = initializeForView(new MyGdxGame(this));
        layout.addView(gameView);

        // Add the AdMob view
        RelativeLayout.LayoutParams adParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
        adParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
        adParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
        layout.addView(mAdView, adParams);
        layout.setKeepScreenOn(true);

        setContentView(layout);
    }
    
    protected Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case SHOW_ADS:
                {
                    Activity activityContext = AndroidLauncher.this;
                    if (rewardedAd.isLoaded()) {
                        RewardedAdCallback adCallback = new RewardedAdCallback() {
                            @Override
                            public void onRewardedAdOpened() {
                                // Ad opened.
                            }
    
                            @Override
                            public void onRewardedAdClosed() {
                                // Ad closed.
                                rewardedAd = createAndLoadRewardedAd();
    
                                gameView.requestFocus();
                            }
    
                            @SuppressLint("ApplySharedPref")
                            @Override
                            public void onUserEarnedReward(@NonNull RewardItem reward) {
                                gameView.requestFocus();
                            }
    
                            @Override
                            public void onRewardedAdFailedToShow(int errorCode) {
                                // Ad failed to display.
                            }
                        };
                        rewardedAd.show(activityContext, adCallback);
                    } else {
                        if (mInterstitialAd.isLoaded()) {
                            mInterstitialAd.show();
                        }
                    }
                    break;
                }
                case HIDE_ADS:
                {
                    mAdView.setVisibility(View.GONE);
                    break;
                }
            }
        }
    };

    // This is the callback that posts a message for the handler
    @Override
    public void showAds(boolean show) {
        handler.sendEmptyMessage(show ? SHOW_ADS : HIDE_ADS);
    }



    @Override
    protected void onResume() {
        super.onResume();

        gameView.requestFocus();
    }
}

Also the method touchUp does not print anything in the log.

A few attempts:

Replacing gameView.requestFocus(); whit this:

runOnUiThread(new Runnable() {
        @Override
        public void run() {
            gameView.requestFocus();
    }
});

The strange thing is that it happens on Android 10, while on Android 6.0 all works fine even without these lines of code.

Note: I know that the rewardedAd must used only if the user want to view it to receive a reward, here I'm using it to test the flow, before continuing implementing it correctly in the Game.

Thanks!

3 Answers

According to the documentation you should run all the operations on the main thread. When you show your ad you're invoking your code from a new Handler() which is not running on the main thread, to fix this you need to run this handler on the main thread by changing the following line

protected Handler handler = new Handler()

to

protected Handler handler = new Handler(Looper.getMainLooper())

Change gameView.requestFocus() to gameView.SetFocusable(True)

It worked for me once, maybe you are encountering the same error that I was !

Have you tried setting gameView.setFocusable(true) and gameView.setFocusableInTouchMode(true) and then requesting the focus?

Related