libgdx updating scene when app goes invisible - java

I am using libgdx's render() function to update scene with time delta, and then draw the scene. However, when the app goes invisible, the render() function is no longer being called, which causes the scene to stop updating, E.g., an animation will stuck at the last frame, until app goes back to foreground.
The problem is that I am building an online real-time game which cannot be 'paused' on client. When the app switches to background, the game must keep updating the scene even without drawing the scene, so that when the user switches back to the app, he can see the most up-to-date scene, instead of a pause-and-resume scene that lags back.
My question is how to achieve this in libgdx, so that the scene keeps updating even when app goes background? Does anyone experience the same problem as mine?

In a Libgdx app, the render thread is only invoked when rendering, thus only when it has the screen. For most games this is the only time the game should be doing any computations, so combining the update and render makes a lot of sense.
In Android, an app generally only gets CPU cycles assigned to it by the OS when its "foreground". To run an app in the background requires using a different platform API (See, for example, https://developer.android.com/training/best-background.html.) Simply creating additional threads will not be sufficient to keep your app running in the background (the OS may decide to terminate it completely if it wants the RAM back for something else).
Generally, users do not expect an Android application to continue running (and using battery) when not in the foreground.
Additionally, even if your app is coded to run in the background correctly, when the foreground app is using the CPU significantly, your background app may not be run.
You can hook into the suspend and resume events to pause your game and then "fast-forward" your game state as necessary. Or, do your computations in the cloud if you require that they run in real-time.

You can try to update your scene from other thread if it doesn't need GL context in it.
Or you can override onPause() method in your AndroidApplication class in Android projects, and keep using render() method for updating.
In onPause() method of AndroidApplication graphics are told to stop rendering.
Maybe you can create interface class to update your scene, without constant rendering when the app is paused, concerning the battery life.
This is the code from onPause() method.
Maybe this will lead you to correct solution.
#Override
protected void onPause () {
boolean isContinuous = graphics.isContinuousRendering();
graphics.setContinuousRendering(true);
graphics.pause();
input.onPause();
if (isFinishing()) {
graphics.clearManagedCaches();
graphics.destroy();
}
graphics.setContinuousRendering(isContinuous);
graphics.onPauseGLSurfaceView();
super.onPause();
}

Related

Android: Customizing recent apps thumbnail (screenshot by default)

The app I'm working on shows some sensitive information that must not be shown on the "Recent Tasks" screen when stopping the app by pressing the home button.
I'd like to blur the sensitive data in the screenshot or show the app logo instead.
I am aware of the following approaches but they don't fit my requirements:
Setting the actvitie's android:excludeFromRecents to true in the manifiest prevents the app from being shown at all in the recent tasks. This would disrupt the user experience.
Using FLAG_SECURE results in a blank card on the recents tasks screen. (How do I prevent Android taking a screenshot when my app goes to the background?) I don't like the blank screen. However, I'll stick to this solution if there is no workaround.
Overriding onCreateThumbnail seems like the ideal solution but, unfortunately, doesn't work as it's currently not invoked by the OS :( (https://code.google.com/p/android/issues/detail?id=29370)
And then there are some workarounds that I tried out but that didn't work as hoped:
Start a new activity that shows the app logo in onPause so that it's screenshot is shown instead of the actual activitie's one. But the new activity takes too long to open and it disrupts the user experience.
Set the activitie's content view to an image of the app logo in onPause. That seemed like a great solution to me. Unfortunately, the screenshot for the recent tasks screen is taken at an unspecified time. During testing the app logo quickly appears before the app is closed when pressing 'Home' but the resulting screenshot shows the activity a short time before that.
Removing the sensitive data from the widgets (e.g. textView.setText("")) has the same problem of screenshot timing just mentioned.
Any alternative ideas or solutions to the listed workarounds?
I looked into this a couple of months ago for the same purpose as you.
Unfortunately, I had to conclude that it is simply not possible. I dug through the android source code and confirmed it.
There is no callbacks or methods from android that allows you to customize it (that works anyway). Besides FLAG_SECURE, this part of the code does not accept any input or change.
OnPause and similar lifecycle methods are called too late (the screenshot is taken already). All lifecycle methods that would hint that you're about to go into the background runs too late.
The image you see in the recent tasks is an actual screenshot - and thus isn't affected by changes you do (too late) to your view. That means you can't modify your view just-in-time (like making it invisible, replacing with something else, adding SECURE_FLAG, or any other obstruction of the view). As an aside, these images can be found on an emulator at /data/system_ce/0/recent_images.
The only exception is using FLAG_SECURE, which will prevent the screenshot from being taken of your application. I experimented with setting this FLAG in onPause and removing it in onResume, however as mentioned already these lifecycle methods runs after the screenshot is taken already, and thus had absolutely no effect.
As discussed in How to change the snapshot shown by recent apps list? there used to be a callback that you could use to customize the thumbnail: onCreateThumbnail. However, this does not work and it is never called. To be clear, the callback is still there, it is simply never called by the OS. The fact that it stopped working is poorly documented, but apparently was silently deprecated/removed in 4.0.3
As for the thumbnail itself, it is a screenshot taken serverside. It is taken before onPause is called (or in fact before any callbacks indicating that your activity is about to go into the background is called).
When your app does go into the background, your actual view is animated (to get that zoom-out transition). That animation can be affected through changes you do in onPause (if you're fast enough that is) (I experimented with setting opacity to 0 on the window among other things). This will however only affect the animation. When the animation is finished, the view is replaced by the screenshot taken earlier.
Also see these questions that discuss this:
When does Android take its recent apps switcher screenshot?
Show custom application image in task manager on ICS or JB
Android never call method onCreateThumbnail
Currently (28/10/2020) is impossibile customizing app thumbnail in recent apps screen.
As explained by #Dellkan in the previous answer, the onCreateThumbnail method is not called anymore by the OS.
Unfortunately, also the suggestion to create a kind of launcher/splash screen without the FLAG_SECURE flag to let the app take a screenshot of that activity is not working, because the screenshot is taken on the activity you see and not at the launch of the app.
You cannot even customize the color of window background when using FLAG_SECURE as reported here.
How about implementing a layout overlay on top of your entire activity?
Make it transparent, it's click-through by default, so no negative impact on UX while in use.
In onPause() set a half-transparent, blurred image as the background of that layout, the data will be scrambled behind it. In onResume() change the background to fully transparent again. Voila.
It might be faster than other types of overlays. The positive side effect is, if you do the unblurring as a short animation effect when the user goes back (with a proper library that uses C++ instead of Java), it might even look cool and the users wouldnt even mind seeing it.
I haven't tried this myself, but it's something you haven't tried yet.
Since onPause is called to late, I use WindowFocusChangeListener to observe when the Fragment loses focus. At this moment we can hide all view which show sensitive data:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// hide sensitive data when window moves to background (before system screenshot is captured)
myViewWithSensitiveData.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
There is a way to customize it. You need your Activities with sensitive data to FLAG_SECURE in onCreate before you setContentView. Then you need an empty Activity, which renders whatever you want to have as the customized thumbnail. This usually is some sort of splash screen. This new Activity needs to be the launcher and is the only Activity not FLAG_SECURE. This Activity is launched and in onResume starts your actual Activity with the sensitive data.
Android OS will take a screenshot of that new Activity at the beginning of your App. Unfortunately the users will also see this Activity for a short moment. Since every other Activity is FLAG_SECURE, Android OS will use the only available screenshot it made at the beginning.
Was looking for a solution and found some dirty things in case you don't want to use 'FLAG_SECURE'. It doesn't give a nice picture but protects data and doesn't prevent making screenshots for the user while they are in the app.
protected void onPause () {
this.getWindow().getDecorView().getRootView().setScaleX((float)200);
this.getWindow().getDecorView().getRootView().setScaleY((float)200);
super.onPause();
}
protected void onResume () {
this.getWindow().getDecorView().getRootView().setScaleX((float)1);
this.getWindow().getDecorView().getRootView().setScaleY((float)1);
super.onResume();
}
I think this can only achieve through BroadCastReceiver but there is no receiver present. So therefore you first disable default screenshot functionality in android and then implementing your own functionality to take screenshot and before taking screenshot you should blur your secure information.

Timer task makes app crash

I have a Canvas object in my app and I want to update it each 20 ms. I tried Java's TimerTask, but it makes my app crash after few updates. Here's update code;
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
invalidate(); // canvas's update
}
}, 20, 20);
What shall I do?
Edit: Error log:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
In this case, run() is called on a background thread, and invalidate() is called on that background thread. This will lead to the exception you have in your question, as those sorts of updates can only be done on the main application thread.
Since calling invalidate() 50 times/second is unlikely to be the right solution for whatever problem you are trying to solve, you may wish to ask a separate Stack Overflow question where you explain what sorts of changes you are trying to make to your UI every 20ms, to see what the recommended approach is. Or, pick up a book on Android game development — while you may not be building a game, game developers drawing to a Canvas also have these sorts of concerns, so the techniques that they use might be relevant to you as well.
If you really want to do this, just change invalidate to postInvalidate.

Rajawali live wallpaper slow back from sleep

Im working on a 3D live wallpaper for android using rajawali. When I turn the screen off and on, its loading the whole scene again what takes some seconds depending on device and scene complexity. Id like to keep my wallpaper in memory "forever" to have the wallpaper visible instantly after the screen turn on.
From what I have found this behavior is because openGL loose context when calling OnPause and OnResume events. I tried setPreserveEglContextOnPause which "may" preserve context, but actually it changes nothing.
Can anybody help me to solve this problem?

Prevent LibGDX game from completely stopping when paused

In my LibGDX game for android, if the user backs out of the game (either by pressing the home button or switching to another application) LibGDX's built in pause() method is supposed to run. Now, this is fine, and it works fine as well. My problem is that if I back out of the game to do whatever, and then rejoin the game, it has restarted the app completely (kind of like if every time you exited and rejoined in the middle of a game of Pacman your score would be zero and all the dots would be back). For my screen switching, it is necessary that the game NOT restart every time the user exits, but simply enter the corresponding state to actually simulate the 'paused' game. How do I stop LibGDX/Android from killing the game altogether upon user exit, but simply pausing it?
The libGDX application lifecycle matches the lifecycle of the Android Activity as documented in the ApplicationListener interface so you should expect the same behavior. When you press the home button while in a libGDX game then the pause method will be called, which is the same as onPause in Android. The game will go to the background but will stay in memory. However this is not guaranteed and the OS might release the games memory for other applications, there really is no way to get around this. In the case when the game comes back to the foreground and the game restarts you'll need to load the games state from when it was paused.
I've written my own article on how to save and load the game state using Json in libGDX, maybe that will be useful to you.
You should use Asset Manager to prevent it, here a good tutorial :
http://code.google.com/p/libgdx/wiki/AssetManager
the official doc :
http://code.google.com/p/libgdx/wiki/AssetManager
Load all of your textures with assets manager
I have the same problem, but I found the solution by:
Disable option in Developer options called
- Don't keep activities (Destroy every activity as soon as the user leaves it)

Android screensaver in app if it's not used

I created an android app. The screen never turns off:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Now I want to show a picture if the screen is not pressed for 5 minutes or something else. The app should not be closed, when pressing on the image the app should be open.
How can I realize that?
I would discourage you from taking this approach. Users expect to have a consistent user experience between various apps on their devices, and likely have a preference to how their device sleeps, either by having specified a sleep timeout or displaying a daydream as introduced in Android 4.2.
If you'd like to provide users with the option to display a screensaver associated with your app, I suggest including a Daydream in your app and otherwise acknowledging the user's preferences.
That being said, if you cannot use Daydream, you could observe if the app is being used or not. Two things come to mind:
Have the root view of your activity intercept touch events to observe if any of its children have been touched.
Observe the activity's onPause() and onResume() to acknowledge that the activity is still being displayed.
You could then invoke a Runnable by posting it to a view using postDelayed(Runnable action, long delayMillis), being wary to remove it when the activity is paused or the timer should be reset using removeCallbacks(Runnable).
I solved the problem!!!
I used that event:
public boolean dispatchTouchEvent(MotionEvent ev)
{
super.dispatchTouchEvent(ev);
// cancel my Timer
return true;
}
Thanks!!

Categories