Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[a11y] Changing logic of how reduce motion options are set to match it with lottie iOS #2536

Merged
merged 4 commits into from
Aug 21, 2024

Conversation

pranayairan
Copy link
Contributor

This PR introduces a new way to determine if reduce motion is enabled or not. It matches iOS Lottie API airbnb/lottie-ios#2110.

This change provides the ability for apps integrating Lottie to have custom reduce motion behavior which doesn't need to rely solely on system settings.

This change also allows reduce motion to work regardless of usage of LottieAnimationView, LottieAnimation in compose or straight LottieDrawable.

Testing

  • Verified that the default ReducedMotionOptionProvider is correctly used when none is provided.
  • Ensured that custom implementations of ReducedMotionOption can be set and used.
  • Updated existing test for LottieDrawable reduce motion.

Pranay Airan added 2 commits August 13, 2024 10:56
…iOS API

Changing logic of how reduce motion options are set to match it with iOS API, this allows apps to provide custom implementation for reduce motion which can rely on either system settings or some other logic.
Copy link
Member

@calda calda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Copy link
Collaborator

@gpeal gpeal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this!

I'm not totally sure all of the different ways this is used today. It's possible that somebody is intentionally using this on a per-view basis and this PR removes the ability to control it at that level.

Paired with my other comment about backwards compatibility, it would be good to potentially solve both with a setting at the view level that falls back to the global config when not set.

*
* @param ignore if true animations will run even when they are disabled in the system settings.
*/
public void setIgnoreDisabledSystemAnimations(boolean ignore) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing an existing public API is tricky. At the very least, we'll need to deprecate this and provide a graceful fallback. Ideally, its behavior would remain the same (scope IgnoreDisabledSystemAnimations() to this view if possible).

It can be fully removed after a few releases.

Copy link
Contributor Author

@pranayairan pranayairan Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gpeal make sense, added the original API back. I also marked it as deprecated so folks can switch to new API

putting the orignal API back to maintain backward compatibility.
Copy link

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@pranayairan
Copy link
Contributor Author

@gpeal wondering if this looks good can we merge it ?

Comment on lines 1248 to 1249
return (systemAnimationsEnabled || ignoreSystemAnimationsDisabled) &&
L.getReducedMotionOption().getCurrentReducedMotionMode(getContext()) == ReducedMotionMode.STANDARD_MOTION;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this correctly, if you set ignoreSystemAnimationsDisabled but the system scale is 0 then:

  • You would expect animationsEnabled to return true
  • systemAnimationsEnable: false
  • ignoreSystemAnimationsDisabled: true
  • LottieConfig: REDUCED

The predicate becomes (false || true) && (REDUCED == STANDARD) which would be false.

I think this may also read easier if you treat ignoreSystemAnimationsDisabled == true as higher priority with:

if (ignoreSystemAnimationsDisabled) {
    return true;
}
return L.getReducedMotionOption().getCurrentReducedMotionMode(getContext()) == ReducedMotionMode.STANDARD_MOTION;

Just off the top of my head, I think this is correct and easier to read. Is that right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right. Ideally getCurrentReducedMotionMode should not even be evaluated when ignoreSystemAnimationsDisabled is set to true. Let me fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it to this

private boolean animationsEnabled() {
    if (ignoreSystemAnimationsDisabled) {
      return true;
    }
    return systemAnimationsEnabled &&
        L.getReducedMotionOption().getCurrentReducedMotionMode(getContext()) == ReducedMotionMode.STANDARD_MOTION;
  }

We still need to consider old systemAnimationsEnabled flag that can be set on drawable directly for backward compatibility

updating logic to consider `ignoreSystemAnimationsDisabled`  first before running other evaluation.
Copy link
Collaborator

@gpeal gpeal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working through this!

Copy link

Snapshot Tests
API 23: Report Diff
API 31: Report Diff

@gpeal gpeal merged commit 733999b into airbnb:master Aug 21, 2024
7 checks passed
@pranayairan
Copy link
Contributor Author

@gpeal thanks for merging. Can you share what is the release cadence we follow ? this will help me plan downstream changes.

@pranayairan pranayairan deleted the pranay-reducemotion-option branch August 21, 2024 22:10
@gpeal
Copy link
Collaborator

gpeal commented Aug 28, 2024

@gpeal thanks for merging. Can you share what is the release cadence we follow ? this will help me plan downstream changes.

I just release 6.5.1 with your change!

@pranayairan
Copy link
Contributor Author

@gpeal thanks. I am testing the integration and I think i miss something. The logic is not working when i am using LottieAnimation in compose. I am not entirely sure how this was working before since LottieAnimation doesn't call playAnimation on LottieDrawable, so none of the logic we have added previously and modified in this PR executes.

From what i can understand looking into the code, when using LottieAnimation we directly draw on canvas.

@gpeal
Copy link
Collaborator

gpeal commented Aug 29, 2024

@gpeal thanks. I am testing the integration and I think i miss something. The logic is not working when i am using LottieAnimation in compose. I am not entirely sure how this was working before since LottieAnimation doesn't call playAnimation on LottieDrawable, so none of the logic we have added previously and modified in this PR executes.

From what i can understand looking into the code, when using LottieAnimation we directly draw on canvas.

That's correct. I should have remembered to check for that in your PR. lottie-compose uses LottieDrawable but uses its own animator.

@pranayairan
Copy link
Contributor Author

make sense, let me try creating a new PR for compose then.

@pranayairan
Copy link
Contributor Author

@gpeal created another PR just for compose. #2542

gpeal pushed a commit that referenced this pull request Aug 30, 2024
#2542)

Our changes in #2536 didn't work for `LottieAnimation` in compose. While LottieAnimation does use LottieDrawable, it has it's own animator and draws on canvas directly. 

This PR addresses that issue by making some logic of reduce motion readable outside LottieDrawable and consuming that logic in `LottieAnimation` 

I tested this by adding a sample lottie file with reduce motion marker locally, see the attached video. 

https://github.com/user-attachments/assets/eb33333f-86b8-46fa-9bbb-82bff8a8c7fe

Co-authored-by: Pranay Airan <[email protected]>
@tevjef
Copy link

tevjef commented Sep 3, 2024

Heads up our UI tests caught this crash this morning in 6.5.1. I will create a issue once I've collected more info.

 java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.ContentResolver android.content.Context.getContentResolver()' on a null object reference
 	at com.airbnb.lottie.utils.Utils.getAnimationScale(Utils.java:267)
 	at com.airbnb.lottie.configurations.reducemotion.SystemReducedMotionOption.getCurrentReducedMotionMode(SystemReducedMotionOption.java:22)
 	at com.airbnb.lottie.LottieDrawable.animationsEnabled(LottieDrawable.java:1252)
 	at com.airbnb.lottie.LottieDrawable.playAnimation(LottieDrawable.java:826)

gpeal pushed a commit that referenced this pull request Sep 4, 2024
#2546)

When calling reduce motion check we need to pass context, we read context in LottieDrawable using `getContext` method. getContext method can return a nullable context, since the code is in java we don't get any compile time error when passing the null context around. 

This resulted in issue where we end up calling `getContentResolver` on a null object in case where context is null. #2536

This PR fixes it by adding a null check before calling `Utils.getAnimationScale(context)`

Co-authored-by: Pranay Airan <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants