Android 12: Why doesn't swiping up trigger onStop() - android-lifecycle

Given foregrounded, when I swipe up from the very bottom of the screen:
Android 10 (Nokia 6.1), 11 (Pixel 4): onStop() is called.
Android 12 (Pixel 3): onStop() is not called.
Then, if you drag/swipe an app screen up to dismiss (or destroy) it:
Android 10 (Nokia 6.1), 11 (Pixel 4): onDestroy() is called.
Android 12 (Pixel 3): onStop() and onDestroy() are called consecutively.
compileSdk 31
minSdk 26
targetSdk 31
I have looked into https://developer.android.com/about/versions/12/behavior-changes-all, this behaviour is not documented there.
Is this an expected thing in Android 12? It's quite annoying as it changes the lifecycle behaviours of the app and its activities/fragments/coroutines...

I tried googling this question but I didn't find an answer, so after some investigation I found out that we can rely on activity focus:
class MainActivity : AppCompatActivity() {
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
}
}
onWindowFocusChanged is called when we open the recent apps view (swipe up from the very bottom of the screen).
But it's also called when you show a dialog inside your app or a user interacts with notification at the top of the screen. So I came up with next solution:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding?.root)
binding?.root?.setOnApplyWindowInsetsListener(insetListener)
}
private fun onFocusLost() {
//your code
}
private fun onFocusTaken() {
//your code
}
//---- implementation details -----\\
private var unnecessaryFocusTriggerTimeStamp: Long = 0
private var recentAppsOpened = false
//insetListener is called when a user interacts with notifications (swipes them out)
private val insetListener = View.OnApplyWindowInsetsListener { _, insets ->
unnecessaryFocusTriggerTimeStamp = System.currentTimeMillis()
insets
}
//this method is called when a dialog is going to be shown
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
unnecessaryFocusTriggerTimeStamp = System.currentTimeMillis()
return super.onCreateView(name, context, attrs)
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (!hasFocus && !this.recentAppsOpened && (System.currentTimeMillis() - this.unnecessaryFocusTriggerTimeStamp) > 50) {
this.recentAppsOpened = true
onFocusLost()
} else if (hasFocus && recentAppsOpened) {
this.recentAppsOpened = false
onFocusTaken()
}
super.onWindowFocusChanged(hasFocus)
}
}
This code doesn't look good, but it works fine on Pixel 5 and Galaxy A52 (Android 12).
The lifecycle will look like this:
[swipe up]
onFocusLost
onPause
onStop
[back to the app]
onStart
onResume
onFocusTaken
Use onFocusLost and onFocusTaken to replace onStop and onStart.

Related

Having problems to tap on button on IOS Unity game

I started to work on my first mobile game in Unity. For now, the game I am working is Pong.
The problem that I am having is that I want to create the pause and resume buttons but whenever I create and tap on them, nothing happens.
This is the created button:
This is my canvas:
This is the event system:
This is the code I use for my button. It works fine when I clic it but not when I tap it:
private void pause() {
Time.timeScale = 0f;
}
private void resume()
{
Time.timeScale = 1f;
}
public void pauseResume() {
if (!paused)
{
pause();
paused = true;
}
else {
if (paused)
{
resume();
paused = false;
}
}
}
I tried using Event triggers but it does not work so my theory is that the problem is not in the way I create the buttons or the way the resume pause method is coded. Something is blocking me of using the button in my device (Iphone 8).

Disable window offset when keyboard appears for ios app

Disable window offset when keyboard appears for ios app
I would like to disable the window offset when the keyboard appears for the ios app. For an android app, this is done via AndroidManifest.xml:
<activity ... android:windowSoftInputMode="adjustResize">
When the keyboard appears, the components do not move from their places.
Is it possible to get this behavior for ios?
Found the solution here in the comments:
iOS Make window-scrolling optional for Items
https://bugreports.qt.io/browse/QTBUG-80790
(this workaround works well for me)
========================
Adrian Eddy added a comment - 11 Apr '20 01:44 - edited
I found a workaround that works with QML . The idea is to install an event filter on QQuickItem and listen for a QEvent::InputMethodQuery with Qt::InputMethodQuery::ImCursorRectangle. Then we set its value to empty QRectF and Qt will no longer scroll the view to show that text field.
in C++ prepare a class and expose it to QML:
class Api : public QObject {
Q_OBJECT
....
public:
Q_INVOKABLE void setupImEventFilter(QQuickItem *item) {
static thread_local ImFixer imf;
item->installEventFilter(&imf);
}
}
// somewhere in main():
view.rootContext()->setContextProperty("api", new Api());
We'll need the actual event filter too:
class ImFixer :
public QObject {
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event) override {
if (event->type() == QEvent::InputMethodQuery) {
QInputMethodQueryEvent *imEvt = static_cast<QInputMethodQueryEvent *>(event);
if (imEvt->queries() == Qt::InputMethodQuery::ImCursorRectangle) {
imEvt->setValue(Qt::InputMethodQuery::ImCursorRectangle, QRectF());
return true;
}
}
return QObject::eventFilter(obj, event);
}
};
Finally in QML add:
TextField {
id: tf;
...
Component.onCompleted: api.setupImEventFilter(tf);
}

Splash Screen disappeared after Application.OnCreate was overridden

Initial Issue
I need to register ProcessLifecycleOwner as described here Xamarin.Android Architecture Components in my Application.OnCreate method.
But it had resulted in the error with 6.2.2 version of MvvmCross:
MvvmCross.Exceptions.MvxIoCResolveException: Failed to resolve type MvvmCross.ViewModels.IMvxAppStart occurred
or just stuck on the Splash Screen with 6.2.3.
Fix
Those problems were fixed by advice from Xamarin.Android mvvmcross app crashes when launching with intent filter.
[Application]
public class App : MvxAndroidApplication<Setup, Core.App>
{
public App(IntPtr reference, JniHandleOwnership transfer) :
base(reference, transfer) { }
public override void OnCreate()
{
MvxAndroidSetupSingleton
.EnsureSingletonAvailable(ApplicationContext)
.EnsureInitialized();
base.OnCreate();
}
}
Current Issue
However Splash Screen dissapeared too, only blue background from default theme was left.
A workaround I've found:
public override void OnCreate()
{
Task.Run(() => MvxAndroidSetupSingleton
.EnsureSingletonAvailable(ApplicationContext)
.EnsureInitialized());
base.OnCreate();
}
But due to parallelism it is not reliable, sometimes works, sometimes crashes.
Question
How Splash Screen can be restored?
Your approach is most likely blocking on the UI thread which us causing the UI to block during the time that the expected splash screen is suppose to show.
Try using an async event handler to allow for a non blocking UI call
[Application]
public class App : MvxAndroidApplication<Setup, Core.App> {
public App(IntPtr reference, JniHandleOwnership transfer) :
base(reference, transfer) {
EnsureInitialized = onEnsureInitialized; //Subscribe to event
}
private event EventHandler EnsureInitialized = delegate { };
private async void onEnsureInitialized(object sender, EventArgs args) {
await Task.Run(() => MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext)
.EnsureInitialized());
}
public override void OnCreate() {
EnsureInitialized(this, EventArgs.Empty); //Raise event
base.OnCreate();
}
}

How to get notified when Xamarin Native Android app goes to sleep or is terminated?

How to get notified when Xamarin Native Android app goes to sleep or is terminated?
When searching, I only found an answer for Xamarin.Forms where the Application object allows to override OnSleep.
The background of this question is that I want to save settings when the app either goes to background or is terminated.
Just like the OnSleep method of Xamarin Forms the OnPause method is called in Android Native when the app goes into the background.
You can override OnPause in both an Activity and a Fragment something like this:
protected override void OnPause()
{
base.OnPause();
// Add your code here
}
Update
You can do the same on application level by adding the Android Application class :
Add a new C# class file in your project called MainApplication.cs.
Then add the Application.IActivityLifecycleCallbacks interface where you can find the activity paused method with the activity context in which it was paused so you can add it and do the needful.
#if DEBUG
[Application(Debuggable = true)]
#else
[Application(Debuggable = false)]
#endif
public class MainApplication : Application , Application.IActivityLifecycleCallbacks
{
public MainApplication(IntPtr handle, JniHandleOwnership transer)
: base(handle, transer)
{
}
public void OnActivityPaused(Android.App.Activity activity)
{
base.OnCreate();
// Add some code
}
public override void OnTerminate()
{
base.OnTerminate();
UnregisterActivityLifecycleCallbacks(this);
}
public override void OnCreate()
{
base.OnCreate();
RegisterActivityLifecycleCallbacks(this);
}
}

stop() and start() Methods in main class are called automatically on iOS

My app connects to Facebook and Parse and it works well on simulator and android but it doesn't work on iOS.
The stop() (the Display.getCurrent() returns my current form) and start() methods of the main class are always called automatically. This caused my app (current form) disappeared in unexpected way. I thought the application was crashed but not.
When I double tap on the Home button I can see my app and when I select, it starts again. Any idea can help?
Thanks, William
When iOS app crashes or get killed, it doesn't remove the app from the recent apps you see when you double tap home button. It will only restart the app when you open it (Apps usually get killed on iOS within 10 minutes, depending on if the app is using any resources or not).
Verify that your app is indeed not crashing on iOS. Uncomment the crash reporter from the class that contains start() and stop() methods. This will send you an email if the app crashes and if you are a pro or higher CN1 subscriber.
Just for clarification, the class should look similar to this:
private Form current;
public void init(Object context) {
Display.getInstance().addEdtErrorHandler((evt) -> {
evt.consume();
Log.p("Exception in MyApp version " + Display.getInstance().getProperty("AppVersion", "1.0"));
Log.p("OS " + Display.getInstance().getPlatformName());
Log.p("Error " + evt.getSource());
Log.p("Current Form " + Display.getInstance().getCurrent().getName());
Log.e((Throwable) evt.getSource());
Log.sendLog();
});
}
public void start() {
if (current != null) {
current.show();
return;
}
new StateMachine("/theme");
}
public void stop() {
current = Display.getInstance().getCurrent();
}
public void destroy() {
}

Resources