Status Update
Comments
il...@google.com <il...@google.com> #2
Jeremy, is this still an issue? I think the problem was that you had two transitions targeting the same View for the same action (e.g. two Slide() transitions).
[Deleted User] <[Deleted User]> #3
I have a similar issue with plain AnimatorSet:
set.start()
set.pause()
set.setCurrentPlayTime(100)
set.setCurrentPlayTime(0)
set.setCurrentPlayTime(100)
set.resume()
doesn't play animation in resume().
[Deleted User] <[Deleted User]> #4
Should clarify that if I filter out setCurrentPlayTime(0)
(or replace it with setCurrentPlayTime(1)
) it works well.
Also even with setCurrentPlayTime(0)
, onAnimationEnd
is notified with correct delay (as if the animation has played).
eu...@gmail.com <eu...@gmail.com> #5
@
I think that is intended for Animator. If you set the currentPlayTime
to 0 or the total duration the animator completes. We do some
eu...@gmail.com <eu...@gmail.com> #6
Did some investigation on the Fragment side and it seems like the merged transition is targeting correctly.
Exiting Transition: Slide@aa9288e: tgts(android.widget.LinearLayout{f9add3d})
>>>>> ExitingViews <<<<<
View: android.widget.LinearLayout{f9add3d}
Entering Transition: Slide@35b8af: tgts(android.widget.LinearLayout{b7f24bc})
>>>>> EnteringViews <<<<<
View: android.widget.LinearLayout{b7f24bc}
Final merged transition: TransitionSet@7bc1c45:
TransitionSet@e133f9a:
Slide@aa9288e: tgts(android.widget.LinearLayout{f9add3d})
Slide@35b8af: tgts(android.widget.LinearLayout{b7f24bc})
merged transition passed to controlDelayedTransition: TransitionSet@7bc1c45:
TransitionSet@e133f9a:
Slide@aa9288e: tgts(android.widget.LinearLayout{f9add3d})
Slide@35b8af: tgts(android.widget.LinearLayout{b7f24bc})
Still digging.
il...@google.com <il...@google.com> #7
Branch: androidx-main
commit 567b7459329d1ec8d27a8c6fe1c4a86442065d7d
Author: Jeremy Woods <jbwoods@google.com>
Date: Tue Sep 26 20:06:54 2023
Add additional logging for transitions
Adding more debug logging in transitions to track the entering and
exiting transitions as well as the final merged transition and its
targets.
Test: added logging
Bug: 300157785
Change-Id: I0d9ad72b865422493c6c895ddb6115abf85eed16
M fragment/fragment/src/main/java/androidx/fragment/app/DefaultSpecialEffectsController.kt
eu...@gmail.com <eu...@gmail.com> #8
So I have isolated this outside of fragment into something much simpler and I think it breaks down when it comes to the adding and removing of Views with animateToStart.
The attached sample is a simple add that goes between two screens BLUE
and GREEN
. It has code for both the 1.5.0-alpha03
and 1.5.0-alpha04
versions, but I think alpha04 is currently broken in another way so I will upload the alpha03 version here.
This is integrated with predictive back similar to how fragment is, so upon cancelling we call animateToStart
, then we do a beginDelayedTransition
on a 0
duration Fade()
and we reverse the view visibility back to what it was prior to starting the transition.
If you only do visibility, cancel always works the view never goes away, it is wonderful, but when you do adding and removing views like we need to in fragment it fails.
First the code for beginDelayedTransition goes from this:
TransitionManager.beginDelayedTransition(container, Fade().apply {
duration = 0
})
reverseViews()
to this:
TransitionManager.beginDelayedTransition(container, Fade().apply {
duration = 0
addListener(onEnd = {
reverseViews()
blueScreen.visibility = View.VISIBLE
greenScreen.visibility = View.VISIBLE
})
})
reverseViews(useVisibility = true)
We need to make this change because after the animateToStart()
view is still parented by the overlay, so we call reverseViews(useVisibility = true)
to only change the visibility and then once the transition finishes we can call reverseViews()
to parent the view properly, then we make both views visible again.
From our perspective after the 0
duration transition our views are back in the proper state, but they do not transition properly after a cancel.
If the app is doing this wrong and we can make the appropriate fixes, doing the same in fragment should resolve this. There is logging available that shows the state of the views when we start the transition.
eu...@gmail.com <eu...@gmail.com> #9
The API has changed since that project was created in a way that makes the API more robust. I'm hoping that has fixed this...
[Deleted User] <[Deleted User]> #10
There appears to be a problem with the order of operations. I'm going to look into fixing that.
eu...@gmail.com <eu...@gmail.com> #11
Branch: androidx-main
commit e57dd5f9ac6cbb8cf83b221e2d5b3fbd3e88ce6b
Author: George Mount <mount@google.com>
Date: Thu Nov 09 14:33:53 2023
Fix animateToStart with Slide.
Fixes: 300157785
Slide was not repositioning the View to its proper
translation after animating it to the start position.
This fixes that so that it is moved.
Test: new test
Change-Id: I698f4dbcef46304f9aa545847d205f7b70c80d63
M transition/transition/src/androidTest/java/androidx/transition/SlideEdgeTest.java
M transition/transition/src/androidTest/java/androidx/transition/TranslationAnimationCreatorTest.java
M transition/transition/src/main/java/androidx/transition/TranslationAnimationCreator.java
[Deleted User] <[Deleted User]> #12
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.transition:transition:1.5.0-alpha05
[Deleted User] <[Deleted User]> #13
jb...@google.com <jb...@google.com> #14
You can only get here if your fragment has a view and the from
method where this error happens doesn't even take null
views (from
, not when the view calls getAlpha()
.
It is clear that the fragment definitely starts off with a view, but that view is removed prematurely by something. The only place fragments every clear out the view is when it is destroyed. Is is possible that your app is clearing the fragment view somewhere? It would be nice if we had some fragment manager logging to see what is actually going on, but without more information there isn't much we can figure out.
ma...@gmail.com <ma...@gmail.com> #15
I was able to generate logs for activity leading up to the crash. See attached. (I am now able to reproduce the crash reliably within our application. I have not had time to prepare a standalone project that reproduces the crash.)
Meanwhile, I'll take a closer look at our Fragment-management code to see if we're clearing the fragment view somewhere. Thanks for the guidance on what might be happening here.
jb...@google.com <jb...@google.com> #16
Based off of the logs we were able to identify the issue and get a fix.
Somewhere in the app you are popping the add of your GamecastFragment
causing a remove and then attempting to do a hide()
on the same fragment you just removed:
fm.beginTransaction()
.hide(fragment1)
.add(R.id.content, fragment2)
.addToBackStack(null)
.commit()
... sometime later
fm.popBackStack()
fm.beginTransaction()
.hide(fragment2)
.show(fragment1)
.addToBackStack(null)
.commit()
This is an issue because the popBackStack()
kicks of the exit transition and then immediately after that the hide is enqueued. Now you have a currently removing fragment that the FragmentManager
is preparing to hide. Since the hide is the last thing it sees, it doesn't know that the fragment is actually being removed and to ignore the hide until the remove is complete, so the states get mixed up and the view is destroyed. Had the app been using setReorderAllowed(true)
on all of the transactions, the pop and last transactions would have been combined together and the FragmentManager
would have calculated the final state and been able to avoid this. That being said it is unlikely that this behavior is actually intended cause hiding the view of a fragment that has been removed doesn't do anything.
[Deleted User] <[Deleted User]> #17
I confirmed your findings. We have a hide that occurs earlier in code, which I think is there as a blanket measure to make sure that the fragment "behind" the fragment we're about to add doesn't remain visible:
final Fragment primaryFragment = fragmentManager.getPrimaryNavigationFragment();
if (primaryFragment != null) {
fragmentTransaction
.hide(primaryFragment);
}
In the scenario where we end up using popBackStack(primaryFragment.getTag(), FragmentManager.POP_BACK_STACK_INCLUSIVE)
as part of our code to show the existing fragment, this hide is unnecessary. If I simply remove it, the crash goes away. If I call setReorderingAllowed(true)
on the transaction, that also eliminates the crash and retains the desired effect.
I'm curious how you plan to address this, since this shouldn't lead to a NullPointerException
. The hardest part about this crash is that our own code appears nowhere in the stack trace, making it very challenging to track down its cause when we see crash reports "in the wild". If this is an invalid combination & order of actions for a transaction, we would ideally find out via an IllegalStateException
or something similar at the time we attempt to commit the transaction. But I can imagine that kind of validation at the time of commit could be costly. Perhaps it makes sense to discard the hide
in this scenario?
Thanks for helping to understand what's going on here! This gives me a much better solution than turning off the new fragment management entirely.
ap...@google.com <ap...@google.com> #18
Branch: androidx-main
commit 907810e8c9f6b2610ce479713f2d5afec0511103
Author: Jeremy Woods <jbwoods@google.com>
Date: Thu Jun 03 18:12:03 2021
Fix conflicts between remove by pop and hide
If you do an add, then call popBackStack() and commit a transaction to
hide the same fragment is being removed by the pop while using
transitions and not using `reorderingAllowed(true)`, the fragment
manager will end up destroying the view of the fragment too early. The
reason for this is that the pop will start the transition of the remove,
but before the remove is complete, the hide operation is enqueued and
causes the computed state of the fragment to be incorrect and that
results in the view being destroyed.
While hiding a removed fragment is not something you actually probably
want to do, we should make sure this at least doesn't crash.
Instead of relying solely on the new pending operations to determine the
state of the fragment, if the pending operation does not affect the
Lifecycle (i.e. it is a show or hide), we should also consider the
lifecycle impact of the running operation as well.
RelNote: "The `FragmentManager` will no longer crash when you attempt to
hide a removing fragment."
Test: testPopRemoveWithHide()
Bug: 183634730
Change-Id: I573ddcfcd1b9c782b5546dd4643e0e4ed042b277
M fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentTransitionTest.kt
M fragment/fragment/src/main/java/androidx/fragment/app/SpecialEffectsController.java
jb...@google.com <jb...@google.com> #19
This has been fixed internally and will be part of the Fragment 1.4.0-alpha03
release.
pa...@irisconnect.co.uk <pa...@irisconnect.co.uk> #20
da...@yit.co.il <da...@yit.co.il> #21
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'float android.view.View.getAlpha()' on a null object reference
at androidx.fragment.app.SpecialEffectsController$Operation$State.from(SpecialEffectsController.java:430)
at androidx.fragment.app.DefaultSpecialEffectsController.executeOperations(DefaultSpecialEffectsController.java:62)
at androidx.fragment.app.SpecialEffectsController.executePendingOperations(SpecialEffectsController.java:306)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1853)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1764)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1701)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:488)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8663)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
[Deleted User] <[Deleted User]> #22
updated version 1.5.0 not fixed this.
java.lang.NullPointerException: Attempt to invoke virtual method 'float android.view.View.getAlpha()' on a null object reference at androidx.fragment.app.SpecialEffectsController$Operation$State.from(SpecialEffectsController.java:430) at androidx.fragment.app.DefaultSpecialEffectsController.executeOperations(DefaultSpecialEffectsController.java:62) at androidx.fragment.app.SpecialEffectsController.executePendingOperations(SpecialEffectsController.java:306) at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1903) at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1808) at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1751) at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:538) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7870) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
mr...@gmail.com <mr...@gmail.com> #23
il...@google.com <il...@google.com> #24
Re
hy...@gmail.com <hy...@gmail.com> #25
java.lang.NullPointerException: Attempt to invoke virtual method 'float android.view.View.getAlpha()' on a null object reference
at androidx.fragment.app.SpecialEffectsController$Operation$State.from(SpecialEffectsController.java:430)
at androidx.fragment.app.DefaultSpecialEffectsController.executeOperations(DefaultSpecialEffectsController.java:62)
at androidx.fragment.app.SpecialEffectsController.executePendingOperations(SpecialEffectsController.java:306)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1912)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1817)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1760)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:547)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:246)
at android.app.ActivityThread.main(ActivityThread.java:8653)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
il...@google.com <il...@google.com> #26
Re
tu...@gmail.com <tu...@gmail.com> #27
Fatal Exception: java.lang.NullPointerException
Attempt to invoke virtual method 'float android.view.View.getZ()' on a null object reference
android.view.ViewGroup.buildOrderedChildList (ViewGroup.java:4658)
android.view.ViewGroup.dispatchDraw (ViewGroup.java:4483)
android.view.View.draw (View.java:24409)
androidx.recyclerview.widget.RecyclerView.draw (RecyclerView.java:4603)
android.view.View.buildDrawingCacheImpl (View.java:23664)
android.view.View.buildDrawingCache (View.java:23524)
android.view.View.updateDisplayListIfDirty (View.java:23235)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ViewGroup.recreateChildDisplayList (ViewGroup.java:4732)
android.view.ViewGroup.dispatchGetDisplayList (ViewGroup.java:4704)
android.view.View.updateDisplayListIfDirty (View.java:23214)
android.view.ThreadedRenderer.updateViewTreeDisplayList (ThreadedRenderer.java:777)
android.view.ThreadedRenderer.updateRootDisplayList (ThreadedRenderer.java:783)
android.view.ThreadedRenderer.draw (ThreadedRenderer.java:881)
android.view.ViewRootImpl.draw (ViewRootImpl.java:5647)
android.view.ViewRootImpl.performDraw (ViewRootImpl.java:5330)
android.view.ViewRootImpl.performTraversals (ViewRootImpl.java:4486)
android.view.ViewRootImpl.doTraversal (ViewRootImpl.java:3116)
android.view.ViewRootImpl$TraversalRunnable.run (ViewRootImpl.java:10885)
android.view.Choreographer$CallbackRecord.run (Choreographer.java:1301)
android.view.Choreographer$CallbackRecord.run (Choreographer.java:1309)
android.view.Choreographer.doCallbacks (Choreographer.java:923)
android.view.Choreographer.doFrame (Choreographer.java:852)
android.view.Choreographer$FrameDisplayEventReceiver.run (Choreographer.java:1283)
android.os.Handler.handleCallback (Handler.java:942)
android.os.Handler.dispatchMessage (Handler.java:99)
android.os.Looper.loopOnce (Looper.java:226)
android.os.Looper.loop (Looper.java:313)
android.app.ActivityThread.main (ActivityThread.java:8757)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067)
[Deleted User] <[Deleted User]> #28
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'float android.view.View.getAlpha()' on a null object reference
at androidx.fragment.app.SpecialEffectsController$Operation$State.from(SpecialEffectsController.java:412)
at androidx.fragment.app.DefaultSpecialEffectsController.executeOperations(DefaultSpecialEffectsController.java:62)
at androidx.fragment.app.SpecialEffectsController.executePendingOperations(SpecialEffectsController.java:297)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2202)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8757)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Description
Component used: Fragment Version used:1.3.1 Devices/Android versions reproduced on:Xperia XZ1
If this is a bug in the library, we would appreciate if you could attach: