Wednesday, 27 January 2021

Activity "freezed" after onResume

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", "");
        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 a lot!



from Activity "freezed" after onResume

No comments:

Post a Comment