[SOLVED... kind of] Changing iOS/Android App Switcher Screenshots?

I'm trying to change the screenshot that is caught by the OS when the application goes to the background / is minimized and then is viewed in the app switcher (physical button on Galaxy, swipe up on iPhone X, etc.). I have tried drawing a sprite when os_is_paused is true; however, the issue is that os_is_paused triggers after the focus shifts back to the app, which is obviously too late. Here's that code for reference:

Code:
if (os_is_paused()) {
    draw_sprite_stretched(Pause_Mosaic, 0, 0, 0, 900, 1600);
}

My questions are:

  1. Is there a native GMS:2 solution that I am missing? Specifically, can I trigger the pause event as the app loses focus rather than when it regains focus?
  2. Since GMS:2 exports to Xcode, I can write a delegate to handle the event handling for apps moving to the background. How would I do this with Android? Is there a way to access the Java code that GMS:2 exports?

Thanks in advance!
 
I didn't realize the gravity of my question until I started looking into it further. Yikes. I'll put down my thoughts here in case anyone ever stumbles upon this in the future.

I settled on a blur effect rather than on changing the app screenshot (Android does not let you handle this task easily, and I wanted the experience to be comparable between the two platforms). Here's the code for each platform:

iOS

BlurEffectPlugin.h
Code:
#import <Foundation/Foundation.h>


@interface BlurEffectPlugin : NSObject

- (double)recordRoomNumber:(double)roomNumber;

@end
BlurEffectPlugin.mm
Code:
#import <Foundation/Foundation.h>
#import "BlurEffectPlugin.h"
#include <asl.h>
#include <stdio.h>


@implementation BlurEffectPlugin {
    int roomNumber;
}

// Singleton variables
//static BlurEffectPlugin *shared;
//static BOOL initialized = NO;

// Instance variables

- (void)appHasGoneInBackground {
    NSLog(@"appHasGoneInBackground method");
   
    if (self->roomNumber != 3) return;
   
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    UIBlurEffect *blurEffect;
    UIVisualEffectView *blurEffectView;
   
    blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
    blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
   
    blurEffectView.frame = window.frame;
    blurEffectView.tag = 221122;
   
    [window addSubview:blurEffectView];
   
}

- (void)appDidBecomeActive {
    NSLog(@"appDidBecoomeActive method");
   
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    [[window viewWithTag:221122] removeFromSuperview];

    window.alpha = 0.99f;
}

- (double)recordRoomNumber:(double)roomNumber {
    int rn = (int)roomNumber;
    self->roomNumber = rn;
    return 0.0;
}

- (id)init {
    self = [super init];
    if (self) {
        // Initialization code here.
        self->roomNumber = 0;
       
        // Set up event listeners.
        [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(appHasGoneInBackground)
                                         name:UIApplicationWillResignActiveNotification
                                         object:nil
         ];
       
        [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(appDidBecomeActive)
                                         name:UIApplicationDidBecomeActiveNotification
                                         object:nil
         ];
       
       
    }
    return self;
}

@end
I created a GMS:2 plug-in with the following options:
Compiler Flags: <empty>
Linker Flags: <empty>
Class Name: BlurEffectPlguin
App Delegate Class Name: <empty>
Everything else: <empty>

I added a Generic Placeholder function named record_room_number; this was implemented in the RoomStart event of one of my singletons. When that room (Room #3) is active and the app is minimized, the app will automatically blur. However, every other room does not have the blur effect.


Android

AndroidBlurPlugin.java
Code:
package ${YYAndroidPackageName};

import android.util.Log;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.ProcessLifecycleOwner;
import android.view.WindowManager;

import ${YYAndroidPackageName}.R;
import ${YYAndroidPackageName}.RunnerActivity;

import com.yoyogames.runner.RunnerJNILib;

public class AndroidBlurPlugin implements LifecycleObserver {

    private static final String TAG = "yoyo";
    private int roomNumber = -1;
    private int pauseCount = 0;
    private boolean triggeredSecureMode = false;

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void addBlur() {
        if (this.pauseCount >= 3) return;
        Log.i(TAG, "ON_PAUSE: calling addBlur method");
        if (this.roomNumber == 3) {
            this.pauseCount ++;
            if (this.pauseCount < 3) return;
            Log.i(TAG, "ON_PAUSE: roomNumber == 3 and pauseNumber == 3, adding blur...");
            try {
                RunnerActivity.CurrentActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
                this.triggeredSecureMode = true;
            }
            catch (Exception ex) { Log.i(TAG, "Exception: " + ex.toString()); }
        }
    }

    public double changeRoomNumber(double roomNumber) {
        this.pauseCount = 0;
        this.roomNumber = (int) roomNumber;
        if (this.triggeredSecureMode) {
            try {
                RunnerActivity.CurrentActivity.runOnUiThread(new Runnable() {

                    @Override
                    public void run() {
                        try {
                            RunnerActivity.CurrentActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
                        }
                        catch (Exception ex) { Log.i(TAG, "Inner Exception Exception: " + ex.toString()); }
                    }
                });      
            }
            catch (Exception ex) { Log.i(TAG, "Outer Exception Exception: " + ex.toString()); }
            this.triggeredSecureMode = false;
        }
        return roomNumber;
    }

    public double registerAsObserver() {
        Log.i(TAG, "onCreate: registerAsObserver within Android.");
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
        Log.i(TAG, "onCreate: registerAsObserver within Android complete.");
        return 99.0;
    }
}
I had to use a different approach here. Android deprecated a function to change the app switcher screenshot. I originally tried to blur the screen myself using a library from GitHub, but the issue is that the code would not run quickly enough (similar to the original problem). Instead, I settled on the following solution: if a user is in Room #3 and triggers the Pause event three times, the Android FLAG_SECURE is added to the activity's window which automatically blurs the screenshot taken (the unfortunate side effect is that screenshots can no longer be taken, so it's a trade-off). When a user enters a new room, the effect is removed.

Here are the details for my Android plug-in:
Class Name: AndroidBlurPlugin
Android Permissions: <empty>
Inject to Gradle dependencies:
dependencies {
compile 'android.arch.lifecycle:extensions:1.1.1@aar'
}
Everything else: <empty>

I added two Generic Placeholder functions here: record_room_number_android and register_android_listener. The former has a similar functionality to its iOS counterpart, and the latter is used to initialize the object in the first place (this could have probably been done using either a constructor or initializer block within the Java, but I figured that this ensures that all of the objects have been fully initialized before running the connector code).


Hopefully this helps someone else out there!
 
Top